From c30a6232df03e1efbd9f3b226777b07e087a1122 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 12 Oct 2020 14:27:29 +0200 Subject: BASELINE: Update Chromium to 85.0.4183.140 Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen --- chromium/components/BUILD.gn | 19 +- chromium/components/OWNERS | 4 + .../components/about_ui/resources/about_credits.js | 3 +- chromium/components/account_id/account_id.cc | 1 + chromium/components/arc/BUILD.gn | 5 +- chromium/components/arc/DEPS | 1 + chromium/components/arc/arc_features.cc | 7 +- chromium/components/arc/arc_features.h | 1 + chromium/components/arc/arc_features_parser.cc | 25 +- chromium/components/arc/arc_util.cc | 26 +- chromium/components/arc/arc_util.h | 7 + chromium/components/arc/arc_util_unittest.cc | 29 + .../components/arc/camera/arc_camera_bridge.cc | 10 +- chromium/components/arc/ime/arc_ime_service.cc | 31 +- chromium/components/arc/ime/arc_ime_service.h | 22 +- .../components/arc/ime/arc_ime_service_unittest.cc | 18 +- chromium/components/arc/intent_helper/DEPS | 2 + .../arc/intent_helper/activity_icon_loader.cc | 3 +- .../arc/intent_helper/arc_intent_helper_bridge.cc | 1 - .../arc_intent_helper_bridge_unittest.cc | 6 +- .../components/arc/intent_helper/custom_tab.cc | 160 +++ chromium/components/arc/intent_helper/custom_tab.h | 76 ++ .../components/arc/intent_helper/intent_filter.cc | 19 +- .../components/arc/intent_helper/intent_filter.h | 8 +- .../intent_helper/intent_filter_mojom_traits.cc | 13 +- .../arc/intent_helper/intent_filter_mojom_traits.h | 10 +- .../arc/intent_helper/intent_filter_unittest.cc | 7 +- chromium/components/arc/midis/arc_midis_bridge.cc | 17 +- chromium/components/arc/midis/arc_midis_bridge.h | 13 +- chromium/components/arc/mojom/BUILD.gn | 20 +- .../arc/mojom/accessibility_helper.mojom | 33 +- chromium/components/arc/mojom/app.mojom | 17 +- chromium/components/arc/mojom/appfuse.mojom | 2 +- chromium/components/arc/mojom/arc_bridge.mojom | 134 ++- chromium/components/arc/mojom/audio.mojom | 4 +- chromium/components/arc/mojom/auth.mojom | 4 +- chromium/components/arc/mojom/bluetooth.mojom | 4 +- .../components/arc/mojom/boot_phase_monitor.mojom | 4 +- chromium/components/arc/mojom/camera.mojom | 4 +- chromium/components/arc/mojom/cert_store.mojom | 6 +- chromium/components/arc/mojom/clipboard.mojom | 4 +- .../components/arc/mojom/crash_collector.mojom | 4 +- chromium/components/arc/mojom/disk_quota.mojom | 3 +- .../arc/mojom/enterprise_reporting.mojom | 5 +- chromium/components/arc/mojom/file_system.mojom | 4 +- chromium/components/arc/mojom/ime.mojom | 17 +- chromium/components/arc/mojom/ime.typemap | 10 +- chromium/components/arc/mojom/ime_mojom_traits.cc | 42 + chromium/components/arc/mojom/ime_mojom_traits.h | 29 +- .../arc/mojom/ime_mojom_traits_unittest.cc | 45 + .../arc/mojom/input_method_manager.mojom | 16 +- chromium/components/arc/mojom/intent_helper.mojom | 15 +- chromium/components/arc/mojom/keymaster.mojom | 5 +- chromium/components/arc/mojom/kiosk.mojom | 4 +- chromium/components/arc/mojom/metrics.mojom | 4 +- chromium/components/arc/mojom/midis.mojom | 7 +- chromium/components/arc/mojom/net.mojom | 26 +- chromium/components/arc/mojom/notifications.mojom | 4 +- chromium/components/arc/mojom/obb_mounter.mojom | 4 +- chromium/components/arc/mojom/oemcrypto.mojom | 6 +- .../components/arc/mojom/oemcrypto_daemon.mojom | 26 - chromium/components/arc/mojom/pip.mojom | 2 +- chromium/components/arc/mojom/policy.mojom | 4 +- chromium/components/arc/mojom/power.mojom | 4 +- chromium/components/arc/mojom/print_spooler.mojom | 13 +- chromium/components/arc/mojom/screen_capture.mojom | 8 +- chromium/components/arc/mojom/timer.mojom | 2 +- chromium/components/arc/mojom/tts.mojom | 4 +- chromium/components/arc/mojom/usb_host.mojom | 2 +- chromium/components/arc/mojom/video.mojom | 13 +- .../arc/mojom/video_decode_accelerator.mojom | 2 +- .../arc/mojom/video_encode_accelerator.mojom | 5 +- .../arc/mojom/voice_interaction_arc_home.mojom | 5 +- .../arc/mojom/voice_interaction_framework.mojom | 5 +- chromium/components/arc/mojom/volume_mounter.mojom | 2 +- chromium/components/arc/mojom/wake_lock.mojom | 2 +- chromium/components/arc/mojom/wallpaper.mojom | 4 +- chromium/components/arc/net/OWNERS | 3 + chromium/components/arc/net/arc_net_host_impl.cc | 524 +++------ chromium/components/arc/net/arc_net_host_impl.h | 6 - chromium/components/arc/power/arc_power_bridge.cc | 4 +- .../arc/rotation_lock/arc_rotation_lock_bridge.cc | 7 +- .../components/arc/session/arc_bridge_host_impl.cc | 237 ++-- .../components/arc/session/arc_bridge_host_impl.h | 146 ++- .../components/arc/session/arc_property_util.cc | 47 +- .../components/arc/session/arc_property_util.h | 9 +- .../arc/session/arc_property_util_unittest.cc | 108 +- .../components/arc/session/arc_session_impl.cc | 2 +- .../arc/session/arc_vm_client_adapter.cc | 37 +- .../arc/session/arc_vm_client_adapter_unittest.cc | 47 +- .../components/arc/session/connection_holder.h | 5 +- .../components/arc/session/file_system_status.cc | 4 +- chromium/components/arc/usb/usb_host_bridge.h | 1 - .../gpu_arc_video_decode_accelerator.cc | 4 +- .../gpu_arc_video_decode_accelerator.h | 7 +- .../gpu_arc_video_encode_accelerator.cc | 8 +- .../gpu_arc_video_encode_accelerator.h | 16 +- .../assist_ranker/ranker_example_util.cc | 4 +- chromium/components/autofill/android/BUILD.gn | 47 +- chromium/components/autofill/android/OWNERS | 6 - .../autofill/android/autofill_provider_android.cc | 350 ------ .../autofill/android/autofill_provider_android.h | 117 -- .../autofill/android/form_data_android.cc | 112 -- .../autofill/android/form_data_android.h | 73 -- .../autofill/android/form_field_data_android.cc | 101 -- .../autofill/android/form_field_data_android.h | 42 - .../autofill/AutofillActionModeCallback.java | 60 - .../autofill/AutofillManagerWrapper.java | 206 ---- .../components/autofill/AutofillProvider.java | 157 --- .../components/autofill/AutofillProviderImpl.java | 540 --------- .../components/autofill/AutofillProviderUMA.java | 255 ---- .../org/chromium/components/autofill/FormData.java | 56 - .../components/autofill/FormFieldData.java | 150 --- .../components/autofill/android/junit/BUILD.gn | 21 - chromium/components/autofill/android/junit/OWNERS | 2 - .../autofill/AutofillProviderImplTest.java | 116 -- .../components/autofill/android/provider/BUILD.gn | 53 + .../components/autofill/android/provider/OWNERS | 2 + .../android/provider/autofill_provider_android.cc | 405 +++++++ .../android/provider/autofill_provider_android.h | 127 ++ .../autofill/android/provider/form_data_android.cc | 112 ++ .../autofill/android/provider/form_data_android.h | 73 ++ .../android/provider/form_field_data_android.cc | 106 ++ .../android/provider/form_field_data_android.h | 42 + .../autofill/AutofillActionModeCallback.java | 60 + .../autofill/AutofillManagerWrapper.java | 206 ++++ .../components/autofill/AutofillProvider.java | 786 +++++++++++++ .../components/autofill/AutofillProviderUMA.java | 255 ++++ .../org/chromium/components/autofill/FormData.java | 56 + .../components/autofill/FormFieldData.java | 162 +++ .../autofill/android/provider/junit/BUILD.gn | 23 + .../components/autofill/AutofillProviderTest.java | 115 ++ .../content/browser/content_autofill_driver.cc | 8 +- .../content/browser/content_autofill_driver.h | 8 +- .../browser/content_autofill_driver_factory.cc | 17 +- .../browser/content_autofill_driver_unittest.cc | 4 +- .../autofill/content/browser/risk/fingerprint.cc | 3 +- .../content/common/mojom/autofill_agent.mojom | 2 +- .../content/common/mojom/autofill_driver.mojom | 4 - .../components/autofill/content/renderer/BUILD.gn | 1 + .../autofill/content/renderer/autofill_agent.cc | 131 +-- .../autofill/content/renderer/autofill_agent.h | 3 +- .../autofill/content/renderer/focus_test_utils.h | 4 +- .../content/renderer/form_autofill_util.cc | 90 +- .../autofill/content/renderer/form_autofill_util.h | 32 +- .../renderer/form_autofill_util_browsertest.cc | 160 ++- .../autofill/content/renderer/form_cache.cc | 44 +- .../autofill/content/renderer/form_tracker.cc | 11 +- .../autofill/content/renderer/form_tracker.h | 4 +- .../renderer/html_based_username_detector.cc | 11 +- .../html_based_username_detector_browsertest.cc | 345 ++++++ .../content/renderer/password_autofill_agent.cc | 106 +- .../content/renderer/password_autofill_agent.h | 34 +- .../renderer/password_form_conversion_utils.cc | 16 +- .../renderer/password_form_conversion_utils.h | 7 +- .../content/renderer/password_generation_agent.cc | 3 - .../content/renderer/password_generation_agent.h | 3 +- chromium/components/autofill/core/browser/BUILD.gn | 5 + .../autofill_and_password_manager_internals.css | 4 +- .../core/browser/autofill_assistant_unittest.cc | 36 +- .../autofill/core/browser/autofill_client.cc | 23 + .../autofill/core/browser/autofill_client.h | 45 +- .../core/browser/autofill_download_manager.cc | 73 +- .../core/browser/autofill_download_manager.h | 16 +- .../browser/autofill_download_manager_unittest.cc | 10 +- .../autofill/core/browser/autofill_experiments.cc | 30 - .../autofill/core/browser/autofill_experiments.h | 5 - .../core/browser/autofill_external_delegate.cc | 8 +- .../browser/autofill_external_delegate_unittest.cc | 271 +++-- .../autofill/core/browser/autofill_handler.cc | 121 +- .../autofill/core/browser/autofill_handler.h | 52 +- .../core/browser/autofill_handler_proxy.cc | 8 +- .../autofill/core/browser/autofill_handler_proxy.h | 2 - .../autofill/core/browser/autofill_manager.cc | 176 ++- .../autofill/core/browser/autofill_manager.h | 37 +- .../core/browser/autofill_manager_unittest.cc | 97 +- .../autofill/core/browser/autofill_metrics.cc | 253 +++- .../autofill/core/browser/autofill_metrics.h | 90 +- .../core/browser/autofill_metrics_unittest.cc | 815 +++++++++++-- .../autofill/core/browser/autofill_provider.cc | 8 +- .../autofill/core/browser/autofill_provider.h | 7 + .../autofill/core/browser/autofill_test_utils.cc | 60 + .../autofill/core/browser/autofill_test_utils.h | 19 + .../autofill/core/browser/autofill_type.cc | 18 + .../core/browser/data_model/credit_card.cc | 39 +- .../autofill/core/browser/data_model/credit_card.h | 13 +- .../browser/data_model/credit_card_unittest.cc | 45 +- .../core/browser/data_model/data_model_utils.cc | 14 + .../core/browser/data_model/data_model_utils.h | 4 + .../core/browser/data_model/phone_number.cc | 14 + .../browser/data_model/phone_number_unittest.cc | 76 ++ .../core/browser/data_model/test_data_creator.cc | 1 + .../autofill/core/browser/field_filler.cc | 41 +- .../autofill/core/browser/field_filler_unittest.cc | 128 ++ .../autofill/core/browser/field_types.cc | 4 + .../components/autofill/core/browser/field_types.h | 11 +- .../autofill/core/browser/form_data_importer.cc | 51 +- .../autofill/core/browser/form_data_importer.h | 3 +- .../core/browser/form_data_importer_unittest.cc | 248 ++-- .../core/browser/form_parsing/credit_card_field.cc | 66 +- .../core/browser/form_parsing/credit_card_field.h | 8 +- .../form_parsing/credit_card_field_unittest.cc | 68 ++ .../core/browser/form_parsing/phone_field.cc | 94 +- .../core/browser/form_parsing/phone_field.h | 7 + .../browser/form_parsing/phone_field_unittest.cc | 331 ++++++ .../autofill/core/browser/form_structure.cc | 305 +++-- .../autofill/core/browser/form_structure.h | 90 +- ...form_structure_process_query_response_fuzzer.cc | 7 +- .../core/browser/form_structure_unittest.cc | 494 +++++--- .../autofill/core/browser/geo/country_names.cc | 3 +- .../core/browser/geo/country_names_unittest.cc | 3 + .../core/browser/metrics/form_event_logger_base.cc | 7 + .../core/browser/metrics/form_event_logger_base.h | 2 +- .../browser/payments/autofill_offer_manager.cc | 13 + .../core/browser/payments/autofill_offer_manager.h | 42 + .../autofill_save_card_infobar_delegate_mobile.cc | 11 +- .../autofill_save_card_infobar_delegate_mobile.h | 13 +- .../browser/payments/credit_card_access_manager.cc | 7 +- .../credit_card_access_manager_unittest.cc | 2 +- .../payments/credit_card_fido_authenticator.cc | 22 +- .../browser/payments/credit_card_save_manager.cc | 69 +- .../payments/credit_card_save_manager_unittest.cc | 52 - .../payments/legal_message_line_unittest.cc | 1 + .../payments/local_card_migration_manager.cc | 3 - .../core/browser/payments/payments_client.cc | 13 + .../browser/payments/payments_client_unittest.cc | 79 +- .../payments/test_internal_authenticator.cc | 15 + .../browser/payments/test_internal_authenticator.h | 2 +- .../autofill/core/browser/personal_data_manager.cc | 47 +- .../autofill/core/browser/personal_data_manager.h | 6 + .../core/browser/personal_data_manager_unittest.cc | 455 +++---- .../autofill/core/browser/proto/server.proto | 46 +- .../autofill/core/browser/randomized_encoder.cc | 1 + .../autofill/core/browser/randomized_encoder.h | 1 + .../autofill/core/browser/test_autofill_client.cc | 10 +- .../autofill/core/browser/test_autofill_client.h | 7 +- .../core/browser/test_autofill_download_manager.cc | 3 +- .../autofill/core/browser/test_autofill_manager.cc | 5 +- .../core/browser/test_autofill_provider.cc | 2 + .../autofill/core/browser/test_autofill_provider.h | 1 + .../core/browser/ui/accessory_sheet_data.cc | 1 + .../core/browser/ui/accessory_sheet_enums.h | 10 + .../core/browser/ui/address_combobox_model.cc | 4 +- .../core/browser/ui/address_combobox_model.h | 4 +- .../core/browser/ui/country_combobox_model.cc | 4 +- .../core/browser/ui/country_combobox_model.h | 4 +- ...ard_expiration_date_fix_flow_controller_impl.cc | 2 +- ...ation_date_fix_flow_controller_impl_unittest.cc | 43 +- .../ui/payments/card_unmask_prompt_controller.h | 1 - .../payments/card_unmask_prompt_controller_impl.cc | 57 +- .../payments/card_unmask_prompt_controller_impl.h | 5 +- .../card_unmask_prompt_controller_impl_unittest.cc | 10 +- .../autofill/core/browser/ui/popup_types.h | 3 + .../core/browser/ui/region_combobox_model.cc | 8 +- .../core/browser/ui/region_combobox_model.h | 4 +- .../core/browser/webdata/autofill_change.h | 2 +- .../core/browser/webdata/autofill_table.cc | 24 +- .../autofill/core/browser/webdata/autofill_table.h | 3 + .../browser/webdata/autofill_table_unittest.cc | 9 +- .../autofill_wallet_metadata_sync_bridge.cc | 5 +- .../core/common/autofill_data_validation.cc | 2 +- .../autofill/core/common/autofill_features.cc | 42 +- .../autofill/core/common/autofill_features.h | 7 + .../core/common/autofill_internals/log_message.cc | 1 + .../common/autofill_internals/logging_scope.cc | 1 + .../core/common/autofill_payments_features.cc | 29 +- .../core/common/autofill_payments_features.h | 4 +- .../core/common/autofill_regex_constants.cc | 3 + .../core/common/autofill_regex_constants.h | 2 + .../autofill/core/common/autofill_regexes.cc | 6 +- .../autofill/core/common/autofill_regexes.h | 5 +- .../components/autofill/core/common/form_data.h | 4 + .../autofill/core/common/form_field_data.cc | 1 + .../autofill/core/common/form_field_data.h | 8 + .../core/common/mojom/autofill_types.mojom | 7 +- .../common/mojom/autofill_types_mojom_traits.cc | 10 +- .../common/mojom/autofill_types_mojom_traits.h | 18 +- .../mojom/autofill_types_mojom_traits_unittest.cc | 36 +- .../autofill/core/common/password_form.cc | 7 +- .../autofill/core/common/password_form.h | 18 +- .../autofill/core/common/password_form_fill_data.h | 7 +- .../core/common/password_form_generation_data.cc | 2 - .../core/common/password_form_generation_data.h | 4 - .../components/autofill/core/common/renderer_id.h | 2 + .../autofill/ios/browser/autofill_agent.mm | 3 +- .../autofill/ios/browser/autofill_util.h | 1 - .../autofill/ios/browser/autofill_util.mm | 32 +- .../autofill/ios/browser/js_autofill_manager.h | 7 - .../autofill/ios/browser/js_autofill_manager.mm | 33 +- .../autofill/ios/browser/js_suggestion_manager.mm | 12 +- .../ios/form_util/form_activity_tab_helper.mm | 1 + .../form_util/form_activity_tab_helper_unittest.mm | 39 +- .../autofill/ios/form_util/form_unittest.mm | 54 +- .../autofill/ios/form_util/resources/fill.js | 58 +- .../autofill/ios/form_util/resources/form.js | 2 +- .../ios/form_util/resources/form_handlers.js | 57 +- .../components/autofill_assistant/browser/BUILD.gn | 10 +- .../browser/actions/action_delegate.h | 28 +- .../browser/actions/collect_user_data_action.cc | 123 +- .../actions/collect_user_data_action_unittest.cc | 55 + .../actions/fallback_handler/fallback_data.cc | 73 -- .../actions/fallback_handler/fallback_data.h | 40 - .../actions/fallback_handler/required_field.cc | 41 +- .../actions/fallback_handler/required_field.h | 21 +- .../required_fields_fallback_handler.cc | 135 ++- .../required_fields_fallback_handler.h | 33 +- .../required_fields_fallback_handler_unittest.cc | 479 ++++---- ...rate_password_for_form_field_action_unittest.cc | 5 +- .../browser/actions/mock_action_delegate.h | 49 +- .../browser/actions/navigate_action.cc | 7 +- .../browser/actions/prompt_action.cc | 4 +- .../browser/actions/prompt_action_unittest.cc | 30 +- .../browser/actions/set_attribute_action.cc | 1 - .../browser/actions/set_form_field_value_action.cc | 1 - .../set_form_field_value_action_unittest.cc | 5 +- .../browser/actions/show_generic_ui_action.cc | 147 ++- .../browser/actions/show_generic_ui_action.h | 26 +- .../actions/show_generic_ui_action_unittest.cc | 117 +- .../browser/actions/show_progress_bar_action.cc | 45 +- .../browser/actions/show_progress_bar_action.h | 5 +- .../actions/show_progress_bar_action_unittest.cc | 166 +++ .../browser/actions/use_address_action.cc | 164 ++- .../browser/actions/use_address_action.h | 23 +- .../browser/actions/use_address_action_unittest.cc | 229 +++- .../browser/actions/use_credit_card_action.cc | 145 ++- .../browser/actions/use_credit_card_action.h | 17 +- .../actions/use_credit_card_action_unittest.cc | 348 +++--- .../actions/wait_for_document_action_unittest.cc | 4 +- .../actions/wait_for_dom_action_unittest.cc | 22 +- .../browser/basic_interactions.cc | 127 +- .../browser/basic_interactions.h | 32 +- .../browser/basic_interactions_unittest.cc | 129 +- .../components/autofill_assistant/browser/client.h | 3 + .../autofill_assistant/browser/client_settings.h | 2 +- .../autofill_assistant/browser/client_status.cc | 3 + .../autofill_assistant/browser/controller.cc | 151 ++- .../autofill_assistant/browser/controller.h | 29 +- .../browser/controller_observer.h | 10 + .../browser/controller_unittest.cc | 123 +- .../domain_type_conversions_h.template | 2 +- .../browser/devtools/devtools_client.cc | 4 +- .../autofill_assistant/browser/element_area.cc | 3 + .../browser/element_area_unittest.cc | 42 +- .../browser/element_precondition_unittest.cc | 121 +- .../autofill_assistant/browser/event_handler.cc | 66 ++ .../autofill_assistant/browser/event_handler.h | 4 + .../browser/fake_script_executor_delegate.cc | 17 +- .../browser/fake_script_executor_delegate.h | 13 +- .../autofill_assistant/browser/features.cc | 8 + .../autofill_assistant/browser/features.h | 2 + .../autofill_assistant/browser/field_formatter.cc | 128 ++ .../autofill_assistant/browser/field_formatter.h | 37 + .../browser/field_formatter_unittest.cc | 209 ++++ .../autofill_assistant/browser/generic_ui.proto | 81 +- .../browser/generic_ui_java_generated_enums.h | 20 + .../autofill_assistant/browser/metrics.h | 20 +- .../autofill_assistant/browser/mock_client.h | 1 + .../browser/mock_controller_observer.h | 5 + .../autofill_assistant/browser/model.proto | 12 +- .../browser/radio_button_controller.cc | 44 + .../browser/radio_button_controller.h | 49 + .../browser/radio_button_controller_unittest.cc | 77 ++ .../autofill_assistant/browser/script_executor.cc | 52 +- .../autofill_assistant/browser/script_executor.h | 16 +- .../browser/script_executor_delegate.h | 15 +- .../browser/script_executor_unittest.cc | 109 +- .../browser/script_precondition.cc | 1 + .../browser/script_precondition_unittest.cc | 7 +- .../browser/script_tracker_unittest.cc | 13 +- .../autofill_assistant/browser/selector.cc | 409 +++++-- .../autofill_assistant/browser/selector.h | 105 +- .../browser/selector_unittest.cc | 181 ++- .../autofill_assistant/browser/service.proto | 392 ++++-- .../autofill_assistant/browser/service_impl.cc | 11 + .../autofill_assistant/browser/service_impl.h | 8 +- .../browser/service_impl_unittest.cc | 21 + .../autofill_assistant/browser/trigger_context.cc | 20 + .../autofill_assistant/browser/trigger_context.h | 16 +- .../browser/trigger_context_unittest.cc | 50 +- .../autofill_assistant/browser/ui_delegate.h | 13 + .../autofill_assistant/browser/user_data_util.cc | 101 ++ .../autofill_assistant/browser/user_data_util.h | 12 + .../browser/user_data_util_unittest.cc | 294 ++++- .../autofill_assistant/browser/user_model.cc | 36 +- .../autofill_assistant/browser/user_model.h | 18 +- .../browser/user_model_unittest.cc | 71 ++ .../autofill_assistant/browser/value_util.cc | 31 +- .../autofill_assistant/browser/value_util.h | 6 - .../browser/value_util_unittest.cc | 37 +- .../autofill_assistant/browser/view_layout.proto | 21 + .../browser/web/element_finder.cc | 929 ++++++++++++--- .../browser/web/element_finder.h | 286 ++++- .../browser/web/element_position_getter.cc | 9 +- .../browser/web/element_rect_getter.cc | 1 + .../browser/web/web_controller.cc | 38 +- .../browser/web/web_controller_browsertest.cc | 759 ++++++++---- .../browser/website_login_manager_impl.cc | 17 +- chromium/components/autofill_payments_strings.grdp | 2 +- .../components/background_task_scheduler/BUILD.gn | 3 + .../BackgroundTaskSchedulerExternalUma.java | 5 +- .../background_task_scheduler/TaskInfo.java | 23 +- .../BackgroundTaskSchedulerJobService.java | 12 +- .../BackgroundTaskSchedulerImplWithMockTest.java | 2 +- .../BackgroundTaskSchedulerJobServiceTest.java | 3 +- .../BundleToPersistableBundleConverterTest.java | 3 +- .../internal/BackgroundTaskSchedulerUmaTest.java | 4 +- .../background_task_scheduler/task_ids.h | 1 + chromium/components/base32/BUILD.gn | 15 - chromium/components/base32/base32.cc | 56 + chromium/components/base32/base32.h | 4 + chromium/components/base32/base32_fuzzer.cc | 1 - chromium/components/base32/base32_test_util.cc | 76 -- chromium/components/base32/base32_test_util.h | 19 - chromium/components/base32/base32_unittest.cc | 1 - chromium/components/blacklist/OWNERS | 3 - chromium/components/blacklist/README.md | 58 - .../blacklist/opt_out_blacklist/BUILD.gn | 33 - .../opt_out_blacklist/opt_out_blacklist.cc | 221 ---- .../opt_out_blacklist/opt_out_blacklist.h | 183 --- .../opt_out_blacklist/opt_out_blacklist_data.cc | 173 --- .../opt_out_blacklist/opt_out_blacklist_data.h | 176 --- .../opt_out_blacklist/opt_out_blacklist_delegate.h | 38 - .../opt_out_blacklist/opt_out_blacklist_item.cc | 74 -- .../opt_out_blacklist/opt_out_blacklist_item.h | 99 -- .../opt_out_blacklist_item_unittest.cc | 80 -- .../opt_out_blacklist_unittest.cc | 1189 ------------------- .../blacklist/opt_out_blacklist/opt_out_store.h | 49 - .../blacklist/opt_out_blacklist/sql/BUILD.gn | 32 - .../blacklist/opt_out_blacklist/sql/DEPS | 3 - .../opt_out_blacklist/sql/opt_out_store_sql.cc | 408 ------- .../opt_out_blacklist/sql/opt_out_store_sql.h | 68 -- .../sql/opt_out_store_sql_unittest.cc | 310 ----- chromium/components/blocked_content/BUILD.gn | 95 ++ chromium/components/blocked_content/DEPS | 18 + chromium/components/blocked_content/OWNERS | 5 + .../components/blocked_content/android/BUILD.gn | 16 + chromium/components/blocked_content/android/DEPS | 6 + .../android/popup_blocked_infobar_delegate.cc | 141 +++ .../android/popup_blocked_infobar_delegate.h | 61 + .../popup_blocked_infobar_delegate_unittest.cc | 133 +++ .../res/drawable-hdpi/infobar_blocked_popups.png | Bin 0 -> 285 bytes .../res/drawable-mdpi/infobar_blocked_popups.png | Bin 0 -> 210 bytes .../res/drawable-xhdpi/infobar_blocked_popups.png | Bin 0 -> 328 bytes .../res/drawable-xxhdpi/infobar_blocked_popups.png | Bin 0 -> 472 bytes .../drawable-xxxhdpi/infobar_blocked_popups.png | Bin 0 -> 563 bytes .../blocked_content/list_item_position.cc | 27 + .../blocked_content/list_item_position.h | 36 + .../components/blocked_content/popup_blocker.cc | 137 +++ .../components/blocked_content/popup_blocker.h | 58 + .../blocked_content/popup_blocker_tab_helper.cc | 171 +++ .../blocked_content/popup_blocker_tab_helper.h | 105 ++ .../popup_blocker_tab_helper_unittest.cc | 189 +++ .../blocked_content/popup_navigation_delegate.h | 55 + .../blocked_content/popup_opener_tab_helper.cc | 129 ++ .../blocked_content/popup_opener_tab_helper.h | 105 ++ .../components/blocked_content/popup_tracker.cc | 174 +++ .../components/blocked_content/popup_tracker.h | 113 ++ chromium/components/blocked_content/pref_names.cc | 16 + chromium/components/blocked_content/pref_names.h | 16 + .../safe_browsing_triggered_popup_blocker.cc | 194 +++ .../safe_browsing_triggered_popup_blocker.h | 148 +++ ...fe_browsing_triggered_popup_blocker_unittest.cc | 501 ++++++++ .../components/blocked_content/url_list_manager.cc | 27 + .../components/blocked_content/url_list_manager.h | 47 + chromium/components/blocked_content_strings.grdp | 11 + chromium/components/blocklist/OWNERS | 3 + chromium/components/blocklist/README.md | 58 + .../blocklist/opt_out_blocklist/BUILD.gn | 34 + .../opt_out_blocklist/opt_out_blocklist.cc | 221 ++++ .../opt_out_blocklist/opt_out_blocklist.h | 183 +++ .../opt_out_blocklist/opt_out_blocklist_data.cc | 173 +++ .../opt_out_blocklist/opt_out_blocklist_data.h | 176 +++ .../opt_out_blocklist/opt_out_blocklist_delegate.h | 41 + .../opt_out_blocklist/opt_out_blocklist_item.cc | 74 ++ .../opt_out_blocklist/opt_out_blocklist_item.h | 99 ++ .../opt_out_blocklist_item_unittest.cc | 80 ++ .../opt_out_blocklist_unittest.cc | 1194 +++++++++++++++++++ .../blocklist/opt_out_blocklist/opt_out_store.h | 49 + .../blocklist/opt_out_blocklist/sql/BUILD.gn | 32 + .../blocklist/opt_out_blocklist/sql/DEPS | 3 + .../opt_out_blocklist/sql/opt_out_store_sql.cc | 410 +++++++ .../opt_out_blocklist/sql/opt_out_store_sql.h | 68 ++ .../sql/opt_out_store_sql_unittest.cc | 310 +++++ chromium/components/bookmarks/browser/BUILD.gn | 5 +- .../components/bookmarks/browser/bookmark_model.h | 2 +- .../bookmarks/browser/bookmark_node_data.cc | 1 + .../browser/bookmark_node_data_read_fuzzer.cc | 1 + .../browser/bookmark_node_data_unittest.cc | 4 +- .../bookmarks/browser/bookmark_node_data_views.cc | 3 +- .../bookmarks/browser/bookmark_storage.cc | 32 - .../bookmarks/browser/bookmark_test_util.cc | 1 + chromium/components/bookmarks/managed/BUILD.gn | 38 +- chromium/components/browser_sync/PRESUBMIT.py | 7 +- .../browser_sync/browser_sync_switches.cc | 2 +- .../browser_sync/signin_confirmation_helper.cc | 1 + .../browser_ui/android/bottomsheet/BUILD.gn | 42 +- .../components/browser_ui/android/bottomsheet/DEPS | 7 + .../android/bottomsheet/internal/BUILD.gn | 44 + .../browser_ui/bottomsheet/BottomSheet.java | 1244 ++++++++++++++++++++ .../bottomsheet/BottomSheetControllerFactory.java | 33 + .../bottomsheet/BottomSheetControllerImpl.java | 505 ++++++++ .../bottomsheet/BottomSheetSwipeDetector.java | 269 +++++ .../bottomsheet/BottomSheetSwipeDetectorTest.java | 319 +++++ .../bottomsheet/TouchRestrictingFrameLayout.java | 50 + .../bottomsheet/java/res/layout/bottom_sheet.xml | 44 + .../android/bottomsheet/java/res/values/dimens.xml | 12 + .../bottomsheet/BottomSheetController.java | 158 +++ .../bottomsheet/BottomSheetObserver.java | 57 + .../bottomsheet/EmptyBottomSheetObserver.java | 30 + .../bottomsheet/ManagedBottomSheetController.java | 54 + .../browser_ui/android/bottomsheet/test/BUILD.gn | 16 + .../browser_ui/client_certificate/OWNERS | 7 + .../browser_ui/client_certificate/android/BUILD.gn | 58 + .../browser_ui/client_certificate/android/DEPS | 6 + .../SSLClientCertificateRequest.java | 320 +++++ .../SSLClientCertificateRequestTest.java | 63 + .../android/ssl_client_certificate_request.cc | 394 +++++++ .../android/ssl_client_certificate_request.h | 32 + .../browser_ui/http_auth/android/BUILD.gn | 33 + .../components/browser_ui/http_auth/android/DEPS | 3 + .../components/browser_ui/http_auth/android/OWNERS | 1 + .../browser_ui/http_auth/android/java/res/OWNERS | 9 + .../android/java/res/layout/http_auth_dialog.xml | 52 + .../browser_ui/http_auth/LoginPrompt.java | 142 +++ chromium/components/browser_ui/media/OWNERS | 4 + .../components/browser_ui/media/android/BUILD.gn | 95 ++ chromium/components/browser_ui/media/android/DEPS | 6 + .../java/res/drawable-hdpi/audio_playing.png | Bin 0 -> 292 bytes .../res/drawable-hdpi/audio_playing_square.png | Bin 0 -> 770 bytes .../drawable-hdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 324 bytes .../drawable-hdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 338 bytes .../res/drawable-hdpi/ic_skip_next_white_36dp.png | Bin 0 -> 248 bytes .../drawable-hdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 271 bytes .../java/res/drawable-mdpi/audio_playing.png | Bin 0 -> 201 bytes .../res/drawable-mdpi/audio_playing_square.png | Bin 0 -> 521 bytes .../drawable-mdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 248 bytes .../drawable-mdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 272 bytes .../res/drawable-mdpi/ic_skip_next_white_36dp.png | Bin 0 -> 183 bytes .../drawable-mdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 201 bytes .../java/res/drawable-xhdpi/audio_playing.png | Bin 0 -> 352 bytes .../res/drawable-xhdpi/audio_playing_square.png | Bin 0 -> 1056 bytes .../drawable-xhdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 326 bytes .../drawable-xhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 375 bytes .../res/drawable-xhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 281 bytes .../drawable-xhdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 309 bytes .../java/res/drawable-xxhdpi/audio_playing.png | Bin 0 -> 517 bytes .../res/drawable-xxhdpi/audio_playing_square.png | Bin 0 -> 1851 bytes .../drawable-xxhdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 545 bytes .../drawable-xxhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 546 bytes .../drawable-xxhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 384 bytes .../ic_skip_previous_white_36dp.png | Bin 0 -> 402 bytes .../java/res/drawable-xxxhdpi/audio_playing.png | Bin 0 -> 675 bytes .../res/drawable-xxxhdpi/audio_playing_square.png | Bin 0 -> 2695 bytes .../ic_fast_forward_white_36dp.png | Bin 0 -> 625 bytes .../drawable-xxxhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 644 bytes .../drawable-xxxhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 434 bytes .../ic_skip_previous_white_36dp.png | Bin 0 -> 442 bytes .../browser_ui/media/MediaImageCallback.java | 22 + .../browser_ui/media/MediaImageManager.java | 235 ++++ .../browser_ui/media/MediaImageManagerTest.java | 298 +++++ .../MediaNotificationButtonComputationTest.java | 96 ++ .../media/MediaNotificationController.java | 905 ++++++++++++++ .../media/MediaNotificationImageUtils.java | 74 ++ .../browser_ui/media/MediaNotificationInfo.java | 354 ++++++ .../media/MediaNotificationListener.java | 57 + .../browser_ui/media/MediaNotificationUma.java | 47 + .../browser_ui/media/MediaSessionHelper.java | 573 +++++++++ .../browser_ui/media/MediaSessionUma.java | 42 + .../browser_ui/modaldialog/android/BUILD.gn | 2 +- .../modaldialog/AppModalPresenterTest.java | 14 +- .../modaldialog/ModalDialogViewTest.java | 20 +- .../browser_ui/notifications/android/BUILD.gn | 1 + .../notifications/ForegroundServiceUtils.java | 91 ++ .../browser_ui/settings/android/BUILD.gn | 4 + .../java/res/layout/settings_action_bar_shadow.xml | 19 + .../settings/android/java/res/values/styles.xml | 43 +- .../browser_ui/settings/ChromeBasePreference.java | 1 - .../browser_ui/settings/IconPreference.java | 38 + chromium/components/browser_ui/share/DEPS | 7 + chromium/components/browser_ui/share/OWNERS | 5 + .../components/browser_ui/share/android/BUILD.gn | 53 + .../android/java/res/layout/share_dialog_item.xml | 29 + .../browser_ui/share/ShareDialogAdapter.java | 52 + .../components/browser_ui/share/ShareHelper.java | 394 +++++++ .../browser_ui/share/ShareImageFileUtils.java | 449 +++++++ .../browser_ui/share/ShareImageFileUtilsTest.java | 348 ++++++ .../components/browser_ui/share/ShareParams.java | 246 ++++ chromium/components/browser_ui/site_settings/DEPS | 1 + .../browser_ui/site_settings/android/BUILD.gn | 8 +- .../java/res/drawable/settings_bluetooth.xml | 5 + .../java/res/xml/site_settings_preferences.xml | 46 +- .../site_settings/ContentSettingsResources.java | 10 + .../browser_ui/site_settings/PermissionInfo.java | 45 +- .../site_settings/SingleCategorySettings.java | 35 +- .../site_settings/SingleWebsiteSettings.java | 61 +- .../browser_ui/site_settings/SiteSettings.java | 7 +- .../site_settings/SiteSettingsCategory.java | 21 +- .../site_settings/SiteSettingsPrefClient.java | 7 - .../site_settings/SiteSettingsPreference.java | 36 - .../site_settings/WebsitePermissionsFetcher.java | 43 +- .../site_settings/WebsitePreference.java | 14 +- .../site_settings/WebsitePreferenceBridge.java | 55 +- .../site_settings/WebsiteAddressTest.java | 2 +- .../android/site_settings_feature_list.cc | 1 + .../site_settings/android/storage_info_fetcher.cc | 17 +- .../android/website_preference_bridge.cc | 18 +- .../strings/android/browser_ui_strings.grd | 135 ++- ...INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 | 1 + .../scheduled_for_later.png.sha1 | 1 + .../browser_ui/strings/android/site_settings.grdp | 12 + .../IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 | 1 + ...EBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 | 1 + ...TE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 | 1 + .../android/translations/browser_ui_strings_af.xtb | 15 + .../android/translations/browser_ui_strings_am.xtb | 15 + .../android/translations/browser_ui_strings_ar.xtb | 15 + .../android/translations/browser_ui_strings_as.xtb | 15 + .../android/translations/browser_ui_strings_az.xtb | 15 + .../android/translations/browser_ui_strings_be.xtb | 15 + .../android/translations/browser_ui_strings_bg.xtb | 15 + .../android/translations/browser_ui_strings_bn.xtb | 15 + .../android/translations/browser_ui_strings_bs.xtb | 15 + .../android/translations/browser_ui_strings_ca.xtb | 15 + .../android/translations/browser_ui_strings_cs.xtb | 15 + .../android/translations/browser_ui_strings_da.xtb | 15 + .../android/translations/browser_ui_strings_de.xtb | 15 + .../android/translations/browser_ui_strings_el.xtb | 15 + .../translations/browser_ui_strings_en-GB.xtb | 15 + .../translations/browser_ui_strings_es-419.xtb | 15 + .../android/translations/browser_ui_strings_es.xtb | 15 + .../android/translations/browser_ui_strings_et.xtb | 15 + .../android/translations/browser_ui_strings_eu.xtb | 15 + .../android/translations/browser_ui_strings_fa.xtb | 15 + .../android/translations/browser_ui_strings_fi.xtb | 15 + .../translations/browser_ui_strings_fil.xtb | 15 + .../translations/browser_ui_strings_fr-CA.xtb | 15 + .../android/translations/browser_ui_strings_fr.xtb | 15 + .../android/translations/browser_ui_strings_gl.xtb | 15 + .../android/translations/browser_ui_strings_gu.xtb | 15 + .../android/translations/browser_ui_strings_hi.xtb | 15 + .../android/translations/browser_ui_strings_hr.xtb | 15 + .../android/translations/browser_ui_strings_hu.xtb | 15 + .../android/translations/browser_ui_strings_hy.xtb | 15 + .../android/translations/browser_ui_strings_id.xtb | 15 + .../android/translations/browser_ui_strings_is.xtb | 15 + .../android/translations/browser_ui_strings_it.xtb | 15 + .../android/translations/browser_ui_strings_iw.xtb | 15 + .../android/translations/browser_ui_strings_ja.xtb | 15 + .../android/translations/browser_ui_strings_ka.xtb | 15 + .../android/translations/browser_ui_strings_kk.xtb | 15 + .../android/translations/browser_ui_strings_km.xtb | 15 + .../android/translations/browser_ui_strings_kn.xtb | 15 + .../android/translations/browser_ui_strings_ko.xtb | 15 + .../android/translations/browser_ui_strings_ky.xtb | 15 + .../android/translations/browser_ui_strings_lo.xtb | 15 + .../android/translations/browser_ui_strings_lt.xtb | 15 + .../android/translations/browser_ui_strings_lv.xtb | 15 + .../android/translations/browser_ui_strings_mk.xtb | 15 + .../android/translations/browser_ui_strings_ml.xtb | 15 + .../android/translations/browser_ui_strings_mn.xtb | 15 + .../android/translations/browser_ui_strings_mr.xtb | 15 + .../android/translations/browser_ui_strings_ms.xtb | 15 + .../android/translations/browser_ui_strings_my.xtb | 15 + .../android/translations/browser_ui_strings_ne.xtb | 15 + .../android/translations/browser_ui_strings_nl.xtb | 15 + .../android/translations/browser_ui_strings_no.xtb | 15 + .../android/translations/browser_ui_strings_or.xtb | 15 + .../android/translations/browser_ui_strings_pa.xtb | 15 + .../android/translations/browser_ui_strings_pl.xtb | 15 + .../translations/browser_ui_strings_pt-BR.xtb | 15 + .../translations/browser_ui_strings_pt-PT.xtb | 15 + .../android/translations/browser_ui_strings_ro.xtb | 15 + .../android/translations/browser_ui_strings_ru.xtb | 15 + .../android/translations/browser_ui_strings_si.xtb | 15 + .../android/translations/browser_ui_strings_sk.xtb | 15 + .../android/translations/browser_ui_strings_sl.xtb | 15 + .../android/translations/browser_ui_strings_sq.xtb | 15 + .../translations/browser_ui_strings_sr-Latn.xtb | 15 + .../android/translations/browser_ui_strings_sr.xtb | 15 + .../android/translations/browser_ui_strings_sv.xtb | 15 + .../android/translations/browser_ui_strings_sw.xtb | 15 + .../android/translations/browser_ui_strings_ta.xtb | 15 + .../android/translations/browser_ui_strings_te.xtb | 15 + .../android/translations/browser_ui_strings_th.xtb | 15 + .../android/translations/browser_ui_strings_tr.xtb | 15 + .../android/translations/browser_ui_strings_uk.xtb | 15 + .../android/translations/browser_ui_strings_ur.xtb | 15 + .../android/translations/browser_ui_strings_uz.xtb | 15 + .../android/translations/browser_ui_strings_vi.xtb | 15 + .../translations/browser_ui_strings_zh-CN.xtb | 15 + .../translations/browser_ui_strings_zh-HK.xtb | 15 + .../translations/browser_ui_strings_zh-TW.xtb | 15 + .../android/translations/browser_ui_strings_zu.xtb | 15 + .../components/browser_ui/styles/android/BUILD.gn | 34 + .../color/default_icon_color_light_tint_list.xml | 12 +- .../java/res/drawable-hdpi/ic_pause_white_24dp.png | Bin 0 -> 103 bytes .../java/res/drawable-hdpi/ic_pause_white_36dp.png | Bin 0 -> 123 bytes .../res/drawable-hdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 194 bytes .../res/drawable-hdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 242 bytes .../android/java/res/drawable-hdpi/top_round.9.png | Bin 0 -> 558 bytes .../java/res/drawable-mdpi/ic_pause_white_24dp.png | Bin 0 -> 82 bytes .../java/res/drawable-mdpi/ic_pause_white_36dp.png | Bin 0 -> 103 bytes .../res/drawable-mdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 154 bytes .../res/drawable-mdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 194 bytes .../android/java/res/drawable-mdpi/top_round.9.png | Bin 0 -> 357 bytes .../java/res/drawable-night-hdpi/top_round.9.png | Bin 0 -> 832 bytes .../java/res/drawable-night-mdpi/top_round.9.png | Bin 0 -> 557 bytes .../java/res/drawable-night-xhdpi/top_round.9.png | Bin 0 -> 1058 bytes .../java/res/drawable-night-xxhdpi/top_round.9.png | Bin 0 -> 1616 bytes .../res/drawable-night-xxxhdpi/top_round.9.png | Bin 0 -> 2135 bytes .../res/drawable-xhdpi/ic_pause_white_24dp.png | Bin 0 -> 90 bytes .../res/drawable-xhdpi/ic_pause_white_36dp.png | Bin 0 -> 92 bytes .../drawable-xhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 220 bytes .../drawable-xhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 283 bytes .../java/res/drawable-xhdpi/top_round.9.png | Bin 0 -> 729 bytes .../res/drawable-xxhdpi/ic_pause_white_24dp.png | Bin 0 -> 92 bytes .../res/drawable-xxhdpi/ic_pause_white_36dp.png | Bin 0 -> 158 bytes .../drawable-xxhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 283 bytes .../drawable-xxhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 390 bytes .../java/res/drawable-xxhdpi/top_round.9.png | Bin 0 -> 964 bytes .../res/drawable-xxxhdpi/ic_pause_white_24dp.png | Bin 0 -> 94 bytes .../res/drawable-xxxhdpi/ic_pause_white_36dp.png | Bin 0 -> 110 bytes .../drawable-xxxhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 343 bytes .../drawable-xxxhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 461 bytes .../java/res/drawable-xxxhdpi/top_round.9.png | Bin 0 -> 1335 bytes .../android/java/res/drawable/ic_security_grey.xml | 11 + .../android/java/res/drawable/ic_update_grey.xml | 11 + .../android/java/res/drawable/ic_vpn_key_grey.xml | 5 + .../components/browser_ui/util/android/BUILD.gn | 8 + .../browser_ui/util/date/CalendarFactory.java | 44 + .../browser_ui/util/date/CalendarUtils.java | 57 + .../browser_ui/util/date/StringUtils.java | 52 + chromium/components/browser_ui/webshare/OWNERS | 5 + .../browser_ui/webshare/android/BUILD.gn | 42 + .../components/browser_ui/webshare/android/DEPS | 5 + .../browser_ui/webshare/BlobReceiver.java | 177 +++ .../browser_ui/webshare/ShareServiceImpl.java | 290 +++++ .../browser_ui/webshare/ShareServiceImplTest.java | 84 ++ .../browser_ui/webshare/SharedFileCollator.java | 74 ++ .../webshare/SharedFileCollatorTest.java | 108 ++ .../components/browser_ui/widget/android/BUILD.gn | 6 + .../java/res/layout/promo_dialog_layout.xml | 2 + .../widget/android/java/res/values/dimens.xml | 2 +- .../browser_ui/widget/DualControlLayoutTest.java | 3 +- .../browser_ui/widget/InsetObserverViewTest.java | 3 +- .../browser_ui/widget/MoreProgressButtonTest.java | 5 +- .../components/browser_ui/widget/PromoDialog.java | 17 +- .../browser_ui/widget/PromoDialogLayout.java | 14 +- .../browser_ui/widget/PromoDialogTest.java | 2 +- .../browser_ui/widget/RadioButtonLayoutTest.java | 3 +- .../browser_ui/widget/RadioButtonRenderTest.java | 6 +- .../RadioButtonWithDescriptionLayoutTest.java | 3 +- .../widget/RadioButtonWithEditTextTest.java | 19 +- .../widget/RoundedIconGeneratorTest.java | 3 +- .../browser_ui/widget/WrappingLayoutTest.java | 6 +- .../widget/highlight/ViewHighlighterTest.java | 3 +- .../widget/image_tiles/TileCoordinatorImpl.java | 2 +- .../widget/image_tiles/TileListView.java | 24 +- .../widget/listmenu/ListMenuRenderTest.java | 3 +- .../widget/promo/PromoCardCoordinatorTest.java | 2 +- .../widget/promo/PromoCardImpressionTest.java | 3 +- .../widget/promo/PromoCardViewRenderTest.java | 18 +- .../browser_ui/widget/scrim/ScrimTest.java | 20 +- .../widget/text/AlertDialogEditText.java | 62 +- .../activity_report_user_stream_data_source.h | 1 - .../browsing_data/content/appcache_helper.cc | 5 +- .../content/browsing_data_helper_browsertest.h | 2 +- .../browsing_data/content/cache_storage_helper.cc | 5 +- .../browsing_data/content/cookie_helper.cc | 16 +- .../browsing_data/content/cookie_helper.h | 3 + .../browsing_data/content/database_helper.cc | 4 +- .../browsing_data/content/file_system_helper.cc | 9 +- .../browsing_data/content/indexed_db_helper.cc | 5 +- .../browsing_data/content/local_storage_helper.cc | 9 +- .../browsing_data/content/service_worker_helper.cc | 5 +- .../browsing_data/content/shared_worker_helper.cc | 9 +- .../browsing_data/core/browsing_data_utils.cc | 132 ++- .../core/browsing_data_utils_unittest.cc | 59 +- .../browsing_data/core/counters/history_counter.cc | 6 +- .../browsing_data/core/counters/history_counter.h | 3 +- .../core/counters/passwords_counter.cc | 217 +++- .../core/counters/passwords_counter.h | 49 +- chromium/components/browsing_data_strings.grdp | 8 +- .../content/captive_portal_service.cc | 70 -- .../content/captive_portal_service.h | 6 - .../content/captive_portal_tab_helper_unittest.cc | 12 +- chromium/components/cast_certificate/cast_crl.cc | 1 + chromium/components/cast_channel/BUILD.gn | 38 +- chromium/components/cast_channel/README.md | 30 + chromium/components/cast_channel/cast_auth_util.cc | 19 +- chromium/components/cast_channel/cast_auth_util.h | 2 + .../cast_channel/cast_auth_util_fuzzer.cc | 45 + chromium/components/cast_channel/cast_framer.cc | 1 + .../cast_channel/cast_framer_ingest_fuzzer.cc | 47 + .../cast_channel/cast_framer_serialize_fuzzer.cc | 24 + .../components/cast_channel/cast_message_fuzzer.cc | 48 - .../cast_channel/cast_message_handler.cc | 28 +- .../components/cast_channel/cast_message_handler.h | 8 +- .../cast_channel/cast_message_handler_unittest.cc | 111 +- .../components/cast_channel/cast_message_util.cc | 6 +- .../components/cast_channel/cast_message_util.h | 18 +- .../cast_channel/cast_message_util_fuzzer.cc | 168 +++ .../cast_channel/cast_message_util_unittest.cc | 21 +- chromium/components/cast_channel/cast_socket.cc | 9 +- .../components/cast_channel/cast_socket_service.cc | 4 +- chromium/components/cast_channel/cast_test_util.h | 10 +- chromium/components/cast_channel/enum_table.h | 4 +- chromium/components/cast_channel/fuzz.dict | 65 + chromium/components/cast_channel/proto/BUILD.gn | 10 +- .../cast_channel/proto/authority_keys.proto | 17 - .../cast_channel/proto/cast_channel.proto | 99 -- .../cast_channel/proto/fuzzer_inputs.proto | 166 +++ chromium/components/cbor/values.h | 3 +- chromium/components/cdm/browser/BUILD.gn | 1 + .../cdm/browser/media_drm_storage_impl.cc | 11 +- .../chrome_require_ct_delegate.cc | 1 + chromium/components/chromeos_camera/BUILD.gn | 13 - chromium/components/chromeos_camera/DEPS | 1 - .../chromeos_camera/camera_app_helper_impl.cc | 66 -- .../chromeos_camera/camera_app_helper_impl.h | 60 - .../components/chromeos_camera/common/BUILD.gn | 5 - .../chromeos_camera/common/camera_app_helper.mojom | 43 - .../jpeg_encode_accelerator_unittest.cc | 3 +- .../cloud_devices/common/description_items.h | 2 +- .../cloud_devices/common/printer_description.h | 1 - .../component_updater/crl_set_remover.cc | 5 +- chromium/components/components_strings.grd | 5 + ...NS_ADVANCED_SECTION_TITLE_SAFETY_CHECK.png.sha1 | 1 + .../constrained_window/constrained_window_views.cc | 1 + .../constrained_window/constrained_window_views.h | 4 +- ...tive_web_contents_modal_dialog_manager_views.cc | 1 + .../components/content_settings/android/BUILD.gn | 25 +- chromium/components/content_settings/android/DEPS | 6 + .../android/cookie_controls_bridge.cc | 106 ++ .../android/cookie_controls_bridge.h | 59 + .../content_settings/CookieControlsBridge.java | 86 ++ .../android/java_templates/PrefNames.java.tmpl | 14 + .../components/content_settings/browser/BUILD.gn | 13 + .../browser/content_settings_usages_state.cc | 78 +- .../browser/content_settings_usages_state.h | 23 +- .../browser/tab_specific_content_settings.cc | 561 +++++---- .../browser/tab_specific_content_settings.h | 255 ++-- .../tab_specific_content_settings_unittest.cc | 259 ++-- .../test_tab_specific_content_settings_delegate.cc | 63 + .../test_tab_specific_content_settings_delegate.h | 47 + .../content_settings/browser/ui/BUILD.gn | 21 + .../browser/ui/cookie_controls_controller.cc | 161 +++ .../browser/ui/cookie_controls_controller.h | 106 ++ .../browser/ui/cookie_controls_view.h | 25 + .../content_settings/core/browser/BUILD.gn | 1 + .../browser/content_settings_policy_provider.cc | 60 +- .../browser/content_settings_policy_provider.h | 7 + .../core/browser/content_settings_pref.cc | 1 + .../core/browser/content_settings_provider.cc | 14 + .../core/browser/content_settings_provider.h | 10 + .../core/browser/content_settings_registry.cc | 2 +- .../core/browser/content_settings_rule.cc | 10 + .../core/browser/content_settings_rule.h | 10 + .../core/browser/cookie_settings.cc | 15 +- .../core/browser/cookie_settings_unittest.cc | 42 + .../core/browser/host_content_settings_map.cc | 24 + .../core/browser/host_content_settings_map.h | 5 + .../content_settings/core/browser/uma_util.h | 1 - .../core/common/content_settings.mojom | 2 + .../core/common/content_settings_mojom_traits.cc | 2 +- .../core/common/content_settings_mojom_traits.h | 4 + .../core/common/content_settings_pattern.cc | 4 + .../core/common/content_settings_pattern.h | 8 + .../common/content_settings_pattern_unittest.cc | 19 + .../content_settings/core/common/features.cc | 7 +- .../content_settings/core/common/features.h | 6 +- .../content_settings/core/common/pref_names.h | 1 - .../renderer/content_settings_agent_impl.cc | 25 +- .../renderer/content_settings_agent_impl.h | 3 +- .../content_settings_agent_impl_browsertest.cc | 3 +- chromium/components/country_codes/country_codes.cc | 4 - .../components/crash/android/crash_keys_android.cc | 1 + .../content/browser/child_exit_observer_android.cc | 143 +-- .../content/browser/child_exit_observer_android.h | 39 +- .../content/browser/crash_handler_host_linux.cc | 9 +- .../components/crash/core/app/breakpad_linux.cc | 1 + .../crash/core/browser/resources/crashes.html | 1 - .../components/crash/core/common/objc_zombie.mm | 1 + chromium/components/cronet/android/BUILD.gn | 42 +- .../data_reduction_proxy/core/browser/BUILD.gn | 2 + .../data_reduction_proxy_config_service_client.cc | 4 +- ...duction_proxy_config_service_client_unittest.cc | 17 +- .../core/browser/data_reduction_proxy_service.cc | 7 +- .../core/browser/data_reduction_proxy_service.h | 4 +- .../core/browser/data_reduction_proxy_settings.cc | 18 +- .../core/browser/data_reduction_proxy_settings.h | 11 +- .../browser/data_reduction_proxy_test_utils.cc | 35 +- .../core/browser/data_reduction_proxy_test_utils.h | 25 +- .../core/common/data_reduction_proxy_switches.cc | 5 + .../core/common/data_reduction_proxy_switches.h | 1 + .../data_reduction_proxy/proto/client_config.proto | 4 +- .../components/data_use_measurement/core/BUILD.gn | 10 +- chromium/components/data_use_measurement/core/DEPS | 1 + .../core/data_use_measurement.cc | 156 ++- .../core/data_use_measurement.h | 72 +- .../core/data_use_measurement_unittest.cc | 47 +- .../core/data_use_pref_names.h | 30 + .../core/data_use_tracker_prefs.cc | 130 ++ .../core/data_use_tracker_prefs.h | 70 ++ .../core/data_use_tracker_prefs_unittest.cc | 173 +++ .../components/dbus/menu/menu_property_list.cc | 11 +- .../components/device_event_log/device_event_log.h | 9 + .../common/discardable_shared_memory_heap.cc | 8 +- .../service/discardable_shared_memory_manager.cc | 1 + chromium/components/dom_distiller/OWNERS | 1 - .../components/dom_distiller/content/browser/DEPS | 1 + .../content/browser/dom_distiller_viewer_source.cc | 71 +- .../content/browser/dom_distiller_viewer_source.h | 4 +- .../dom_distiller/content/common/mojom/BUILD.gn | 1 - .../mojom/distiller_page_notifier_service.mojom | 11 - .../dom_distiller/content/renderer/BUILD.gn | 2 - .../renderer/distiller_js_render_frame_observer.cc | 39 +- .../renderer/distiller_js_render_frame_observer.h | 19 +- .../distiller_page_notifier_service_impl.cc | 27 - .../distiller_page_notifier_service_impl.h | 37 - .../core/css/distilledpage_desktop.css | 16 +- .../core/html/dom_distiller_viewer.html | 11 +- .../dom_distiller/ios/distiller_page_ios.mm | 6 +- chromium/components/domain_reliability/OWNERS | 1 - .../domain_reliability/quic_error_mapping.cc | 9 + .../download/database/download_db_conversions.cc | 72 +- .../download/database/download_db_conversions.h | 8 + .../database/download_db_conversions_unittest.cc | 22 + .../database/in_progress/in_progress_info.cc | 3 +- .../database/in_progress/in_progress_info.h | 8 +- .../download/database/proto/download_entry.proto | 6 + .../internal/background_service/controller_impl.cc | 1 + .../background_service/file_monitor_impl.cc | 1 + .../download/internal/background_service/stats.cc | 10 +- .../download/internal/background_service/stats.h | 4 - .../download/internal/common/base_file.cc | 6 +- .../download/internal/common/download_item_impl.cc | 95 +- .../internal/common/download_item_impl_delegate.cc | 11 +- .../internal/common/download_item_impl_unittest.cc | 296 +++-- .../download/internal/common/download_stats.cc | 4 + .../internal/common/download_stats_unittest.cc | 7 + .../download/internal/common/download_utils.cc | 1 + .../common/in_progress_download_manager.cc | 22 +- .../common/simple_download_manager_coordinator.cc | 7 +- .../components/download/public/common/BUILD.gn | 11 +- .../public/common/auto_resumption_handler.cc | 163 ++- .../public/common/auto_resumption_handler.h | 38 +- .../common/auto_resumption_handler_unittest.cc | 141 ++- .../components/download/public/common/base_file.h | 2 +- .../download/public/common/download_features.cc | 8 +- .../download/public/common/download_features.h | 14 +- .../download/public/common/download_item.h | 11 +- .../download/public/common/download_item_impl.h | 20 + .../public/common/download_item_impl_delegate.h | 2 + .../download/public/common/download_schedule.cc | 24 + .../download/public/common/download_schedule.h | 38 + .../public/common/download_schedule_unittest.cc | 30 + .../download/public/common/download_stats.h | 24 +- .../download/public/common/mock_download_item.h | 6 + .../public/common/mock_download_item_impl.cc | 1 + .../public/common/mock_download_item_impl.h | 4 +- .../download/public/task/download_task_types.h | 3 + .../components/download/public/task/task_manager.h | 12 +- .../download/public/task/task_manager_impl.cc | 7 +- .../download/public/task/task_manager_unittest.cc | 28 + chromium/components/embedder_support/BUILD.gn | 1 + .../components/embedder_support/android/BUILD.gn | 4 +- .../browser_context/browser_context_handle.cc | 3 + .../delegate/web_contents_delegate_android.cc | 3 +- .../delegate/web_contents_delegate_android.h | 3 +- .../delegate/ColorPickerDialogRenderTest.java | 3 +- .../embedder_support/view/ContentView.java | 154 ++- .../embedder_support/util/OriginTest.java | 3 +- .../embedder_support/android/metrics/BUILD.gn | 4 +- .../embedder_support/android/metrics/DEPS | 3 + .../metrics/android_metrics_log_uploader.cc | 12 + .../metrics/android_metrics_service_client.cc | 278 ++++- .../metrics/android_metrics_service_client.h | 46 +- .../android_metrics_service_client_unittest.cc | 2 +- .../android/view/content_view_render_view.h | 1 - .../embedder_support/origin_trials/BUILD.gn | 2 + .../embedder_support/origin_trials/features.cc | 20 + .../embedder_support/origin_trials/features.h | 20 + .../origin_trials/origin_trial_policy_impl.cc | 20 + .../origin_trials/origin_trial_policy_impl.h | 1 + .../origin_trial_policy_impl_unittest.cc | 13 + chromium/components/enterprise/BUILD.gn | 19 + chromium/components/enterprise/DEPS | 5 + chromium/components/enterprise/OWNERS | 11 + .../enterprise/browser/reporting/policy_info.cc | 142 +++ .../enterprise/browser/reporting/policy_info.h | 36 + .../browser/reporting/report_request_definition.h | 29 + chromium/components/enterprise/common/BUILD.gn | 10 + .../components/enterprise/common/proto/BUILD.gn | 12 + .../enterprise/common/proto/connectors.proto | 106 ++ chromium/components/enterprise/common/strings.cc | 13 + chromium/components/enterprise/common/strings.h | 18 + .../error_page/common/localized_error.cc | 6 + .../components/error_page/common/net_error_info.h | 3 +- chromium/components/error_page_strings.grdp | 4 + chromium/components/exo/BUILD.gn | 8 +- .../exo/client_controlled_shell_surface.cc | 48 +- .../exo/client_controlled_shell_surface.h | 4 + .../client_controlled_shell_surface_unittest.cc | 2 + chromium/components/exo/data_device.cc | 71 +- chromium/components/exo/data_device.h | 11 +- chromium/components/exo/data_device_unittest.cc | 40 +- chromium/components/exo/data_offer.cc | 10 +- chromium/components/exo/data_offer.h | 5 +- chromium/components/exo/drag_drop_operation.cc | 14 +- chromium/components/exo/drag_drop_operation.h | 2 + .../components/exo/drag_drop_operation_unittest.cc | 127 ++ .../components/exo/fullscreen_shell_surface.cc | 62 +- chromium/components/exo/fullscreen_shell_surface.h | 11 +- chromium/components/exo/gamepad_delegate.h | 10 +- chromium/components/exo/gaming_seat.cc | 8 +- chromium/components/exo/gaming_seat_unittest.cc | 80 +- chromium/components/exo/keyboard.cc | 16 +- chromium/components/exo/keyboard_unittest.cc | 155 +-- .../components/exo/notification_surface_manager.h | 5 +- chromium/components/exo/pointer.cc | 19 +- chromium/components/exo/pointer.h | 2 +- chromium/components/exo/seat.cc | 8 +- chromium/components/exo/seat.h | 6 + chromium/components/exo/server/BUILD.gn | 21 + chromium/components/exo/server/OWNERS | 1 + .../exo/server/wayland_server_controller.cc | 62 + .../exo/server/wayland_server_controller.h | 61 + chromium/components/exo/shell_surface_base.cc | 49 +- chromium/components/exo/shell_surface_base.h | 7 +- chromium/components/exo/shell_surface_unittest.cc | 142 ++- chromium/components/exo/shell_surface_util.cc | 58 +- chromium/components/exo/shell_surface_util.h | 2 +- chromium/components/exo/sub_surface.cc | 6 + chromium/components/exo/sub_surface.h | 2 +- chromium/components/exo/sub_surface_unittest.cc | 22 + chromium/components/exo/surface.cc | 28 +- chromium/components/exo/surface.h | 6 +- chromium/components/exo/surface_unittest.cc | 67 +- chromium/components/exo/text_input.cc | 7 + chromium/components/exo/text_input.h | 2 + chromium/components/exo/touch.cc | 2 + chromium/components/exo/wayland/BUILD.gn | 5 +- chromium/components/exo/wayland/DEPS | 1 + chromium/components/exo/wayland/clients/blur.cc | 2 +- .../components/exo/wayland/clients/blur_main.cc | 1 + .../components/exo/wayland/clients/client_base.cc | 2 +- .../components/exo/wayland/clients/color_space.cc | 1 + .../wayland/clients/explicit_synchronization.cc | 2 +- .../exo/wayland/clients/fullscreen_shell.cc | 2 +- chromium/components/exo/wayland/clients/rects.cc | 2 +- chromium/components/exo/wayland/clients/simple.cc | 2 +- .../components/exo/wayland/clients/subsurface.cc | 4 +- chromium/components/exo/wayland/clients/yuv.cc | 1 + chromium/components/exo/wayland/server.cc | 35 +- chromium/components/exo/wayland/server.h | 2 + .../exo/wayland/wayland_display_observer.cc | 13 +- .../exo/wayland/wayland_display_observer.h | 8 +- .../exo/wayland/wayland_display_output.cc | 29 + .../exo/wayland/wayland_display_output.h | 13 +- .../components/exo/wayland/wayland_positioner.cc | 199 +++- .../components/exo/wayland/wayland_positioner.h | 25 +- .../exo/wayland/wayland_positioner_unittest.cc | 182 ++- chromium/components/exo/wayland/wl_compositor.cc | 7 +- .../exo/wayland/wl_data_device_manager.cc | 31 +- chromium/components/exo/wayland/wl_output.cc | 16 +- chromium/components/exo/wayland/xdg_shell.cc | 692 +++++++++++ chromium/components/exo/wayland/xdg_shell.h | 41 + chromium/components/exo/wayland/zaura_shell.cc | 4 +- .../components/exo/wayland/zaura_shell_unittest.cc | 34 + .../components/exo/wayland/zcr_gaming_input.cc | 17 +- .../exo/wayland/zwp_text_input_manager.cc | 1 + chromium/components/exo/wayland/zxdg_shell.cc | 13 +- chromium/components/exo/wayland/zxdg_shell.h | 14 +- chromium/components/exo/wm_helper_chromeos.cc | 7 +- chromium/components/exo/xdg_shell_surface.h | 1 - .../components/external_intents/android/BUILD.gn | 5 + .../android/external_intents_feature_list.cc | 1 + .../ExternalNavigationDelegate.java | 10 +- .../ExternalNavigationHandler.java | 182 ++- .../external_intents/ExternalNavigationParams.java | 64 +- .../InterceptNavigationDelegateImpl.java | 4 +- .../ExternalNavigationHandlerTest.java | 176 ++- .../external_intents/RedirectHandlerTest.java | 26 +- .../favicon/content/content_favicon_driver.cc | 50 +- .../favicon/content/content_favicon_driver.h | 7 + .../content/content_favicon_driver_unittest.cc | 73 +- .../components/favicon/core/favicon_driver_impl.cc | 4 +- .../components/favicon/core/favicon_driver_impl.h | 2 + .../components/favicon/core/favicon_handler.cc | 1 + ...ory_ui_favicon_request_handler_impl_unittest.cc | 211 ++-- chromium/components/favicon/core/test/BUILD.gn | 2 + .../components/favicon/ios/favicon_url_util.cc | 1 + .../internal/persistent_availability_store.cc | 1 + .../feature_engagement/EventConstants.java | 6 + .../feature_engagement/FeatureConstants.java | 7 + .../feature_engagement/public/event_constants.cc | 3 + .../feature_engagement/public/event_constants.h | 5 + .../feature_engagement/public/feature_constants.cc | 7 + .../feature_engagement/public/feature_constants.h | 3 + .../feature_engagement/public/feature_list.cc | 3 + .../feature_engagement/public/feature_list.h | 8 + chromium/components/federated_learning/BUILD.gn | 37 + chromium/components/federated_learning/DEPS | 3 + chromium/components/federated_learning/OWNERS | 5 + chromium/components/federated_learning/README.md | 7 + .../federated_learning/floc_blocklist_service.cc | 88 ++ .../federated_learning/floc_blocklist_service.h | 59 + .../floc_blocklist_service_unittest.cc | 178 +++ .../federated_learning/floc_constants.cc | 19 + .../components/federated_learning/floc_constants.h | 27 + chromium/components/federated_learning/floc_id.cc | 64 + chromium/components/federated_learning/floc_id.h | 46 + .../components/federated_learning/proto/BUILD.gn | 9 + .../federated_learning/proto/blocklist.proto | 13 + chromium/components/federated_learning/sim_hash.cc | 93 ++ chromium/components/federated_learning/sim_hash.h | 44 + .../federated_learning/sim_hash_unittest.cc | 203 ++++ chromium/components/feed/core/common/pref_names.cc | 2 + chromium/components/feed/core/common/pref_names.h | 2 + chromium/components/feed/core/proto/BUILD.gn | 1 + .../components/feed/core/proto/v2/packing.proto | 19 + .../feed/core/proto/v2/wire/capability.proto | 2 + .../feed/core/proto/v2/wire/client_info.proto | 7 +- .../feed/core/shared_prefs/pref_names.cc | 3 + chromium/components/feed/core/v2/BUILD.gn | 1 + chromium/components/feed/core/v2/config.cc | 5 + chromium/components/feed/core/v2/config.h | 3 + chromium/components/feed/core/v2/enums.cc | 6 + chromium/components/feed/core/v2/enums.h | 7 +- .../components/feed/core/v2/feed_network_impl.cc | 161 +-- .../components/feed/core/v2/feed_network_impl.h | 5 +- .../feed/core/v2/feed_network_impl_unittest.cc | 6 +- chromium/components/feed/core/v2/feed_store.h | 9 - chromium/components/feed/core/v2/feed_stream.cc | 87 +- chromium/components/feed/core/v2/feed_stream.h | 24 +- .../feed/core/v2/feed_stream_unittest.cc | 144 ++- .../components/feed/core/v2/metrics_reporter.cc | 100 +- .../components/feed/core/v2/metrics_reporter.h | 22 +- .../feed/core/v2/metrics_reporter_unittest.cc | 190 ++- chromium/components/feed/core/v2/prefs.cc | 44 +- chromium/components/feed/core/v2/prefs.h | 21 +- chromium/components/feed/core/v2/proto_util.cc | 27 +- chromium/components/feed/core/v2/proto_util.h | 12 +- .../components/feed/core/v2/proto_util_unittest.cc | 3 +- .../components/feed/core/v2/protocol_translator.cc | 61 +- .../components/feed/core/v2/protocol_translator.h | 5 + .../feed/core/v2/protocol_translator_unittest.cc | 37 + .../components/feed/core/v2/public/feed_service.cc | 53 +- .../components/feed/core/v2/public/feed_service.h | 5 + .../feed/core/v2/public/feed_stream_api.h | 12 + chromium/components/feed/core/v2/public/types.h | 6 + .../components/feed/core/v2/request_throttler.cc | 10 +- chromium/components/feed/core/v2/scheduling.cc | 21 +- chromium/components/feed/core/v2/stream_model.cc | 9 +- chromium/components/feed/core/v2/stream_model.h | 4 +- .../feed/core/v2/stream_model/ephemeral_change.cc | 2 +- .../feed/core/v2/stream_model/feature_tree.cc | 90 +- .../feed/core/v2/stream_model/feature_tree.h | 43 +- .../components/feed/core/v2/surface_updater.cc | 3 + .../core/v2/tasks/load_stream_from_store_task.cc | 2 - .../feed/core/v2/tasks/load_stream_task.cc | 15 +- .../feed/core/v2/tasks/upload_actions_task.cc | 65 +- .../feed/core/v2/tasks/upload_actions_task.h | 1 + chromium/components/feed/core/v2/types.cc | 34 + chromium/components/feed/core/v2/types.h | 12 + chromium/components/feed/core/v2/types_unittest.cc | 32 + chromium/components/feed/feed_feature_list.cc | 5 +- chromium/components/feed/feed_feature_list.h | 2 +- chromium/components/feedback/anonymizer_tool.cc | 21 +- .../feedback/anonymizer_tool_unittest.cc | 6 +- chromium/components/feedback/feedback_report.cc | 3 +- .../feedback/system_logs/system_logs_fetcher.cc | 18 +- .../find_in_page/android/find_in_page_bridge.cc | 3 +- .../components/find_in_page/find_tab_helper.cc | 36 +- chromium/components/find_in_page/find_tab_helper.h | 12 +- chromium/components/flags_ui/BUILD.gn | 2 + chromium/components/flags_ui/feature_entry.cc | 38 +- chromium/components/flags_ui/feature_entry.h | 87 +- .../components/flags_ui/feature_entry_macros.h | 50 +- chromium/components/flags_ui/flags_state.cc | 80 +- .../components/flags_ui/flags_state_unittest.cc | 43 +- chromium/components/flags_ui/flags_test_helpers.cc | 3 +- chromium/components/flags_ui/flags_ui_metrics.cc | 77 ++ chromium/components/flags_ui/flags_ui_metrics.h | 35 + .../flags_ui/pref_service_flags_storage.cc | 1 + chromium/components/flags_ui/resources/flags.html | 6 +- chromium/components/flags_ui/resources/flags.js | 4 +- chromium/components/gcm_driver/android/BUILD.gn | 2 + .../google/core/common/google_switches.cc | 6 + .../google/core/common/google_switches.h | 1 + .../components/google/core/common/google_util.cc | 9 +- .../components/google/core/common/google_util.h | 6 - .../gwp_asan/client/guarded_page_allocator.cc | 1 + .../client/sampling_malloc_shims_unittest.cc | 8 +- .../sampling_partitionalloc_shims_unittest.cc | 11 +- .../multi_process/client_connection_manager.cc | 33 +- .../heap_profiling/multi_process/supervisor.cc | 34 +- .../heap_profiling/multi_process/test_driver.cc | 30 +- .../heap_profiling/multi_process/test_driver.h | 2 +- chromium/components/history/core/browser/BUILD.gn | 1 + .../core/browser/android/android_cache_database.cc | 1 + .../core/browser/android/visit_sql_handler.cc | 7 +- .../browser/expire_history_backend_unittest.cc | 8 +- .../history/core/browser/history_backend.cc | 40 +- .../history/core/browser/history_backend.h | 1 + .../core/browser/history_backend_db_unittest.cc | 51 + .../core/browser/history_backend_unittest.cc | 220 +++- .../history/core/browser/history_database.cc | 9 +- .../core/browser/history_querying_unittest.cc | 2 +- .../history/core/browser/history_service.cc | 216 ++-- .../history/core/browser/history_service.h | 21 +- .../core/browser/history_service_unittest.cc | 123 +- .../history/core/browser/history_types.cc | 9 +- .../history/core/browser/history_types.h | 19 +- .../sync/delete_directive_handler_unittest.cc | 18 +- .../core/browser/sync/typed_url_sync_bridge.cc | 12 +- .../browser/sync/typed_url_sync_bridge_unittest.cc | 29 +- .../history/core/browser/thumbnail_database.cc | 1 + .../history/core/browser/top_sites_backend.cc | 1 + .../history/core/browser/top_sites_database.cc | 1 + .../core/browser/top_sites_impl_unittest.cc | 2 +- .../history/core/browser/url_database.cc | 1 + .../history/core/browser/url_database_unittest.cc | 1 + .../components/history/core/browser/url_row.cc | 2 + chromium/components/history/core/browser/url_row.h | 9 + .../history/core/browser/visit_database.cc | 54 +- .../history/core/browser/visit_database.h | 6 +- .../core/browser/visit_database_unittest.cc | 80 +- .../core/cache/image_data_store_disk.cc | 1 + .../image_fetcher/core/cached_image_fetcher.cc | 37 +- .../components/image_fetcher/core/image_fetcher.h | 2 +- chromium/components/infobars/android/BUILD.gn | 70 ++ chromium/components/infobars/android/DEPS | 3 + chromium/components/infobars/android/OWNERS | 10 + .../components/infobars/InfoBarControlLayout.java | 507 ++++++++ .../infobars/InfoBarControlLayoutTest.java | 187 +++ .../infobars/InfoBarInteractionHandler.java | 31 + .../components/infobars/InfoBarLayout.java | 573 +++++++++ .../components/infobars/InfoBarMessageView.java | 53 + .../res/drawable-hdpi/infobar_shadow_left.9.png | Bin 0 -> 163 bytes .../res/drawable-hdpi/infobar_shadow_top.png | Bin 0 -> 95 bytes .../res/drawable-mdpi/infobar_shadow_left.9.png | Bin 0 -> 133 bytes .../res/drawable-mdpi/infobar_shadow_top.png | Bin 0 -> 88 bytes .../res/drawable-xhdpi/infobar_shadow_left.9.png | Bin 0 -> 187 bytes .../res/drawable-xhdpi/infobar_shadow_top.png | Bin 0 -> 85 bytes .../res/drawable-xxhdpi/infobar_shadow_left.9.png | Bin 0 -> 245 bytes .../res/drawable-xxhdpi/infobar_shadow_top.png | Bin 0 -> 104 bytes .../res/drawable-xxxhdpi/infobar_shadow_left.9.png | Bin 0 -> 289 bytes .../res/drawable-xxxhdpi/infobar_shadow_top.png | Bin 0 -> 94 bytes .../infobar_control_icon_with_description.xml | 41 + .../android/res/layout/infobar_control_message.xml | 13 + .../android/res/layout/infobar_control_spinner.xml | 10 + .../layout/infobar_control_spinner_drop_down.xml | 17 + .../res/layout/infobar_control_spinner_view.xml | 25 + .../android/res/layout/infobar_control_toggle.xml | 36 + .../infobars/android/res/values/dimens.xml | 43 + .../components/infobars/android/res/values/ids.xml | 10 + chromium/components/infobars/content/BUILD.gn | 18 + chromium/components/infobars/content/DEPS | 4 + .../infobars/content/content_infobar_manager.cc | 115 ++ .../infobars/content/content_infobar_manager.h | 88 ++ .../components/infobars/core/infobar_delegate.cc | 3 +- .../components/infobars/core/infobar_delegate.h | 9 +- chromium/components/js_injection/DEPS | 6 + chromium/components/js_injection/OWNERS | 5 + chromium/components/js_injection/README.md | 3 + chromium/components/js_injection/browser/BUILD.gn | 27 + .../js_injection/browser/js_communication_host.cc | 227 ++++ .../js_injection/browser/js_communication_host.h | 110 ++ .../browser/js_to_browser_messaging.cc | 129 ++ .../js_injection/browser/js_to_browser_messaging.h | 66 ++ .../components/js_injection/browser/web_message.cc | 13 + .../components/js_injection/browser/web_message.h | 26 + .../js_injection/browser/web_message_host.h | 24 + .../browser/web_message_host_factory.h | 34 + .../js_injection/browser/web_message_reply_proxy.h | 25 + chromium/components/js_injection/common/BUILD.gn | 71 ++ chromium/components/js_injection/common/OWNERS | 4 + .../js_injection/common/interfaces.mojom | 65 + .../js_injection/common/origin_matcher.cc | 125 ++ .../js_injection/common/origin_matcher.h | 75 ++ .../js_injection/common/origin_matcher.mojom | 22 + .../js_injection/common/origin_matcher_internal.cc | 125 ++ .../js_injection/common/origin_matcher_internal.h | 89 ++ .../common/origin_matcher_mojom_traits.cc | 85 ++ .../common/origin_matcher_mojom_traits.h | 45 + .../js_injection/common/origin_matcher_unittest.cc | 404 +++++++ chromium/components/js_injection/renderer/BUILD.gn | 25 + chromium/components/js_injection/renderer/DEPS | 4 + .../components/js_injection/renderer/js_binding.cc | 246 ++++ .../components/js_injection/renderer/js_binding.h | 87 ++ .../js_injection/renderer/js_communication.cc | 133 +++ .../js_injection/renderer/js_communication.h | 76 ++ chromium/components/keep_alive_registry/BUILD.gn | 1 + .../keep_alive_registry/keep_alive_types.cc | 1 + .../browser_context_keyed_service_factory.h | 30 + .../language/core/common/language_experiments.cc | 12 - .../language/core/common/language_experiments.h | 10 - .../language/core/common/locale_util_unittest.cc | 7 +- .../language_usage_metrics.cc | 6 + .../leveldb_proto/internal/leveldb_database.cc | 1 + .../internal/unique_proto_database.cc | 1 + .../public/shared_proto_database_client_list.cc | 1 + chromium/components/lookalikes/core/BUILD.gn | 1 + chromium/components/lookalikes/core/features.cc | 3 + chromium/components/lookalikes/core/features.h | 4 + .../lookalikes/core/lookalike_url_util.cc | 372 ++++-- .../lookalikes/core/lookalike_url_util.h | 48 +- .../lookalikes/core/lookalike_url_util_unittest.cc | 242 ++-- chromium/components/management_strings.grdp | 47 +- .../IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1 | 1 + .../IDS_MANAGEMENT_CONNECTORS_EVENT.png.sha1 | 1 + ...IDS_MANAGEMENT_CONNECTORS_VISIBLE_DATA.png.sha1 | 1 + ...EMENT_DATA_LOSS_PREVENTION_PERMISSIONS.png.sha1 | 1 - ..._MANAGEMENT_ENTERPRISE_REPORTING_EVENT.png.sha1 | 1 + ...EMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1 | 1 - ...MENT_ENTERPRISE_REPORTING_VISIBLE_DATA.png.sha1 | 1 + .../IDS_MANAGEMENT_FILE_ATTACHED_EVENT.png.sha1 | 1 + ..._MANAGEMENT_FILE_ATTACHED_VISIBLE_DATA.png.sha1 | 1 + .../IDS_MANAGEMENT_FILE_DOWNLOADED_EVENT.png.sha1 | 1 + ...ANAGEMENT_FILE_DOWNLOADED_VISIBLE_DATA.png.sha1 | 1 + .../IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1 | 1 + ...ANAGEMENT_REPORT_APP_INFO_AND_ACTIVITY.png.sha1 | 1 + .../IDS_MANAGEMENT_TEXT_ENTERED_EVENT.png.sha1 | 1 + ...S_MANAGEMENT_TEXT_ENTERED_VISIBLE_DATA.png.sha1 | 1 + ...NAGEMENT_THREAT_PROTECTION_DESCRIPTION.png.sha1 | 2 +- ...EMENT_THREAT_PROTECTION_DESCRIPTION_BY.png.sha1 | 2 +- .../renderer/media_playback_options.cc | 1 - .../media_controls_progress_view_unittest.cc | 6 +- .../media_notification_background.h | 2 +- .../media_notification_container.h | 3 +- .../media_notification_view_impl.cc | 15 +- .../media_notification_view_impl.h | 4 +- .../media_notification_view_impl_unittest.cc | 24 +- .../vector_icons/vector_icons.cc.template | 1 - chromium/components/metal_util/BUILD.gn | 18 +- chromium/components/metal_util/device.mm | 1 + chromium/components/metal_util/hdr_copier_layer.mm | 4 +- chromium/components/metal_util/switches.cc | 11 + chromium/components/metal_util/switches.h | 16 + chromium/components/metal_util/test_shader.h | 13 +- chromium/components/metal_util/test_shader.mm | 221 +++- chromium/components/metrics/BUILD.gn | 39 +- chromium/components/metrics/DEPS | 6 + .../metrics/call_stack_profile_encoding.cc | 2 + .../components/metrics/call_stack_profile_params.h | 3 +- chromium/components/metrics/content/DEPS | 4 + .../metrics/content/gpu_metrics_provider.cc | 33 + .../metrics/content/gpu_metrics_provider.h | 29 + .../content/rendering_perf_metrics_provider.cc | 20 + .../content/rendering_perf_metrics_provider.h | 30 + .../metrics/content/subprocess_metrics_provider.cc | 210 ++++ .../metrics/content/subprocess_metrics_provider.h | 116 ++ .../subprocess_metrics_provider_unittest.cc | 161 +++ chromium/components/metrics/daily_event.cc | 1 + .../components/metrics/field_trials_provider.cc | 38 +- .../components/metrics/field_trials_provider.h | 15 + .../metrics/field_trials_provider_unittest.cc | 68 +- .../metrics/file_metrics_provider_unittest.cc | 1 + chromium/components/metrics/gpu/DEPS | 4 - .../components/metrics/gpu/gpu_metrics_provider.cc | 36 - .../components/metrics/gpu/gpu_metrics_provider.h | 29 - .../metrics/gpu/rendering_perf_metrics_provider.cc | 20 - .../metrics/gpu/rendering_perf_metrics_provider.h | 30 - chromium/components/metrics/log_decoder.cc | 10 + chromium/components/metrics/log_decoder.h | 13 + .../components/metrics/log_decoder_unittest.cc | 33 + chromium/components/metrics/log_store.h | 4 + chromium/components/metrics/metrics_log.cc | 2 + chromium/components/metrics/metrics_log.h | 8 + chromium/components/metrics/metrics_log_manager.cc | 6 +- chromium/components/metrics/metrics_log_store.cc | 22 +- chromium/components/metrics/metrics_log_store.h | 7 +- .../metrics/metrics_log_store_unittest.cc | 23 +- chromium/components/metrics/metrics_pref_names.cc | 11 + chromium/components/metrics/metrics_pref_names.h | 2 + chromium/components/metrics/metrics_scheduler.cc | 4 + chromium/components/metrics/metrics_service.cc | 18 +- chromium/components/metrics/metrics_service.h | 16 +- .../components/metrics/metrics_service_accessor.cc | 23 - .../components/metrics/metrics_service_accessor.h | 17 - .../components/metrics/metrics_service_client.cc | 29 +- .../components/metrics/metrics_service_client.h | 4 + .../components/metrics/metrics_service_unittest.cc | 129 +- .../metrics/net/net_metrics_log_uploader.cc | 60 + .../metrics/net/network_metrics_provider.cc | 98 -- .../metrics/net/network_metrics_provider.h | 10 - .../metrics/net/wifi_access_point_info_provider.cc | 25 - .../metrics/net/wifi_access_point_info_provider.h | 54 - .../wifi_access_point_info_provider_chromeos.cc | 124 -- .../net/wifi_access_point_info_provider_chromeos.h | 48 - chromium/components/metrics/reporting_service.cc | 4 + .../metrics/reporting_service_unittest.cc | 1 + .../components/metrics/stability_metrics_helper.cc | 3 - .../structured/structured_metrics_provider.cc | 1 + chromium/components/metrics/unsent_log_store.cc | 125 +- chromium/components/metrics/unsent_log_store.h | 59 +- .../components/metrics/unsent_log_store_metrics.cc | 30 + .../components/metrics/unsent_log_store_metrics.h | 23 +- .../metrics/unsent_log_store_metrics_impl.cc | 41 +- .../metrics/unsent_log_store_metrics_impl.h | 3 + .../unsent_log_store_metrics_impl_unittest.cc | 82 ++ .../metrics/unsent_log_store_unittest.cc | 146 ++- .../metrics_services_manager.cc | 2 +- .../metrics_services_manager_client.h | 4 +- .../minidump_uploader/CrashFileManagerTest.java | 5 +- .../MinidumpUploadCallableTest.java | 2 +- .../MinidumpUploadJobImplTest.java | 2 +- .../minidump_uploader/MinidumpUploaderTest.java | 2 +- .../rewrite_minidumps_as_mimes.cc | 1 + .../mirroring/browser/cast_remoting_sender.cc | 5 +- .../single_client_video_capture_host_unittest.cc | 10 +- .../mirroring/mojom/mirroring_service_host.mojom | 3 - .../mirroring/service/fake_video_capture_host.cc | 10 +- .../mirroring/service/mirror_settings.cc | 3 +- .../components/mirroring/service/mirror_settings.h | 13 +- .../components/mirroring/service/rtp_stream.cc | 6 +- .../mirroring/service/rtp_stream_unittest.cc | 3 +- chromium/components/mirroring/service/session.cc | 7 +- .../mirroring/service/udp_socket_client.h | 2 - .../mirroring/service/video_capture_client.cc | 16 +- .../service/video_capture_client_unittest.cc | 13 +- .../components/module_installer/android/BUILD.gn | 2 + .../module_installer/engine/FakeEngine.java | 15 +- .../logger/SplitAvailabilityLogger.java | 2 +- .../module_installer/android/module_desc_java.gni | 20 +- .../module_installer/android/module_desc_java.py | 87 +- chromium/components/nacl/broker/BUILD.gn | 4 +- chromium/components/nacl/browser/BUILD.gn | 2 - chromium/components/nacl/features.gni | 14 - chromium/components/nacl/loader/BUILD.gn | 8 +- .../components/navigation_interception/BUILD.gn | 5 +- .../navigation_interception/android/BUILD.gn | 5 +- .../navigation_interception/NavigationParams.java | 14 +- .../intercept_navigation_throttle.cc | 3 +- .../navigation_interception/navigation_params.cc | 25 +- .../navigation_interception/navigation_params.h | 7 +- .../navigation_params_android.cc | 4 +- .../navigation_metrics/navigation_metrics.cc | 27 +- .../navigation_metrics/navigation_metrics.h | 9 + .../navigation_metrics_unittest.cc | 10 - chromium/components/net_log/chrome_net_log.cc | 8 +- chromium/components/net_log/chrome_net_log.h | 2 +- .../net_log/net_export_file_writer_unittest.cc | 5 + .../components/net_log/resources/net_export.html | 6 +- chromium/components/neterror/resources/BUILD.gn | 1 - .../components/neterror/resources/neterror.html | 3 +- chromium/components/neterror/resources/neterror.js | 2 + .../components/network_session_configurator/OWNERS | 1 - .../browser/network_session_configurator.cc | 22 +- .../network_session_configurator_unittest.cc | 62 +- .../common/network_switch_list.h | 3 + .../click_based_category_ranker.cc | 1 + .../category_rankers/constant_category_ranker.cc | 1 + .../reading_list_suggestions_provider.cc | 1 + .../ntp_snippets/remote/json_request_unittest.cc | 19 +- .../remote_suggestions_fetcher_impl_unittest.cc | 10 +- .../remote_suggestions_status_service_impl.cc | 1 + .../ntp_snippets/remote/request_throttler.cc | 1 + .../components/ntp_snippets/user_classifier.cc | 1 + .../components/ntp_tiles/most_visited_sites.cc | 1 + .../offline_items_collection/core/BUILD.gn | 28 + .../OfflineContentAggregatorBridge.java | 14 + .../OfflineContentProvider.java | 3 + .../offline_items_collection/OfflineItem.java | 2 + .../OfflineItemSchedule.java | 37 + .../bridges/OfflineItemBridge.java | 12 +- .../bridges/OfflineItemBridgeUnitTest.java | 45 + .../android/offline_content_aggregator_bridge.cc | 20 + .../android/offline_content_aggregator_bridge.h | 7 + .../core/android/offline_item_bridge.cc | 16 +- .../core/android/offline_item_bridge.h | 5 + .../core/android/offline_item_bridge_unittest.cc | 68 ++ .../core/offline_content_aggregator.cc | 10 + .../core/offline_content_aggregator.h | 2 + .../core/offline_content_provider.h | 5 + .../offline_items_collection/core/offline_item.cc | 29 +- .../offline_items_collection/core/offline_item.h | 26 +- .../core/offline_item_unittest.cc | 26 + .../test_support/mock_offline_content_provider.h | 4 + .../core/throttled_offline_content_provider.cc | 6 + .../core/throttled_offline_content_provider.h | 2 + .../background/request_queue_store_unittest.cc | 1 + .../core/downloads/download_ui_adapter.cc | 6 + .../core/downloads/download_ui_adapter.h | 5 +- .../offline_pages/core/model/delete_page_task.cc | 1 + .../offline_pages/core/model/get_pages_task.cc | 1 + .../core/model/mark_page_accessed_task.cc | 1 + .../core/model/offline_page_model_taskified.cc | 1 + .../core/offline_page_metadata_store_test_util.cc | 1 + .../core/prefetch/prefetch_service_test_taco.h | 2 +- .../prefetch/store/prefetch_downloader_quota.cc | 1 + .../prefetch/tasks/metrics_finalization_task.cc | 1 + .../prefetch/tasks/stale_entry_finalizer_task.cc | 1 + .../components/offline_pages/task/task_queue.cc | 1 + chromium/components/omnibox/browser/BUILD.gn | 4 + chromium/components/onc/docs/onc_spec.md | 26 +- chromium/components/onc/onc_constants.cc | 1 + chromium/components/onc/onc_constants.h | 1 + chromium/components/open_from_clipboard/BUILD.gn | 10 - .../clipboard_recent_content.cc | 14 +- .../open_from_clipboard/clipboard_recent_content.h | 24 + .../clipboard_recent_content_features.cc | 11 - .../clipboard_recent_content_features.h | 15 - .../clipboard_recent_content_generic.cc | 36 + .../clipboard_recent_content_generic.h | 4 + .../clipboard_recent_content_generic_unittest.cc | 4 - .../clipboard_recent_content_impl_ios.h | 25 + .../clipboard_recent_content_impl_ios.mm | 389 ++++-- .../clipboard_recent_content_ios.h | 4 + .../clipboard_recent_content_ios.mm | 77 +- .../clipboard_recent_content_ios_unittest.mm | 21 +- .../fake_clipboard_recent_content.cc | 36 + .../fake_clipboard_recent_content.h | 4 + .../components/optimization_guide/hint_cache.cc | 16 +- .../components/optimization_guide/hint_cache.h | 9 +- .../optimization_guide/hints_component_util.cc | 13 +- .../optimization_guide/hints_component_util.h | 15 +- .../hints_component_util_unittest.cc | 74 +- .../optimization_guide/hints_processing_util.cc | 2 + .../optimization_guide/optimization_filter.cc | 11 +- .../optimization_guide/optimization_filter.h | 5 +- .../optimization_filter_unittest.cc | 32 +- .../optimization_guide_decider.h | 15 +- .../optimization_guide_features.cc | 10 +- .../optimization_guide/optimization_guide_store.h | 6 +- .../optimization_guide/proto/hints.proto | 20 +- .../proto/performance_hints_metadata.proto | 7 + .../test_optimization_guide_decider.cc | 14 +- .../test_optimization_guide_decider.h | 8 +- .../url_pattern_with_wildcards.h | 1 - .../components/os_crypt/key_storage_util_linux.cc | 1 + chromium/components/os_crypt/os_crypt_win.cc | 1 + chromium/components/page_info/BUILD.gn | 1 + chromium/components/page_info/android/BUILD.gn | 7 + .../android/java/res/layout/page_info.xml | 9 + .../android/java/res/layout/page_info_subpage.xml | 67 ++ .../page_info/PageInfoConnectionController.java | 50 + .../components/page_info/PageInfoController.java | 178 ++- .../page_info/PageInfoControllerDelegate.java | 36 +- .../page_info/PageInfoCookiesController.java | 53 + .../page_info/PageInfoPermissionsController.java | 51 + .../components/page_info/PageInfoRowView.java | 1 + .../components/page_info/PageInfoSubpage.java | 48 + .../page_info/PageInfoSubpageController.java | 28 + .../components/page_info/PageInfoView.java | 25 +- .../components/page_info/PageInfoViewV2.java | 5 + .../android/page_info_controller_android.cc | 6 +- .../page_info/android/page_info_feature_list.cc | 3 +- chromium/components/page_info/page_info.cc | 112 +- chromium/components/page_info/page_info.h | 13 +- chromium/components/page_info/page_info_ui.cc | 13 +- chromium/components/page_info_strings.grdp | 11 +- .../IDS_PAGE_INFO_DELETE_HID_DEVICE.png.sha1 | 1 + ...S_PAGE_INFO_HID_DEVICE_SECONDARY_LABEL.png.sha1 | 1 + ...DS_PAGE_INFO_TYPE_CAMERA_PAN_TILT_ZOOM.png.sha1 | 2 +- .../IDS_PAGE_INFO_TYPE_HID.png.sha1 | 1 + .../components/page_load_metrics/browser/BUILD.gn | 3 + .../browser/metrics_web_contents_observer.cc | 112 +- .../browser/metrics_web_contents_observer.h | 19 +- ...ack_forward_cache_page_load_metrics_observer.cc | 60 + ...back_forward_cache_page_load_metrics_observer.h | 38 + .../observers/core_page_load_metrics_observer.cc | 253 +++- .../observers/core_page_load_metrics_observer.h | 44 +- .../core_page_load_metrics_observer_unittest.cc | 448 +++---- .../observers/largest_contentful_paint_handler.cc | 117 +- .../observers/largest_contentful_paint_handler.h | 83 +- .../browser/observers/use_counter/ukm_features.cc | 4 + .../use_counter_page_load_metrics_observer.h | 1 - .../browser/page_load_metrics_embedder_base.cc | 3 + .../browser/page_load_metrics_observer.cc | 29 - .../browser/page_load_metrics_observer.h | 33 +- .../browser/page_load_metrics_observer_delegate.cc | 16 + .../browser/page_load_metrics_observer_delegate.h | 27 + .../browser/page_load_metrics_test_waiter.cc | 38 +- .../browser/page_load_metrics_test_waiter.h | 7 + .../browser/page_load_metrics_update_dispatcher.cc | 71 +- .../browser/page_load_metrics_update_dispatcher.h | 12 + .../browser/page_load_metrics_util.cc | 13 + .../browser/page_load_metrics_util.h | 5 + .../page_load_metrics/browser/page_load_tracker.cc | 158 ++- .../page_load_metrics/browser/page_load_tracker.h | 29 +- .../page_load_metrics/common/page_end_reason.h | 8 +- .../common/page_load_metrics.mojom | 88 +- .../page_load_metrics/common/page_load_timing.cc | 26 +- .../renderer/metrics_render_frame_observer.cc | 182 ++- .../renderer/metrics_render_frame_observer.h | 16 +- .../metrics_render_frame_observer_unittest.cc | 13 +- .../renderer/page_timing_metrics_sender.cc | 4 +- chromium/components/paint_preview/browser/BUILD.gn | 1 + .../paint_preview/browser/compositor_utils.cc | 8 +- .../paint_preview/browser/directory_key.cc | 4 + .../paint_preview/browser/directory_key.h | 2 + .../paint_preview/browser/file_manager.cc | 72 +- .../paint_preview/browser/file_manager.h | 12 + .../paint_preview/browser/file_manager_unittest.cc | 57 + .../browser/paint_preview_base_service.cc | 4 +- .../paint_preview/browser/paint_preview_client.cc | 19 +- .../browser/paint_preview_client_unittest.cc | 6 + .../paint_preview/browser/service_sandbox_type.h | 29 + .../common/mojom/paint_preview_recorder.mojom | 3 + .../paint_preview/common/paint_preview_tracker.cc | 12 +- .../paint_preview/common/paint_preview_tracker.h | 5 + .../common/paint_preview_tracker_unittest.cc | 27 + .../paint_preview/common/proto/paint_preview.proto | 6 +- .../components/paint_preview/features/features.h | 2 +- .../paint_preview/player/android/BUILD.gn | 6 + .../paintpreview/player/OverscrollHandler.java | 33 + .../paintpreview/player/PaintPreviewFrame.java | 27 +- .../player/PlayerCompositorDelegateImpl.java | 27 +- .../paintpreview/player/PlayerManager.java | 70 +- .../player/PlayerSwipeRefreshHandler.java | 82 ++ .../player/frame/PlayerFrameCoordinator.java | 20 +- .../player/frame/PlayerFrameGestureDetector.java | 9 +- .../player/frame/PlayerFrameMediator.java | 317 ++++- .../player/frame/PlayerFrameProperties.java | 6 +- .../paintpreview/player/frame/PlayerFrameView.java | 61 +- .../player/frame/PlayerFrameViewBinder.java | 2 + .../player/frame/PlayerFrameViewDelegate.java | 25 + .../player/PaintPreviewPlayerTest.java | 131 ++- .../paintpreview/player/PaintPreviewTestRule.java | 30 +- .../paintpreview/player/PlayerManagerTest.java | 24 +- .../PaintPreviewCustomFlingingShadowScroller.java | 4 +- .../player/frame/PlayerFrameMediatorTest.java | 586 ++++++++- .../android/player_compositor_delegate_android.cc | 73 +- .../android/player_compositor_delegate_android.h | 7 +- .../player_compositor_delegate_android_unittest.cc | 20 +- .../player/player_compositor_delegate.cc | 23 +- .../player/player_compositor_delegate.h | 7 +- .../player/player_compositor_delegate_unittest.cc | 62 +- .../renderer/paint_preview_recorder_browsertest.cc | 18 +- .../renderer/paint_preview_recorder_impl.cc | 27 +- .../renderer/paint_preview_recorder_utils.cc | 2 +- .../content/browser/bad_message.cc | 2 +- .../content/browser/content_credential_manager.cc | 2 +- .../browser/content_password_manager_driver.cc | 15 +- .../browser/content_password_manager_driver.h | 3 +- .../content_password_manager_driver_factory.cc | 3 +- .../content_password_manager_driver_unittest.cc | 18 +- ...password_manager_log_router_factory_unittest.cc | 2 - .../password_requirements_service_factory.cc | 3 +- .../password_manager/core/browser/BUILD.gn | 18 +- .../affiliated_match_helper_unittest.cc | 24 +- .../android_affiliation/affiliation_backend.cc | 9 +- .../affiliation_backend_unittest.cc | 20 +- .../android_affiliation/affiliation_database.cc | 10 +- .../affiliation_database_unittest.cc | 8 +- .../affiliation_fetch_throttler_unittest.cc | 15 +- .../affiliation_fetcher_unittest.cc | 14 +- .../affiliation_service_unittest.cc | 17 +- .../android_affiliation/affiliation_utils.cc | 7 +- .../android_affiliation/affiliation_utils.h | 2 +- .../android_affiliation/facet_manager_unittest.cc | 6 +- .../android_affiliation/fake_affiliation_api.cc | 3 +- .../fake_affiliation_fetcher.cc | 3 +- .../mock_affiliated_match_helper.cc | 2 +- .../mock_affiliation_consumer.cc | 3 +- .../browser_save_password_progress_logger.cc | 2 +- .../core/browser/bulk_leak_check_service.cc | 12 + .../core/browser/bulk_leak_check_service.h | 57 +- .../browser/bulk_leak_check_service_interface.cc | 13 + .../browser/bulk_leak_check_service_interface.h | 90 ++ .../browser/bulk_leak_check_service_unittest.cc | 26 +- .../compromised_credentials_observer_unittest.cc | 4 +- .../compromised_credentials_table_unittest.cc | 6 +- .../core/browser/credential_manager_impl.cc | 56 +- .../core/browser/credential_manager_impl.h | 4 +- .../browser/credential_manager_impl_unittest.cc | 147 ++- .../core/browser/credential_manager_logger.cc | 21 +- .../core/browser/credential_manager_logger.h | 8 +- .../browser/credential_manager_logger_unittest.cc | 12 +- ...ntial_manager_password_form_manager_unittest.cc | 6 +- .../credential_manager_pending_request_task.cc | 4 +- .../credential_manager_pending_request_task.h | 7 +- .../password_manager/core/browser/export/OWNERS | 4 + .../core/browser/export/csv_writer_unittest.cc | 2 +- .../core/browser/export/password_csv_writer.cc | 4 +- .../browser/export/password_csv_writer_unittest.cc | 8 +- .../browser/export/password_manager_exporter.cc | 14 +- .../browser/export/password_manager_exporter.h | 3 +- .../export/password_manager_exporter_unittest.cc | 10 +- .../core/browser/form_fetcher_impl.cc | 14 +- .../core/browser/form_fetcher_impl.h | 12 +- .../core/browser/form_fetcher_impl_unittest.cc | 120 +- .../core/browser/form_parsing/form_parser.cc | 35 +- .../core/browser/form_parsing/form_parser.h | 2 +- .../password_manager/core/browser/form_saver.h | 6 +- .../core/browser/form_saver_impl_unittest.cc | 12 +- .../password_requirements_spec_fetcher_impl.cc | 9 +- .../password_manager/core/browser/hsts_query.cc | 6 +- .../password_manager/core/browser/hsts_query.h | 8 +- .../core/browser/hsts_query_unittest.cc | 4 +- .../core/browser/http_auth_manager_impl.cc | 6 +- .../core/browser/http_auth_manager_unittest.cc | 58 +- .../core/browser/http_credentials_cleaner.cc | 4 +- .../browser/http_credentials_cleaner_unittest.cc | 6 +- .../core/browser/http_password_store_migrator.cc | 19 +- .../core/browser/http_password_store_migrator.h | 6 +- .../http_password_store_migrator_unittest.cc | 57 +- .../core/browser/import/csv_password.cc | 2 +- .../import/csv_password_sequence_unittest.cc | 4 +- .../core/browser/import/csv_password_unittest.cc | 8 +- .../browser/import/password_importer_unittest.cc | 11 +- .../leak_detection_check_factory_impl_unittest.cc | 3 + .../core/browser/leak_detection_delegate.cc | 44 +- .../core/browser/leak_detection_delegate_helper.cc | 2 +- .../leak_detection_delegate_helper_unittest.cc | 12 +- .../browser/leak_detection_delegate_unittest.cc | 65 +- .../core/browser/leak_detection_dialog_utils.cc | 35 +- .../core/browser/leak_detection_dialog_utils.h | 3 + .../leak_detection_dialog_utils_unittest.cc | 66 ++ .../core/browser/login_database.cc | 222 ++-- .../password_manager/core/browser/login_database.h | 38 +- .../core/browser/login_database_ios.cc | 63 +- .../core/browser/login_database_ios_unittest.cc | 10 +- .../core/browser/login_database_unittest.cc | 208 ++-- .../core/browser/mock_bulk_leak_check_service.cc | 14 + .../core/browser/mock_bulk_leak_check_service.h | 38 + .../core/browser/mock_password_feature_manager.cc | 2 +- .../core/browser/mock_password_feature_manager.h | 2 +- .../browser/mock_password_form_manager_for_ui.h | 3 +- .../core/browser/multi_store_form_fetcher.cc | 55 +- .../core/browser/multi_store_form_fetcher.h | 10 + .../browser/multi_store_form_fetcher_unittest.cc | 41 +- .../browser/multi_store_password_save_manager.cc | 55 +- .../browser/multi_store_password_save_manager.h | 3 +- .../multi_store_password_save_manager_unittest.cc | 83 +- .../core/browser/origin_credential_store.cc | 5 +- .../core/browser/password_autofill_manager.cc | 67 +- .../core/browser/password_autofill_manager.h | 5 +- .../browser/password_autofill_manager_unittest.cc | 571 +++++---- .../browser/password_bubble_experiment_unittest.cc | 8 + .../core/browser/password_feature_manager.h | 11 +- .../core/browser/password_feature_manager_impl.cc | 6 +- .../core/browser/password_feature_manager_impl.h | 2 +- .../core/browser/password_form_filling.cc | 19 +- .../core/browser/password_form_filling_unittest.cc | 90 +- .../core/browser/password_form_manager.cc | 81 +- .../core/browser/password_form_manager.h | 19 +- .../core/browser/password_form_manager_for_ui.h | 7 +- .../core/browser/password_form_manager_unittest.cc | 131 ++- .../core/browser/password_form_metrics_recorder.cc | 104 +- .../core/browser/password_form_metrics_recorder.h | 114 +- .../password_form_metrics_recorder_unittest.cc | 486 ++++++-- .../browser/password_generation_frame_helper.cc | 6 +- .../password_generation_frame_helper_unittest.cc | 20 +- .../core/browser/password_generation_manager.cc | 12 +- .../password_generation_manager_unittest.cc | 16 +- .../core/browser/password_list_sorter.cc | 6 +- .../core/browser/password_list_sorter.h | 8 +- .../core/browser/password_list_sorter_unittest.cc | 18 +- .../core/browser/password_manager.cc | 141 ++- .../core/browser/password_manager.h | 29 +- .../core/browser/password_manager_client.cc | 17 +- .../core/browser/password_manager_client.h | 38 +- .../core/browser/password_manager_client_helper.cc | 19 +- .../core/browser/password_manager_client_helper.h | 6 + .../password_manager_client_helper_unittest.cc | 36 +- .../core/browser/password_manager_driver.h | 7 +- .../core/browser/password_manager_features_util.cc | 73 +- .../core/browser/password_manager_features_util.h | 33 +- .../password_manager_features_util_unittest.cc | 131 ++- .../browser/password_manager_metrics_recorder.cc | 10 +- .../browser/password_manager_metrics_recorder.h | 13 +- .../password_manager_metrics_recorder_unittest.cc | 4 +- .../core/browser/password_manager_metrics_util.cc | 48 +- .../core/browser/password_manager_metrics_util.h | 52 +- .../password_manager_onboarding_unittest.cc | 15 +- .../core/browser/password_manager_test_utils.cc | 14 +- .../core/browser/password_manager_unittest.cc | 711 +++++------ .../core/browser/password_manager_util.cc | 39 +- .../core/browser/password_manager_util_unittest.cc | 40 +- .../browser/password_reuse_detection_manager.cc | 2 +- .../browser/password_reuse_detector_consumer.cc | 4 +- .../core/browser/password_save_manager.h | 9 +- .../core/browser/password_save_manager_impl.cc | 8 +- .../core/browser/password_save_manager_impl.h | 7 +- .../browser/password_save_manager_impl_unittest.cc | 112 +- .../core/browser/password_store.cc | 80 +- .../password_manager/core/browser/password_store.h | 41 +- .../core/browser/password_store_change.h | 2 +- .../core/browser/password_store_consumer.cc | 8 + .../core/browser/password_store_consumer.h | 12 + .../core/browser/password_store_default.cc | 16 +- .../browser/password_store_default_unittest.cc | 7 +- .../core/browser/password_store_signin_notifier.cc | 4 +- .../core/browser/password_store_sync.h | 19 + .../core/browser/password_store_unittest.cc | 14 +- .../core/browser/password_sync_util.cc | 4 +- .../core/browser/password_ui_utils.cc | 8 +- .../core/browser/password_ui_utils.h | 4 +- .../core/browser/password_ui_utils_unittest.cc | 7 +- .../core/browser/psl_matching_helper.cc | 19 +- .../core/browser/psl_matching_helper_unittest.cc | 20 +- .../core/browser/sql_table_builder_unittest.cc | 1 + .../core/browser/statistics_table.cc | 2 +- .../core/browser/statistics_table_unittest.cc | 5 +- .../core/browser/store_metrics_reporter.cc | 200 +++- .../core/browser/store_metrics_reporter.h | 16 +- .../browser/store_metrics_reporter_unittest.cc | 136 ++- .../core/browser/stub_password_manager_client.cc | 14 +- .../core/browser/stub_password_manager_client.h | 7 +- .../browser/sync/password_model_type_controller.cc | 39 +- .../browser/sync/password_model_type_controller.h | 5 + .../core/browser/sync/password_sync_bridge.cc | 13 +- .../browser/sync/password_sync_bridge_unittest.cc | 46 +- .../core/browser/sync_credentials_filter.cc | 2 +- .../browser/sync_credentials_filter_unittest.cc | 10 +- .../core/browser/sync_username_test_base.cc | 2 +- .../core/browser/sync_username_test_base.h | 6 +- .../core/browser/test_password_store.cc | 32 +- .../browser/ui/bulk_leak_check_service_adapter.cc | 10 +- .../browser/ui/bulk_leak_check_service_adapter.h | 8 +- .../browser/ui/compromised_credentials_manager.cc | 269 +++++ .../browser/ui/compromised_credentials_manager.h | 190 +++ .../ui/compromised_credentials_manager_unittest.cc | 525 +++++++++ .../browser/ui/compromised_credentials_provider.cc | 151 --- .../browser/ui/compromised_credentials_provider.h | 105 -- .../compromised_credentials_provider_unittest.cc | 390 ------ .../core/browser/ui/credential_utils.h | 25 - .../browser/ui/post_save_compromised_helper.cc | 66 ++ .../core/browser/ui/post_save_compromised_helper.h | 80 ++ .../ui/post_save_compromised_helper_unittest.cc | 169 +++ .../core/browser/votes_uploader.cc | 4 +- .../password_manager/core/browser/votes_uploader.h | 7 +- .../core/browser/votes_uploader_unittest.cc | 2 +- .../core/common/credential_manager_types.cc | 13 +- .../core/common/credential_manager_types.h | 2 +- .../common/credential_manager_types_unittest.cc | 10 +- .../core/common/password_manager_features.cc | 22 +- .../core/common/password_manager_features.h | 8 + .../core/common/password_manager_pref_names.cc | 5 + .../core/common/password_manager_pref_names.h | 5 + .../core/common/password_manager_ui.h | 9 + .../ios/account_select_fill_data.cc | 2 +- .../ios/account_select_fill_data_unittest.cc | 2 +- .../password_manager/ios/js_password_manager.h | 35 +- .../password_manager/ios/js_password_manager.mm | 142 +-- .../password_manager/ios/password_form_helper.h | 5 +- .../password_manager/ios/password_form_helper.mm | 64 +- .../ios/password_form_helper_unittest.mm | 28 +- .../ios/resources/password_controller.js | 34 +- .../password_manager/ios/test_helpers.cc | 4 +- chromium/components/password_manager_strings.grdp | 7 +- ...ASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE.png.sha1 | 2 +- ...GER_OPT_INTO_ACCOUNT_STORED_GENERATION.png.sha1 | 1 - ...SSWORD_MANAGER_RE_SIGNIN_ACCOUNT_STORE.png.sha1 | 1 + chromium/components/payments/content/BUILD.gn | 7 + chromium/components/payments/content/DEPS | 3 + .../components/payments/content/android/BUILD.gn | 45 +- chromium/components/payments/content/android/DEPS | 1 + .../payments/content/android/byte_buffer_helper.h | 2 +- .../org/chromium/components/payments/Address.java | 19 +- .../payments/IPaymentDetailsUpdateService.aidl | 44 + .../IPaymentDetailsUpdateServiceCallback.aidl | 29 + .../components/payments/JniPaymentApp.java | 260 ++++ .../components/payments/JourneyLogger.java | 253 ++++ .../components/payments/MojoStructCollection.java | 30 + .../src/org/chromium/components/payments/OWNERS | 3 + .../payments/PackageManagerDelegate.java | 140 +++ .../chromium/components/payments/PaymentApp.java | 67 +- .../payments/PaymentDetailsUpdateService.java | 76 ++ .../PaymentDetailsUpdateServiceHelper.java | 245 ++++ .../components/payments/PaymentFeatureList.java | 81 ++ .../components/payments/PaymentHandlerHost.java | 85 +- .../components/payments/PaymentRequestSpec.java | 79 ++ .../PaymentRequestUpdateEventListener.java | 68 ++ .../payments/intent/IsReadyToPayServiceHelper.java | 14 +- .../payments/intent/WebPaymentIntentHelper.java | 131 +-- .../intent/WebPaymentIntentHelperType.java | 251 +++- .../payments/payment_details_update_service.aidl | 6 + .../android/java_templates/ErrorStrings.java.tmpl | 3 + .../payments/content/android/jni_payment_app.cc | 254 ++++ .../payments/content/android/jni_payment_app.h | 101 ++ .../content/android/payment_feature_list.cc | 69 ++ .../content/android/payment_feature_list.h | 21 + .../content/android/payment_handler_host.cc | 68 +- .../content/android/payment_handler_host.h | 64 +- .../content/android/payment_request_spec.cc | 90 ++ .../content/android/payment_request_spec.h | 66 ++ .../payment_request_update_event_listener.cc | 50 + .../payment_request_update_event_listener.h | 37 + .../payments/content/autofill_payment_app.cc | 244 ++++ .../payments/content/autofill_payment_app.h | 120 ++ .../content/autofill_payment_app_factory.cc | 2 +- .../content/autofill_payment_app_unittest.cc | 362 ++++++ .../content/content_payment_request_delegate.h | 11 +- .../content/installable_payment_app_crawler.cc | 100 +- .../content/installable_payment_app_crawler.h | 24 +- .../payments/content/manifest_verifier.cc | 3 +- .../components/payments/content/payment_app.cc | 182 +++ chromium/components/payments/content/payment_app.h | 204 ++++ .../payments/content/payment_app_factory.h | 26 +- .../payments/content/payment_app_service.cc | 6 +- .../payments/content/payment_app_service.h | 3 + .../payments/content/payment_app_unittest.cc | 24 +- .../payments/content/payment_handler_host.cc | 8 +- .../payments/content/payment_handler_host.h | 3 +- .../components/payments/content/payment_request.cc | 67 +- .../components/payments/content/payment_request.h | 18 +- .../payments/content/payment_request_spec.cc | 2 +- .../payments/content/payment_request_state.cc | 30 +- .../payments/content/payment_request_state.h | 33 +- .../content/payment_request_state_unittest.cc | 5 +- .../payments/content/payment_response_helper.h | 2 +- .../content/payment_response_helper_unittest.cc | 2 +- .../payments/content/service_worker_payment_app.cc | 152 ++- .../payments/content/service_worker_payment_app.h | 69 +- .../content/service_worker_payment_app_factory.cc | 49 +- .../content/service_worker_payment_app_finder.cc | 172 ++- .../content/service_worker_payment_app_finder.h | 27 +- .../content/service_worker_payment_app_unittest.cc | 26 +- .../test_content_payment_request_delegate.cc | 8 +- .../test_content_payment_request_delegate.h | 3 +- chromium/components/payments/core/BUILD.gn | 12 +- .../payments/core/autofill_payment_app.cc | 240 ---- .../payments/core/autofill_payment_app.h | 119 -- .../payments/core/autofill_payment_app_unittest.cc | 362 ------ .../payments/core/can_make_payment_query.cc | 5 + .../payments/core/can_make_payment_query.h | 3 + chromium/components/payments/core/error_strings.cc | 4 + chromium/components/payments/core/error_strings.h | 14 + chromium/components/payments/core/features.cc | 7 + chromium/components/payments/core/features.h | 7 + chromium/components/payments/core/journey_logger.h | 10 +- .../payments/core/mock_payment_request_delegate.cc | 11 - .../payments/core/mock_payment_request_delegate.h | 45 - .../payments/core/native_error_strings.cc | 6 - .../payments/core/native_error_strings.h | 10 - chromium/components/payments/core/payment_app.cc | 147 --- chromium/components/payments/core/payment_app.h | 132 --- .../payments/core/payment_request_base_delegate.h | 4 +- .../payments/core/payment_request_delegate.cc | 17 + .../payments/core/payment_request_delegate.h | 10 +- .../payments/core/test_payment_request_delegate.cc | 2 +- .../payments/core/test_payment_request_delegate.h | 2 +- .../pdf/renderer/pdf_accessibility_tree.cc | 1173 ++++++++++-------- .../pdf/renderer/pdf_accessibility_tree.h | 45 +- .../renderer/pdf_accessibility_tree_browsertest.cc | 53 +- .../renderer/pdf_accessibility_tree_unittest.cc | 214 ++++ .../pdf/renderer/pdf_ax_action_target.cc | 2 +- chromium/components/pdf_strings.grdp | 9 + .../IDS_PDF_DOWNLOAD_EDITED.png.sha1 | 1 + .../IDS_PDF_DOWNLOAD_ORIGINAL.png.sha1 | 1 + chromium/components/pdf_strings_grdp/OWNERS | 3 +- chromium/components/performance_manager/BUILD.gn | 13 + chromium/components/performance_manager/DEPS | 1 + chromium/components/performance_manager/README.md | 14 + .../decorators/site_data_recorder.cc | 345 ++++++ .../decorators/site_data_recorder.h | 72 ++ .../decorators/site_data_recorder_unittest.cc | 393 +++++++ .../decorators/v8_per_frame_memory_decorator.cc | 483 ++++++++ .../v8_per_frame_memory_decorator_unittest.cc | 970 +++++++++++++++ .../embedder/performance_manager_registry.h | 13 +- .../components/performance_manager/features.cc | 4 + .../performance_manager/graph/frame_node_impl.cc | 121 +- .../performance_manager/graph/frame_node_impl.h | 60 +- .../graph/frame_node_impl_describer.cc | 3 +- .../graph/frame_node_impl_unittest.cc | 218 +++- .../performance_manager/graph/graph_impl.cc | 44 +- .../performance_manager/graph/graph_impl.h | 32 +- .../graph/graph_impl_unittest.cc | 20 + .../graph/graph_registered_unittest.cc | 55 + .../performance_manager/graph/node_attached_data.h | 2 +- .../graph/node_attached_data_impl.h | 2 +- .../performance_manager/graph/node_base.cc | 4 - .../performance_manager/graph/node_base.h | 7 +- .../performance_manager/graph/page_node.cc | 22 + .../performance_manager/graph/page_node_impl.cc | 65 + .../performance_manager/graph/page_node_impl.h | 22 + .../graph/page_node_impl_describer.cc | 4 + .../graph/page_node_impl_unittest.cc | 4 + .../graph/policies/process_priority_policy.cc | 5 +- .../tab_loading_frame_navigation_policy.cc | 76 +- ...tab_loading_frame_navigation_policy_unittest.cc | 179 ++- .../tab_loading_frame_navigation_scheduler.cc | 17 +- ...ading_frame_navigation_scheduler_browsertest.cc | 118 +- .../components/performance_manager/owned_objects.h | 108 ++ .../performance_manager/owned_objects_unittest.cc | 73 ++ .../performance_manager/performance_manager.cc | 41 +- .../performance_manager_browsertest.cc | 37 +- .../performance_manager_impl.cc | 6 +- .../performance_manager/performance_manager_impl.h | 3 +- .../performance_manager_impl_unittest.cc | 21 +- .../performance_manager_registry.cc | 10 + .../performance_manager_registry_impl.cc | 77 +- .../performance_manager_registry_impl.h | 29 + .../performance_manager_registry_impl_unittest.cc | 128 +- .../performance_manager_tab_helper.cc | 185 ++- .../performance_manager_tab_helper.h | 15 +- .../performance_manager_tab_helper_unittest.cc | 91 ++ .../performance_manager_unittest.cc | 6 +- .../site_data/non_recording_site_data_cache.cc | 6 +- .../site_data/non_recording_site_data_cache.h | 3 +- .../persistence/site_data/site_data_cache.h | 5 +- .../site_data/site_data_cache_factory.cc | 80 +- .../site_data/site_data_cache_factory.h | 60 +- .../site_data/site_data_cache_factory_unittest.cc | 54 +- .../persistence/site_data/site_data_cache_impl.cc | 7 +- .../persistence/site_data/site_data_cache_impl.h | 3 +- .../persistence/site_data/site_data_impl.h | 2 + .../persistence/site_data/site_data_writer.h | 1 + .../page_load_tracker_decorator_helper.h | 2 +- .../decorators/v8_per_frame_memory_decorator.h | 198 ++++ .../performance_manager/public/features.h | 5 + .../performance_manager/public/graph/frame_node.h | 30 +- .../performance_manager/public/graph/graph.h | 4 +- .../public/graph/node_attached_data.h | 2 +- .../performance_manager/public/graph/page_node.h | 42 + .../policies/tab_loading_frame_navigation_policy.h | 29 +- .../tab_loading_frame_navigation_scheduler.h | 1 + .../public/performance_manager.h | 67 +- .../performance_manager_main_thread_observer.h | 18 + .../public/performance_manager_owned.h | 51 + .../public/performance_manager_registered.h | 83 ++ .../public/web_contents_proxy.h | 6 + .../performance_manager/registered_objects.h | 87 ++ .../registered_objects_unittest.cc | 94 ++ .../render_process_host_proxy_browsertest.cc | 13 +- .../service_worker_context_adapter.cc | 15 + .../service_worker_context_adapter.h | 6 +- .../test_support/graph_test_harness.h | 21 +- .../performance_manager_browsertest_harness.cc | 15 +- .../performance_manager_browsertest_harness.h | 2 +- .../performance_manager_test_harness.cc | 8 +- .../performance_manager_test_harness.h | 19 +- .../test_support/test_harness_helper.h | 31 +- .../performance_manager/web_contents_proxy.cc | 7 + .../performance_manager/web_contents_proxy_impl.h | 6 + .../web_contents_proxy_unittest.cc | 13 +- .../performance_manager/worker_watcher.cc | 140 ++- .../performance_manager/worker_watcher.h | 22 +- .../performance_manager/worker_watcher_unittest.cc | 84 +- chromium/components/permissions/BUILD.gn | 4 + chromium/components/permissions/android/BUILD.gn | 3 + .../permissions/PermissionDialogController.java | 10 +- .../components/permissions/chooser_context_base.cc | 2 +- .../components/permissions/chooser_context_base.h | 4 +- chromium/components/permissions/contexts/OWNERS | 6 + .../contexts/geolocation_permission_context.cc | 5 + .../contexts/geolocation_permission_context.h | 5 + .../geolocation_permission_context_unittest.cc | 57 +- .../contexts/webxr_permission_context.cc | 136 +++ .../contexts/webxr_permission_context.h | 59 + .../notification_permission_ui_selector.h | 2 + .../permissions/permission_context_base.cc | 15 + .../components/permissions/permission_request.cc | 6 + .../components/permissions/permission_request.h | 5 + .../permissions/permission_request_impl.cc | 42 +- .../permissions/permission_request_impl.h | 3 + .../permissions/permission_request_manager.cc | 45 +- .../permissions/permission_request_manager.h | 8 + .../components/permissions/permission_result.h | 4 + .../components/permissions/permission_uma_util.cc | 83 +- .../components/permissions/permission_uma_util.h | 9 + .../components/permissions/permissions_client.cc | 6 + .../components/permissions/permissions_client.h | 15 + .../permissions/quota_permission_context_impl.cc | 9 +- chromium/components/permissions_strings.grdp | 34 +- .../IDS_AR_PERMISSION_CHIP.png.sha1 | 1 + .../IDS_CLIPBOARD_PERMISSION_CHIP.png.sha1 | 1 + .../IDS_GEOLOCATION_PERMISSION_CHIP.png.sha1 | 1 + ...DIA_CAPTURE_AUDIO_ONLY_PERMISSION_CHIP.png.sha1 | 1 + ...PAN_TILT_ZOOM_ONLY_PERMISSION_FRAGMENT.png.sha1 | 1 - ...MERA_PAN_TILT_ZOOM_PERMISSION_FRAGMENT.png.sha1 | 1 + ...APTURE_VIDEO_AND_AUDIO_PERMISSION_CHIP.png.sha1 | 1 + ...DIA_CAPTURE_VIDEO_ONLY_PERMISSION_CHIP.png.sha1 | 1 + .../IDS_MIDI_SYSEX_PERMISSION_CHIP.png.sha1 | 1 + .../IDS_NOTIFICATION_PERMISSIONS_CHIP.png.sha1 | 1 + .../IDS_VR_PERMISSION_CHIP.png.sha1 | 1 + chromium/components/plugins/renderer/BUILD.gn | 2 +- chromium/components/plugins/renderer/DEPS | 1 + .../components/plugins/renderer/webview_plugin.cc | 25 +- .../components/plugins/renderer/webview_plugin.h | 24 +- chromium/components/policy/android/BUILD.gn | 4 + chromium/components/policy/core/browser/BUILD.gn | 1 + chromium/components/policy/core/common/BUILD.gn | 23 +- chromium/components/policy/proto/BUILD.gn | 14 +- chromium/components/policy_strings.grdp | 18 +- .../IDS_COPY_POLICIES_JSON.png.sha1 | 1 + .../IDS_POLICY_COPY_VALUE.png.sha1 | 1 + .../IDS_POLICY_INVALID.png.sha1 | 1 + .../IDS_POLICY_LABEL_FUTURE.png.sha1 | 1 + chromium/components/prefs/BUILD.gn | 8 + chromium/components/prefs/android/BUILD.gn | 19 + .../org/chromium/components/prefs/PrefService.java | 105 ++ .../prefs/android/pref_service_android.cc | 90 ++ .../prefs/android/pref_service_android.h | 53 + .../components/prefs/command_line_pref_store.cc | 1 + chromium/components/prefs/json_pref_store.cc | 1 + chromium/components/prefs/pref_member.cc | 8 +- chromium/components/prefs/pref_member.h | 2 +- chromium/components/prefs/pref_service.cc | 26 +- chromium/components/prefs/pref_service.h | 18 + chromium/components/prerender/OWNERS | 9 + chromium/components/prerender/common/BUILD.gn | 29 + chromium/components/prerender/common/DEPS | 3 + chromium/components/prerender/common/OWNERS | 5 + .../prerender/common/prerender_canceler.mojom | 15 + .../prerender/common/prerender_final_status.cc | 90 ++ .../prerender/common/prerender_final_status.h | 91 ++ .../prerender/common/prerender_messages.h | 35 + .../prerender/common/prerender_origin.cc | 44 + .../components/prerender/common/prerender_origin.h | 43 + .../prerender/common/prerender_types.mojom | 16 + chromium/components/previews/DEPS | 4 +- chromium/components/previews/content/BUILD.gn | 4 +- chromium/components/previews/content/DEPS | 2 +- .../previews/content/previews_decider_impl.cc | 80 +- .../previews/content/previews_decider_impl.h | 68 +- .../content/previews_decider_impl_unittest.cc | 461 ++++---- .../content/previews_optimization_guide.cc | 5 +- .../previews/content/previews_optimization_guide.h | 5 +- .../previews_optimization_guide_unittest.cc | 8 +- .../previews/content/previews_ui_service.cc | 38 +- .../previews/content/previews_ui_service.h | 50 +- .../content/previews_ui_service_unittest.cc | 121 +- .../previews/content/previews_user_data.cc | 15 +- .../previews/content/previews_user_data.h | 57 +- .../content/previews_user_data_unittest.cc | 12 +- chromium/components/previews/core/BUILD.gn | 10 +- chromium/components/previews/core/DEPS | 2 +- .../previews/core/previews_black_list.cc | 131 --- .../components/previews/core/previews_black_list.h | 163 --- .../previews/core/previews_black_list_unittest.cc | 256 ---- .../previews/core/previews_block_list.cc | 131 +++ .../components/previews/core/previews_block_list.h | 163 +++ .../previews/core/previews_block_list_unittest.cc | 260 ++++ .../previews/core/previews_experiments.cc | 30 +- .../previews/core/previews_experiments.h | 28 +- .../previews/core/previews_experiments_unittest.cc | 33 +- .../components/previews/core/previews_features.cc | 4 - .../components/previews/core/previews_logger.cc | 76 +- .../components/previews/core/previews_logger.h | 32 +- .../previews/core/previews_logger_observer.h | 18 +- .../previews/core/previews_logger_unittest.cc | 221 ++-- .../components/previews/core/previews_switches.cc | 9 +- .../components/previews/core/previews_switches.h | 6 +- chromium/components/printing/browser/BUILD.gn | 1 + chromium/components/printing/browser/OWNERS | 4 + .../printing/browser/print_composite_client.cc | 165 +-- .../printing/browser/print_composite_client.h | 61 +- .../printing/browser/print_manager_utils.cc | 5 +- .../printing/browser/service_sandbox_type.h | 28 + chromium/components/printing/common/BUILD.gn | 1 + chromium/components/printing/common/print.mojom | 44 +- .../components/printing/common/print_messages.cc | 10 +- .../components/printing/common/print_messages.h | 79 +- .../printing/renderer/print_render_frame_helper.cc | 215 ++-- .../printing/renderer/print_render_frame_helper.h | 34 +- .../renderer/print_render_frame_helper_mac.mm | 6 +- chromium/components/printing/test/BUILD.gn | 1 + .../proxy_config/pref_proxy_config_tracker_impl.cc | 1 + chromium/components/query_tiles/BUILD.gn | 1 + .../components/query_tiles/TestTileProvider.java | 8 + .../query_tiles/android/tile_provider_bridge.cc | 1 + chromium/components/query_tiles/internal/BUILD.gn | 6 + .../query_tiles/internal/black_hole_log_sink.cc | 15 + .../query_tiles/internal/black_hole_log_sink.h | 30 + .../internal/init_aware_tile_service.cc | 16 +- .../query_tiles/internal/init_aware_tile_service.h | 1 + .../internal/init_aware_tile_service_unittest.cc | 1 + .../components/query_tiles/internal/log_sink.h | 27 + .../components/query_tiles/internal/log_source.h | 39 + .../components/query_tiles/internal/logger_impl.cc | 101 ++ .../components/query_tiles/internal/logger_impl.h | 40 + chromium/components/query_tiles/internal/stats.cc | 45 +- chromium/components/query_tiles/internal/stats.h | 34 + .../query_tiles/internal/stats_unittest.cc | 51 +- .../components/query_tiles/internal/tile_config.cc | 6 +- .../components/query_tiles/internal/tile_config.h | 8 +- .../query_tiles/internal/tile_config_unittest.cc | 32 +- .../query_tiles/internal/tile_fetcher.cc | 13 + .../components/query_tiles/internal/tile_fetcher.h | 3 + .../query_tiles/internal/tile_iterator.h | 1 - .../query_tiles/internal/tile_manager.cc | 46 +- .../components/query_tiles/internal/tile_manager.h | 3 + .../query_tiles/internal/tile_manager_unittest.cc | 71 +- .../query_tiles/internal/tile_service_impl.cc | 11 +- .../query_tiles/internal/tile_service_impl.h | 1 + .../internal/tile_service_impl_unittest.cc | 52 + .../query_tiles/internal/tile_service_scheduler.cc | 38 +- .../query_tiles/internal/tile_service_scheduler.h | 3 + .../internal/tile_service_scheduler_unittest.cc | 87 +- chromium/components/query_tiles/logger.h | 57 + chromium/components/query_tiles/test/BUILD.gn | 10 +- chromium/components/query_tiles/tile_service.h | 4 + .../components/query_tiles/tile_service_prefs.cc | 2 + .../components/query_tiles/tile_service_prefs.h | 4 + chromium/components/rappor/rappor_prefs.cc | 1 + .../remote_cocoa/app_shim/bridged_content_view.mm | 19 +- .../app_shim/browser_native_widget_window_mac.mm | 17 + .../remote_cocoa/app_shim/window_move_loop.mm | 3 +- chromium/components/remote_cocoa/browser/window.mm | 1 + chromium/components/renderer_context_menu/DEPS | 1 + chromium/components/resources/BUILD.gn | 1 + .../android/blocked_content_resource_id.h | 27 + .../components/resources/android/theme_resources.h | 1 + .../android_system_error_page_resources.grdp | 2 +- ...l_and_password_manager_internals_resources.grdp | 8 +- .../resources/components_scaled_resources.grd | 2 + chromium/components/resources/crash_resources.grdp | 4 +- .../resources/default_100_percent/sadplugin.png | Bin 0 -> 238 bytes .../default_100_percent/webview-crash.png | Bin 0 -> 153 bytes .../resources/default_200_percent/sadplugin.png | Bin 0 -> 321 bytes .../default_200_percent/webview-crash.png | Bin 0 -> 187 bytes .../resources/dom_distiller_resources.grdp | 14 +- .../components/resources/flags_ui_resources.grdp | 6 +- .../components/resources/gcm_driver_resources.grdp | 6 +- .../components/resources/net_log_resources.grdp | 4 +- .../components/resources/neterror_resources.grdp | 2 +- .../resources/ntp_tiles_dev_ui_resources.grdp | 9 +- .../resources/offline_pages_resources.grdp | 2 +- .../components/resources/policy_resources.grdp | 8 +- .../components/resources/printing_resources.grdp | 2 +- .../resources/safe_browsing_resources.grdp | 6 +- .../security_interstitials_dev_ui_resources.grdp | 1 - .../security_interstitials_resources.grdp | 18 +- .../components/resources/signin_resources.grdp | 4 +- .../components/resources/translate_resources.grdp | 2 +- .../resources/user_actions_ui_resources.grdp | 6 +- .../components/resources/version_ui_resources.grdp | 6 +- chromium/components/safe_browsing/BUILD.gn | 3 - chromium/components/safe_browsing/DEPS | 1 + .../android/safe_browsing_api_handler_bridge.cc | 9 +- .../safe_browsing/content/base_blocking_page.cc | 4 - .../safe_browsing/content/base_blocking_page.h | 2 +- .../safe_browsing/content/base_ui_manager.cc | 64 +- .../safe_browsing/content/browser/BUILD.gn | 2 +- .../content/browser/browser_url_loader_throttle.cc | 60 +- .../content/browser/browser_url_loader_throttle.h | 6 +- .../content/browser/mojo_safe_browsing_impl.cc | 4 +- .../safe_browsing_url_checker_impl_content.cc | 13 +- .../content/browser/threat_details.cc | 9 +- .../content/browser/threat_details_cache.cc | 11 +- .../content/browser/threat_details_history.cc | 7 +- .../content/common/safe_browsing.mojom | 13 +- .../content/password_protection/BUILD.gn | 3 + .../safe_browsing/content/password_protection/DEPS | 3 +- .../content/password_protection/metrics_util.cc | 1 + .../mock_password_protection_service.h | 17 +- .../password_protection_request.cc | 15 +- .../password_protection_service.cc | 65 +- .../password_protection_service.h | 33 +- .../password_protection_service_unittest.cc | 36 +- .../content/password_protection/visual_utils.cc | 182 +++ .../content/password_protection/visual_utils.h | 10 + .../password_protection/visual_utils_unittest.cc | 259 ++++ .../renderer/websocket_sb_handshake_throttle.cc | 38 +- .../renderer/websocket_sb_handshake_throttle.h | 20 +- .../content/triggers/ad_popup_trigger.cc | 4 +- .../content/triggers/ad_redirect_trigger.cc | 4 +- .../content/triggers/ad_sampler_trigger.cc | 4 +- .../content/triggers/suspicious_site_trigger.cc | 4 +- .../safe_browsing/content/web_ui/BUILD.gn | 1 + .../components/safe_browsing/content/web_ui/DEPS | 1 + .../content/web_ui/resources/safe_browsing.css | 25 +- .../content/web_ui/resources/safe_browsing.html | 28 +- .../content/web_ui/resources/safe_browsing.js | 77 +- .../content/web_ui/safe_browsing_ui.cc | 187 ++- .../content/web_ui/safe_browsing_ui.h | 11 + chromium/components/safe_browsing/core/BUILD.gn | 13 + .../components/safe_browsing/core/browser/BUILD.gn | 2 +- .../core/browser/safe_browsing_token_fetcher.cc | 2 +- .../core/browser/safe_browsing_url_checker_impl.cc | 48 +- .../core/browser/safe_browsing_url_checker_impl.h | 28 +- .../safe_browsing_url_checker_impl_unittest.cc | 51 +- .../core/common/safe_browsing_prefs.cc | 73 +- .../core/common/safe_browsing_prefs.h | 3 +- .../core/common/safe_browsing_prefs_unittest.cc | 8 +- .../core/db/v4_local_database_manager.cc | 3 +- .../core/db/v4_protocol_manager_util_unittest.cc | 1 + .../components/safe_browsing/core/db/v4_store.cc | 45 +- .../safe_browsing/core/db/v4_store_fuzzer.cc | 1 + .../safe_browsing/core/db/v4_store_unittest.cc | 1 + .../core/db/v4_update_protocol_manager.cc | 9 +- .../core/db/v4_update_protocol_manager.h | 5 + chromium/components/safe_browsing/core/features.cc | 27 +- chromium/components/safe_browsing/core/features.h | 6 + .../safe_browsing/core/proto/client_model.proto | 2 +- .../safe_browsing/core/proto/realtimeapi.proto | 2 +- .../safe_browsing/core/proto/webprotect.proto | 3 + .../safe_browsing/core/realtime/BUILD.gn | 42 +- .../safe_browsing/core/realtime/policy_engine.cc | 60 +- .../safe_browsing/core/realtime/policy_engine.h | 26 +- .../core/realtime/policy_engine_unittest.cc | 86 +- .../core/realtime/url_lookup_service.cc | 332 +----- .../core/realtime/url_lookup_service.h | 151 +-- .../core/realtime/url_lookup_service_base.cc | 284 +++++ .../core/realtime/url_lookup_service_base.h | 191 +++ .../core/realtime/url_lookup_service_unittest.cc | 12 +- .../safe_browsing/core/verdict_cache_manager.cc | 11 +- .../safe_browsing/core/verdict_cache_manager.h | 6 +- .../core/verdict_cache_manager_unittest.cc | 32 +- .../components/safe_browsing/ios/browser/BUILD.gn | 18 + .../ios/browser/safe_browsing_url_allow_list.h | 131 +++ .../ios/browser/safe_browsing_url_allow_list.mm | 159 +++ chromium/components/safe_search_api/url_checker.cc | 1 + chromium/components/safety_check/safety_check.h | 1 + chromium/components/schema_org/extractor.cc | 1 - .../android/template_url_service_android.cc | 1 + .../default_search_policy_handler.cc | 25 +- .../search_engines/search_engines_pref_names.cc | 4 + .../search_engines/search_engines_pref_names.h | 1 + chromium/components/search_engines/template_url.cc | 8 +- chromium/components/search_engines/template_url.h | 11 +- .../search_engines/template_url_service.cc | 2 + .../search_engines/template_url_unittest.cc | 22 +- chromium/components/search_provider_logos/BUILD.gn | 2 - .../components/search_provider_logos/features.cc | 14 - .../components/search_provider_logos/features.h | 18 - .../search_provider_logos/google_logo_api.cc | 43 +- .../google_logo_api_unittest.cc | 38 +- .../components/search_provider_logos/logo_cache.cc | 23 + .../search_provider_logos/logo_cache_unittest.cc | 18 + .../components/search_provider_logos/logo_common.h | 9 + .../search_provider_logos/logo_service_impl.cc | 1 - .../logo_service_impl_unittest.cc | 1 - .../security_interstitials/content/BUILD.gn | 4 + .../content/captive_portal_blocking_page.cc | 3 + .../content/captive_portal_helper_android.cc | 13 +- .../content/cert_report_helper.cc | 8 +- .../content/insecure_form_blocking_page.cc | 110 ++ .../content/insecure_form_blocking_page.h | 43 + .../content/insecure_form_navigation_throttle.cc | 79 ++ .../content/insecure_form_navigation_throttle.h | 40 + .../content/origin_policy_interstitial_page.cc | 1 + .../content/security_blocking_page_factory.h | 5 + .../content/security_interstitial_page.cc | 8 - .../content/ssl_blocking_page_base.cc | 1 - .../content/ssl_error_handler_unittest.cc | 7 +- .../core/browser/resources/extended_reporting.js | 10 +- .../resources/interstitial_insecureform.css | 20 + .../core/browser/resources/interstitial_large.html | 3 +- .../core/browser/resources/interstitial_large.js | 30 +- .../resources/interstitial_webview_quiet.html | 2 +- .../resources/interstitial_webview_quiet.js | 3 + .../browser/resources/list_of_interstitials.html | 9 + .../core/common/resources/interstitial_common.css | 1 + .../core/common_string_util.cc | 3 + .../core/controller_client.cc | 3 - .../security_interstitials/core/features.cc | 3 + .../security_interstitials/core/features.h | 3 + .../core/safe_browsing_loud_error_ui.cc | 11 +- .../core/ssl_error_options_mask.cc | 1 + .../components/security_interstitials_strings.grdp | 46 +- .../IDS_INSECURE_FORM_BACK_BUTTON.png.sha1 | 1 + .../IDS_INSECURE_FORM_HEADING.png.sha1 | 1 + .../IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1 | 1 + .../IDS_INSECURE_FORM_SUBMIT_BUTTON.png.sha1 | 1 + .../IDS_INSECURE_FORM_TITLE.png.sha1 | 1 + .../IDS_LOOKALIKE_URL_BACK_TO_SAFETY.png.sha1 | 1 + chromium/components/security_state/DEPS | 1 - .../security_state/content/content_utils.cc | 1 - chromium/components/security_state/core/BUILD.gn | 3 - .../security_state/core/security_state.cc | 15 +- .../security_state/core/security_state.h | 12 +- .../core/security_state_pref_names.cc | 14 - .../core/security_state_pref_names.h | 19 - .../security_state/core/security_state_unittest.cc | 2 - chromium/components/security_state/ios/BUILD.gn | 4 +- chromium/components/security_state/ios/DEPS | 3 +- .../security_state/ios/security_state_utils.mm | 2 +- .../ios/security_state_utils_unittest.mm | 2 +- chromium/components/send_tab_to_self/BUILD.gn | 2 - .../send_tab_to_self/send_tab_to_self_bridge.cc | 82 -- .../send_tab_to_self/send_tab_to_self_metrics.cc | 17 - .../send_tab_to_self/send_tab_to_self_metrics.h | 31 - chromium/components/services/app_service/BUILD.gn | 38 + chromium/components/services/app_service/DEPS | 43 + chromium/components/services/app_service/README.md | 504 +++++++- .../services/app_service/app_service_impl.cc | 493 ++++++++ .../services/app_service/app_service_impl.h | 168 +++ .../app_service/app_service_impl_unittest.cc | 405 +++++++ .../services/app_service/public/cpp/BUILD.gn | 129 +- .../app_service/public/cpp/app_registry_cache.cc | 133 +++ .../app_service/public/cpp/app_registry_cache.h | 201 ++++ .../public/cpp/app_registry_cache_unittest.cc | 384 ++++++ .../public/cpp/app_registry_cache_wrapper.cc | 46 + .../public/cpp/app_registry_cache_wrapper.h | 46 + .../services/app_service/public/cpp/app_update.cc | 527 +++++++++ .../services/app_service/public/cpp/app_update.h | 148 +++ .../app_service/public/cpp/app_update_unittest.cc | 798 +++++++++++++ .../services/app_service/public/cpp/icon_cache.cc | 157 +++ .../services/app_service/public/cpp/icon_cache.h | 118 ++ .../app_service/public/cpp/icon_cache_unittest.cc | 214 ++++ .../app_service/public/cpp/icon_coalescer.cc | 212 ++++ .../app_service/public/cpp/icon_coalescer.h | 102 ++ .../public/cpp/icon_coalescer_unittest.cc | 341 ++++++ .../services/app_service/public/cpp/icon_loader.cc | 79 ++ .../services/app_service/public/cpp/icon_loader.h | 111 ++ .../services/app_service/public/cpp/instance.cc | 36 + .../services/app_service/public/cpp/instance.h | 65 + .../app_service/public/cpp/instance_registry.cc | 154 +++ .../app_service/public/cpp/instance_registry.h | 181 +++ .../public/cpp/instance_registry_unittest.cc | 593 ++++++++++ .../app_service/public/cpp/instance_update.cc | 161 +++ .../app_service/public/cpp/instance_update.h | 79 ++ .../public/cpp/instance_update_unittest.cc | 215 ++++ .../app_service/public/cpp/intent_filter_util.cc | 128 ++ .../app_service/public/cpp/intent_filter_util.h | 81 ++ .../app_service/public/cpp/intent_test_util.cc | 49 + .../app_service/public/cpp/intent_test_util.h | 24 + .../services/app_service/public/cpp/intent_util.cc | 123 ++ .../services/app_service/public/cpp/intent_util.h | 33 +- .../app_service/public/cpp/intent_util_unittest.cc | 268 +++++ .../public/cpp/preferred_apps_converter.cc | 164 +++ .../public/cpp/preferred_apps_converter.h | 54 + .../cpp/preferred_apps_converter_unittest.cc | 471 ++++++++ .../app_service/public/cpp/preferred_apps_list.cc | 142 +++ .../app_service/public/cpp/preferred_apps_list.h | 70 ++ .../public/cpp/preferred_apps_list_unittest.cc | 585 +++++++++ .../app_service/public/cpp/publisher_base.cc | 126 ++ .../app_service/public/cpp/publisher_base.h | 89 ++ .../app_service/public/cpp/stub_icon_loader.cc | 53 + .../app_service/public/cpp/stub_icon_loader.h | 45 + .../services/app_service/public/mojom/BUILD.gn | 21 +- .../app_service/public/mojom/app_service.mojom | 235 ++++ .../services/app_service/public/mojom/types.mojom | 320 ++++- .../heap_profiling/json_exporter_unittest.cc | 10 +- .../services/paint_preview_compositor/BUILD.gn | 2 + .../paint_preview_compositor_impl.cc | 157 ++- .../paint_preview_compositor_impl.h | 15 +- .../paint_preview_compositor_impl_unittest.cc | 9 + .../public/mojom/paint_preview_compositor.mojom | 3 + .../paint_preview_compositor/skp_result.cc | 15 + .../services/paint_preview_compositor/skp_result.h | 31 + .../print_compositor/print_compositor_impl.cc | 14 +- .../print_compositor/print_compositor_impl.h | 5 +- chromium/components/services/storage/BUILD.gn | 2 +- .../components/services/storage/dom_storage/DEPS | 3 - .../storage/dom_storage/local_storage_impl.cc | 2 +- .../dom_storage/session_storage_impl_unittest.cc | 7 +- .../dom_storage/session_storage_metadata.cc | 1 + .../storage/indexed_db/leveldb/leveldb_factory.cc | 1 + .../storage/indexed_db/leveldb/leveldb_state.cc | 7 +- .../storage/indexed_db/scopes/leveldb_scope.h | 2 +- .../storage/indexed_db/scopes/scope_lock.h | 1 - .../storage/indexed_db/scopes/scope_lock_range.h | 1 - .../indexed_db/scopes/scopes_lock_manager.h | 1 - .../transactional_leveldb_transaction.cc | 6 +- .../transactional_leveldb_transaction.h | 5 +- .../transactional_leveldb_transaction_unittest.cc | 83 +- .../services/storage/public/mojom/BUILD.gn | 3 + .../public/mojom/blob_storage_context.mojom | 88 ++ .../storage/public/mojom/indexed_db_control.mojom | 13 + .../mojom/service_worker_storage_control.mojom | 46 +- .../sessions/core/command_storage_backend.cc | 43 +- .../sessions/core/command_storage_backend.h | 3 + .../core/command_storage_backend_unittest.cc | 16 + .../sessions/core/session_service_commands.cc | 40 + .../sessions/core/session_service_commands.h | 4 + chromium/components/sessions/core/session_types.h | 3 + .../signin/core/browser/android/BUILD.gn | 10 +- .../components/signin/AccessTokenData.java | 54 + .../components/signin/AccountManagerDelegate.java | 4 +- .../components/signin/AccountManagerFacade.java | 4 +- .../signin/AccountManagerFacadeImpl.java | 4 +- .../components/signin/AccountTrackerService.java | 20 +- .../signin/SystemAccountManagerDelegate.java | 9 +- .../signin/core/browser/cookie_reminter.cc | 3 - .../signin/core/browser/dice_header_helper.cc | 1 + .../identity_manager/oauth_multilogin_helper.cc | 20 +- .../identity_manager/oauth_multilogin_helper.h | 3 +- .../oauth_multilogin_helper_unittest.cc | 6 +- .../identity_manager/primary_account_manager.cc | 7 +- .../identity_manager/primary_account_manager.h | 2 - .../primary_account_manager_unittest.cc | 6 +- .../primary_account_mutator_impl.cc | 19 +- .../primary_account_mutator_impl.h | 2 +- ...rofile_oauth2_token_service_delegate_android.cc | 13 +- chromium/components/signin/public/android/DEPS | 2 + .../ProfileOAuth2TokenServiceDelegate.java | 38 +- .../ProfileOAuth2TokenServiceDelegateTest.java | 8 +- .../signin/public/base/signin_metrics.cc | 7 + .../components/signin/public/base/signin_metrics.h | 19 + .../signin/public/base/signin_metrics_unittest.cc | 2 + .../signin/public/identity_manager/BUILD.gn | 5 + .../accounts_cookie_mutator_unittest.cc | 2 +- .../public/identity_manager/identity_manager.cc | 147 +-- .../public/identity_manager/identity_manager.h | 92 +- .../identity_manager/identity_manager_builder.cc | 54 +- .../identity_manager/identity_manager_builder.h | 6 +- .../identity_manager_builder_unittest.cc | 134 +++ .../identity_manager/identity_manager_unittest.cc | 298 ++--- .../identity_manager/identity_test_environment.cc | 41 +- .../identity_manager/identity_test_environment.h | 2 + .../public/identity_manager/identity_test_utils.cc | 13 +- .../public/identity_manager/identity_utils.cc | 1 + .../identity_manager/primary_account_mutator.h | 14 +- chromium/components/site_isolation/BUILD.gn | 26 + chromium/components/site_isolation/DEPS | 7 +- chromium/components/site_isolation/features.cc | 44 + chromium/components/site_isolation/features.h | 20 + chromium/components/site_isolation/pref_names.cc | 17 + chromium/components/site_isolation/pref_names.h | 16 + .../site_isolation/site_isolation_policy.cc | 133 +++ .../site_isolation/site_isolation_policy.h | 54 + .../site_isolation_policy_unittest.cc | 938 +++++++++++++++ chromium/components/speech/BUILD.gn | 22 + chromium/components/speech/DEPS | 4 + chromium/components/speech/downstream_loader.cc | 48 + chromium/components/speech/downstream_loader.h | 46 + .../components/speech/downstream_loader_client.h | 40 + chromium/components/speech/upstream_loader.cc | 129 ++ chromium/components/speech/upstream_loader.h | 77 ++ .../components/speech/upstream_loader_client.h | 34 + chromium/components/spellcheck/BUILD.gn | 1 - .../spellcheck/browser/spell_check_host_impl.cc | 9 +- .../spellcheck/browser/spell_check_host_impl.h | 9 +- .../spellcheck/browser/spellcheck_platform.h | 12 +- .../spellcheck/browser/spellcheck_platform_win.cc | 4 +- .../spellcheck/browser/windows_spell_checker.cc | 1 + .../browser/windows_spell_checker_unittest.cc | 14 - chromium/components/spellcheck/common/BUILD.gn | 4 - .../components/spellcheck/common/spellcheck.mojom | 2 +- .../spellcheck/common/spellcheck_features.cc | 17 +- .../spellcheck/common/spellcheck_features.h | 25 +- .../components/spellcheck/renderer/spellcheck.cc | 36 +- .../components/spellcheck/renderer/spellcheck.h | 4 + .../spellcheck/renderer/spellcheck_language.cc | 4 + .../spellcheck/renderer/spellcheck_language.h | 4 + .../spellcheck/renderer/spellcheck_provider.cc | 57 +- .../spellcheck/renderer/spellcheck_provider.h | 12 +- .../renderer/spellcheck_provider_test.cc | 52 +- .../spellcheck/renderer/spellcheck_provider_test.h | 26 +- .../renderer/spellcheck_provider_unittest.cc | 163 ++- .../renderer/spellcheck_renderer_metrics.cc | 8 +- .../renderer/spellcheck_renderer_metrics.h | 4 +- .../spellcheck/renderer/spellcheck_worditerator.cc | 18 + .../spellcheck/renderer/spellcheck_worditerator.h | 4 + .../renderer/spellcheck_worditerator_unittest.cc | 72 ++ .../spellcheck/renderer/spelling_engine.cc | 32 +- chromium/components/spellcheck/spellcheck.md | 8 +- .../spellcheck/spellcheck_build_features.gni | 7 - chromium/components/sqlite_proto/key_value_data.h | 20 +- chromium/components/sqlite_proto/key_value_table.h | 9 +- chromium/components/ssl_errors/error_info.cc | 1 + .../browser/startup_metric_utils.cc | 6 - .../storage_monitor/mtab_watcher_linux.cc | 1 + .../components/storage_monitor/storage_monitor.cc | 1 + .../storage_monitor/storage_monitor_mac.mm | 5 +- .../storage_monitor_mac_unittest.mm | 5 +- .../storage_monitor/storage_monitor_win.cc | 1 + .../storage_monitor/volume_mount_watcher_win.cc | 36 +- .../components/strings/components_strings_af.xtb | 100 +- .../components/strings/components_strings_am.xtb | 87 +- .../components/strings/components_strings_ar.xtb | 100 +- .../components/strings/components_strings_as.xtb | 100 +- .../components/strings/components_strings_az.xtb | 99 +- .../components/strings/components_strings_be.xtb | 100 +- .../components/strings/components_strings_bg.xtb | 96 +- .../components/strings/components_strings_bn.xtb | 100 +- .../components/strings/components_strings_bs.xtb | 100 +- .../components/strings/components_strings_ca.xtb | 82 +- .../components/strings/components_strings_cs.xtb | 82 +- .../components/strings/components_strings_da.xtb | 100 +- .../components/strings/components_strings_de.xtb | 84 +- .../components/strings/components_strings_el.xtb | 100 +- .../strings/components_strings_en-GB.xtb | 93 +- .../strings/components_strings_es-419.xtb | 100 +- .../components/strings/components_strings_es.xtb | 100 +- .../components/strings/components_strings_et.xtb | 98 +- .../components/strings/components_strings_eu.xtb | 86 +- .../components/strings/components_strings_fa.xtb | 100 +- .../components/strings/components_strings_fi.xtb | 100 +- .../components/strings/components_strings_fil.xtb | 95 +- .../strings/components_strings_fr-CA.xtb | 82 +- .../components/strings/components_strings_fr.xtb | 91 +- .../components/strings/components_strings_gl.xtb | 95 +- .../components/strings/components_strings_gu.xtb | 98 +- .../components/strings/components_strings_hi.xtb | 96 +- .../components/strings/components_strings_hr.xtb | 101 +- .../components/strings/components_strings_hu.xtb | 96 +- .../components/strings/components_strings_hy.xtb | 100 +- .../components/strings/components_strings_id.xtb | 91 +- .../components/strings/components_strings_is.xtb | 100 +- .../components/strings/components_strings_it.xtb | 100 +- .../components/strings/components_strings_iw.xtb | 96 +- .../components/strings/components_strings_ja.xtb | 82 +- .../components/strings/components_strings_ka.xtb | 82 +- .../components/strings/components_strings_kk.xtb | 100 +- .../components/strings/components_strings_km.xtb | 100 +- .../components/strings/components_strings_kn.xtb | 88 +- .../components/strings/components_strings_ko.xtb | 91 +- .../components/strings/components_strings_ky.xtb | 100 +- .../components/strings/components_strings_lo.xtb | 100 +- .../components/strings/components_strings_lt.xtb | 100 +- .../components/strings/components_strings_lv.xtb | 100 +- .../components/strings/components_strings_mk.xtb | 100 +- .../components/strings/components_strings_ml.xtb | 90 +- .../components/strings/components_strings_mn.xtb | 99 +- .../components/strings/components_strings_mr.xtb | 100 +- .../components/strings/components_strings_ms.xtb | 100 +- .../components/strings/components_strings_my.xtb | 100 +- .../components/strings/components_strings_ne.xtb | 90 +- .../components/strings/components_strings_nl.xtb | 88 +- .../components/strings/components_strings_no.xtb | 101 +- .../components/strings/components_strings_or.xtb | 82 +- .../components/strings/components_strings_pa.xtb | 91 +- .../components/strings/components_strings_pl.xtb | 87 +- .../strings/components_strings_pt-BR.xtb | 82 +- .../strings/components_strings_pt-PT.xtb | 100 +- .../components/strings/components_strings_ro.xtb | 100 +- .../components/strings/components_strings_ru.xtb | 82 +- .../components/strings/components_strings_si.xtb | 94 +- .../components/strings/components_strings_sk.xtb | 82 +- .../components/strings/components_strings_sl.xtb | 100 +- .../components/strings/components_strings_sq.xtb | 91 +- .../strings/components_strings_sr-Latn.xtb | 100 +- .../components/strings/components_strings_sr.xtb | 100 +- .../components/strings/components_strings_sv.xtb | 100 +- .../components/strings/components_strings_sw.xtb | 95 +- .../components/strings/components_strings_ta.xtb | 96 +- .../components/strings/components_strings_te.xtb | 100 +- .../components/strings/components_strings_th.xtb | 82 +- .../components/strings/components_strings_tr.xtb | 89 +- .../components/strings/components_strings_uk.xtb | 95 +- .../components/strings/components_strings_ur.xtb | 97 +- .../components/strings/components_strings_uz.xtb | 100 +- .../components/strings/components_strings_vi.xtb | 100 +- .../strings/components_strings_zh-CN.xtb | 82 +- .../strings/components_strings_zh-HK.xtb | 82 +- .../strings/components_strings_zh-TW.xtb | 82 +- .../components/strings/components_strings_zu.xtb | 100 +- .../subresource_filter/FILTER_LIST_GENERATION.md | 4 +- chromium/components/subresource_filter/README.md | 2 +- ...state_computing_navigation_throttle_unittest.cc | 62 +- .../browser/async_document_subresource_filter.cc | 42 + .../browser/async_document_subresource_filter.h | 35 +- .../async_document_subresource_filter_unittest.cc | 37 +- .../content_subresource_filter_throttle_manager.cc | 23 +- ...subresource_filter_throttle_manager_unittest.cc | 16 +- .../browser/fake_safe_browsing_database_manager.cc | 15 +- .../browser/fake_safe_browsing_database_manager.h | 12 +- .../content/browser/ruleset_publisher_impl.cc | 5 +- .../content/browser/ruleset_service_unittest.cc | 1 + ...r_safe_browsing_activation_throttle_unittest.cc | 26 +- .../browser/verified_ruleset_dealer_unittest.cc | 10 +- .../content/common/ruleset_dealer_unittest.cc | 38 +- .../renderer/subresource_filter_agent_unittest.cc | 3 +- .../core/browser/subresource_filter_features.h | 4 +- .../core/common/activation_decision.h | 4 +- .../core/common/activation_scope.h | 2 +- .../core/common/flat/indexed_ruleset.fbs | 10 +- .../core/common/indexed_ruleset.cc | 30 +- .../core/common/indexed_ruleset.h | 12 +- .../core/common/indexed_ruleset_unittest.cc | 52 +- .../core/common/test_ruleset_utils.cc | 4 +- .../core/common/test_ruleset_utils.h | 8 +- .../core/common/unindexed_ruleset_unittest.cc | 4 +- .../core/mojom/subresource_filter.mojom | 6 +- .../subresource_filter/tools/filter_tool.h | 8 +- .../subresource_filter/tools/filter_tool_main.cc | 12 +- .../tools/filter_tool_unittest.cc | 34 +- .../tools/indexing_tool_unittest.cc | 4 +- .../subresource_filter/tools/rule_parser/rule.cc | 8 +- .../subresource_filter/tools/rule_parser/rule.h | 14 +- .../tools/rule_parser/rule_parser.cc | 28 +- .../tools/rule_parser/rule_parser.h | 4 +- .../tools/rule_parser/rule_parser_unittest.cc | 24 +- .../tools/rule_parser/rule_unittest.cc | 4 +- .../ruleset_converter/rule_stream_unittest.cc | 4 +- .../ruleset_converter_unittest.cc | 4 +- chromium/components/suggestions/blacklist_store.cc | 1 + .../components/suggestions/suggestions_store.cc | 1 + chromium/components/sync/BUILD.gn | 47 +- chromium/components/sync/android/BUILD.gn | 2 - chromium/components/sync/base/ordinal.h | 12 +- .../components/sync/protocol/protocol_sources.gni | 1 + chromium/components/sync_bookmarks/PRESUBMIT.py | 7 +- .../bookmark_local_changes_builder.cc | 4 +- .../sync_bookmarks/bookmark_model_merger.cc | 13 +- .../sync_bookmarks/bookmark_model_merger.h | 6 + .../bookmark_model_merger_unittest.cc | 146 ++- .../sync_bookmarks/bookmark_model_observer_impl.cc | 34 +- .../bookmark_model_observer_impl_unittest.cc | 243 ++++ .../bookmark_model_type_processor.cc | 18 +- .../bookmark_model_type_processor_unittest.cc | 95 +- .../bookmark_remote_updates_handler.cc | 27 +- .../bookmark_remote_updates_handler.h | 11 +- .../bookmark_remote_updates_handler_unittest.cc | 130 +- .../bookmark_specifics_conversions.cc | 14 +- .../bookmark_specifics_conversions.h | 10 +- chromium/components/sync_bookmarks/switches.cc | 6 +- chromium/components/sync_bookmarks/switches.h | 2 + .../sync_bookmarks/synced_bookmark_tracker.cc | 30 +- .../sync_bookmarks/synced_bookmark_tracker.h | 23 +- .../synced_bookmark_tracker_unittest.cc | 122 ++ .../sync_preferences/pref_model_associator.cc | 31 +- .../sync_preferences/pref_model_associator.h | 2 +- .../sync_preferences/pref_service_syncable.cc | 1 - .../pref_service_syncable_unittest.cc | 78 +- chromium/components/sync_sessions/PRESUBMIT.py | 7 +- .../sync_sessions/sessions_global_id_mapper.cc | 21 +- chromium/components/system_media_controls/BUILD.gn | 17 +- .../mac/now_playing_info_center_delegate.mm | 1 + .../components/tab_groups/tab_group_visual_data.cc | 15 +- .../components/tab_groups/tab_group_visual_data.h | 10 +- .../graphics_memory_dump_provider_android.cc | 1 + .../tracing/common/trace_startup_config.cc | 9 +- .../components/translate/content/android/BUILD.gn | 17 + .../components/translate/core/browser/BUILD.gn | 1 + chromium/components/translate_strings.grdp | 18 - chromium/components/ui_devtools/agent_util.cc | 1 + .../components/ui_devtools/views/dom_agent_mac.mm | 5 +- .../ui_devtools/views/overlay_agent_mac.mm | 1 + .../components/ui_devtools/views/widget_element.cc | 1 + chromium/components/ukm/BUILD.gn | 3 +- .../components/ukm/field_trials_provider_helper.cc | 23 + .../components/ukm/field_trials_provider_helper.h | 20 + .../ukm/observers/ukm_consent_state_observer.cc | 2 +- chromium/components/ukm/ukm_recorder_impl.cc | 30 +- chromium/components/ukm/ukm_reporting_service.cc | 13 +- chromium/components/ukm/ukm_service.cc | 41 +- chromium/components/ukm/ukm_service.h | 13 +- chromium/components/ukm/ukm_service_unittest.cc | 144 +-- chromium/components/ukm/ukm_test_helper.cc | 9 +- .../url_keyed_data_collection_consent_helper.cc | 8 +- .../url_keyed_data_collection_consent_helper.h | 16 +- ...eyed_data_collection_consent_helper_unittest.cc | 3 +- chromium/components/update_client/action_runner.cc | 1 + .../update_client/background_downloader_win.cc | 1 + chromium/components/update_client/component.cc | 1 + .../components/update_client/net/network_impl.cc | 5 +- .../components/update_client/net/network_impl.h | 1 + chromium/components/update_client/network.h | 1 + .../update_client/protocol_serializer_fuzzer.cc | 3 +- .../components/update_client/request_sender.cc | 7 +- .../update_client/request_sender_unittest.cc | 3 + chromium/components/update_client/task_update.cc | 11 +- chromium/components/update_client/task_update.h | 3 + .../update_client/update_checker_unittest.cc | 8 +- chromium/components/update_client/update_client.cc | 2 +- .../update_client/update_query_params.cc | 4 +- .../spoof_checks/idn_spoof_checker.cc | 65 +- .../url_formatter/spoof_checks/idn_spoof_checker.h | 72 +- .../spoof_checks/idn_spoof_checker_unittest.cc | 41 +- .../spoof_checks/top_domains/domains.skeletons | 999 ++++++++-------- .../top_domains/make_top_domain_skeletons.cc | 60 +- .../top_domains/test_domains.skeletons | 11 +- .../top_domains/top_domain_generator.cc | 77 +- .../spoof_checks/top_domains/top_domain_util.cc | 2 +- .../spoof_checks/top_domains/trie_entry.cc | 16 + .../spoof_checks/top_domains/trie_entry.h | 18 + .../components/url_formatter/tools/format_url.cc | 75 +- chromium/components/url_formatter/url_formatter.cc | 130 +- chromium/components/url_formatter/url_formatter.h | 16 +- .../components/url_pattern_index/closed_hash_map.h | 2 +- .../components/url_pattern_index/ngram_extractor.h | 2 +- .../components/url_pattern_index/string_splitter.h | 2 +- chromium/components/user_manager/BUILD.gn | 1 + .../components/user_manager/fake_user_manager.cc | 9 +- .../components/user_manager/fake_user_manager.h | 11 + chromium/components/user_manager/known_user.cc | 19 +- .../user_manager/user_image/user_image.cc | 1 + chromium/components/user_prefs/BUILD.gn | 10 + chromium/components/user_prefs/android/BUILD.gn | 20 + chromium/components/user_prefs/android/DEPS | 4 + .../chromium/components/user_prefs/UserPrefs.java | 27 + .../user_prefs/android/user_prefs_android.cc | 23 + chromium/components/variations/BUILD.gn | 2 + chromium/components/variations/android/BUILD.gn | 3 + .../variations/firstrun/VariationsSeedBridge.java | 25 +- .../variations/client_filterable_state.cc | 27 + .../variations/client_filterable_state.h | 3 + .../field_trial_testing_config_schema.json | 4 + .../field_trial_config/field_trial_util.cc | 15 +- .../field_trial_util_unittest.cc | 404 ++++++- .../net/variations_command_line_unittest.cc | 3 +- .../variations/net/variations_http_headers.cc | 169 ++- .../variations/net/variations_http_headers.h | 4 +- .../net/variations_http_headers_unittest.cc | 137 ++- .../net/variations_url_loader_throttle.cc | 2 +- chromium/components/variations/processed_study.cc | 1 + chromium/components/variations/proto/BUILD.gn | 13 + .../variations/proto/client_variations.proto | 1 + chromium/components/variations/proto/study.proto | 1 + .../variations/proto/variations_seed.proto | 1 + .../service/variations_field_trial_creator.cc | 28 +- .../service/variations_field_trial_creator.h | 4 - .../variations_field_trial_creator_unittest.cc | 6 +- .../variations/service/variations_service.cc | 31 +- .../variations/service/variations_service.h | 5 +- .../service/variations_service_client.cc | 1 + .../service/variations_service_unittest.cc | 46 +- chromium/components/variations/study_filtering.cc | 1 + .../variations/synthetic_trial_registry.cc | 142 ++- .../variations/synthetic_trial_registry.h | 68 +- .../synthetic_trial_registry_unittest.cc | 116 +- chromium/components/variations/synthetic_trials.h | 3 + chromium/components/variations/variations_client.h | 4 +- .../variations/variations_http_header_provider.cc | 52 +- .../variations/variations_http_header_provider.h | 15 +- .../variations_http_header_provider_unittest.cc | 37 + .../components/variations/variations_seed_store.cc | 73 +- .../components/variations/variations_seed_store.h | 18 +- .../variations/variations_seed_store_unittest.cc | 25 +- chromium/components/vector_icons/BUILD.gn | 1 - .../vector_icons/camera_pan_tilt_zoom.icon | 34 - .../vector_icons/vector_icons.cc.template | 1 - .../visitedlink/browser/visitedlink_writer.cc | 12 +- .../visitedlink/browser/visitedlink_writer.h | 2 +- .../viz/client/client_resource_provider.cc | 4 +- .../viz/client/frame_eviction_manager.cc | 1 + chromium/components/viz/common/BUILD.gn | 2 + chromium/components/viz/common/DEPS | 4 +- .../components/viz/common/delegated_ink_metadata.h | 62 + chromium/components/viz/common/features.cc | 12 +- chromium/components/viz/common/features.h | 3 + .../viz/common/frame_sinks/begin_frame_source.h | 2 +- .../viz/common/frame_sinks/copy_output_request.cc | 28 +- .../components/viz/common/gl_scaler_test_util.cc | 1 + .../gpu/context_cache_controller_unittest.cc | 2 +- .../viz/common/gpu/context_lost_reason.h | 10 +- .../components/viz/common/gpu/metal_api_proxy.h | 3 + .../viz/common/gpu/metal_context_provider.mm | 1 + .../gpu/vulkan_in_process_context_provider.cc | 36 +- .../gpu/vulkan_in_process_context_provider.h | 3 + .../viz/common/quads/compositor_frame_metadata.cc | 28 +- .../viz/common/quads/compositor_frame_metadata.h | 14 + chromium/components/viz/common/quads/draw_quad.cc | 4 +- .../viz/common/quads/draw_quad_unittest.cc | 1 + .../components/viz/common/quads/render_pass.cc | 8 +- .../viz/common/resources/platform_color.h | 2 +- .../viz/common/resources/resource_format_utils.cc | 7 +- .../viz/common/resources/resource_format_utils.h | 5 + .../common/resources/resource_format_utils_mac.mm | 40 + .../viz/common/resources/resource_sizes.h | 2 +- .../components/viz/common/yuv_readback_unittest.cc | 15 +- chromium/components/viz/demo/demo_main.cc | 22 +- chromium/components/viz/host/BUILD.gn | 4 + chromium/components/viz/host/gpu_host_impl.cc | 12 +- chromium/components/viz/host/gpu_host_impl.h | 2 + .../components/viz/host/hit_test/hit_test_query.cc | 6 +- .../viz/host/host_gpu_memory_buffer_manager.cc | 20 +- .../host_gpu_memory_buffer_manager_unittest.cc | 15 +- .../viz/host/renderer_settings_creation.cc | 23 +- chromium/components/viz/service/BUILD.gn | 29 +- chromium/components/viz/service/display/DEPS | 1 + .../viz/service/display/ca_layer_overlay.h | 7 +- .../display/copy_output_scaling_pixeltest.cc | 3 + .../viz/service/display/dc_layer_overlay.cc | 1 - .../viz/service/display/direct_renderer.cc | 106 +- .../viz/service/display/direct_renderer.h | 5 + chromium/components/viz/service/display/display.cc | 108 +- .../service/display/display_resource_provider.cc | 87 +- .../service/display/display_resource_provider.h | 21 +- .../viz/service/display/display_unittest.cc | 28 +- .../components/viz/service/display/gl_renderer.cc | 32 +- .../components/viz/service/display/gl_renderer.h | 5 +- .../viz/service/display/gl_renderer_copier.cc | 26 +- .../service/display/gl_renderer_copier_unittest.cc | 12 +- .../viz/service/display/gl_renderer_unittest.cc | 6 + .../viz/service/display/output_surface.h | 12 +- .../service/display/overlay_processor_interface.cc | 6 +- .../service/display/overlay_processor_interface.h | 4 + .../viz/service/display/overlay_processor_mac.cc | 6 + .../viz/service/display/overlay_processor_mac.h | 1 + .../viz/service/display/overlay_processor_ozone.cc | 1 + .../viz/service/display/overlay_processor_stub.cc | 4 + .../viz/service/display/overlay_processor_stub.h | 1 + .../display/overlay_processor_using_strategy.cc | 7 + .../display/overlay_processor_using_strategy.h | 1 + .../viz/service/display/overlay_processor_win.cc | 6 + .../viz/service/display/overlay_processor_win.h | 1 + .../display/overlay_strategy_underlay_cast.cc | 1 + .../viz/service/display/program_binding.cc | 1 + .../viz/service/display/program_binding.h | 2 +- .../viz/service/display/renderer_perftest.cc | 3 + chromium/components/viz/service/display/shader.h | 1 - .../viz/service/display/skia_readback_pixeltest.cc | 3 + .../viz/service/display/skia_renderer.cc | 76 +- .../viz/service/display/software_renderer.cc | 5 +- .../viz/service/display/surface_aggregator.cc | 494 ++++---- .../viz/service/display/surface_aggregator.h | 107 +- .../service/display/surface_aggregator_unittest.cc | 796 ++++++++++++- .../viz/service/display_embedder/buffer_queue.cc | 15 +- .../display_embedder/buffer_queue_unittest.cc | 5 +- .../gl_output_surface_buffer_queue.cc | 10 +- .../service/display_embedder/output_presenter.cc | 115 ++ .../service/display_embedder/output_presenter.h | 117 ++ .../display_embedder/output_presenter_fuchsia.cc | 490 ++++++++ .../display_embedder/output_presenter_fuchsia.h | 115 ++ .../display_embedder/output_presenter_gl.cc | 408 +++++++ .../service/display_embedder/output_presenter_gl.h | 78 ++ .../output_surface_provider_impl.cc | 46 +- .../service/display_embedder/skia_output_device.cc | 80 +- .../service/display_embedder/skia_output_device.h | 31 +- .../skia_output_device_buffer_queue.cc | 519 ++------ .../skia_output_device_buffer_queue.h | 73 +- .../skia_output_device_buffer_queue_unittest.cc | 22 +- .../display_embedder/skia_output_device_dawn.cc | 28 +- .../display_embedder/skia_output_device_dawn.h | 12 +- .../display_embedder/skia_output_device_gl.cc | 33 +- .../display_embedder/skia_output_device_gl.h | 9 +- .../skia_output_device_offscreen.cc | 2 +- .../display_embedder/skia_output_device_vulkan.cc | 77 +- .../display_embedder/skia_output_device_vulkan.h | 12 +- .../display_embedder/skia_output_device_webview.cc | 5 +- .../display_embedder/skia_output_device_x11.cc | 29 +- .../skia_output_surface_dependency.h | 4 + .../display_embedder/skia_output_surface_impl.cc | 108 +- .../display_embedder/skia_output_surface_impl.h | 3 + .../skia_output_surface_impl_on_gpu.cc | 364 +++--- .../skia_output_surface_impl_on_gpu.h | 31 +- .../skia_output_surface_impl_unittest.cc | 1 + .../frame_sinks/compositor_frame_sink_support.cc | 17 + .../compositor_frame_sink_support_unittest.cc | 37 +- .../service/frame_sinks/frame_sink_manager_impl.h | 2 +- .../frame_sinks/root_compositor_frame_sink_impl.cc | 2 +- .../frame_sink_video_capturer_impl.cc | 43 +- .../frame_sink_video_capturer_impl_unittest.cc | 137 +-- .../video_capture/interprocess_frame_pool.cc | 1 + .../components/viz/service/gl/gpu_service_impl.cc | 39 +- .../components/viz/service/gl/gpu_service_impl.h | 9 +- .../service/gl/info_collection_gpu_service_impl.cc | 46 +- .../service/gl/info_collection_gpu_service_impl.h | 19 +- .../components/viz/service/main/viz_main_impl.cc | 3 +- .../viz/service/surfaces/surface_manager.h | 2 +- .../viz/service/surfaces/surface_unittest.cc | 10 +- chromium/components/viz/viz.gni | 2 +- .../web_modal/web_contents_modal_dialog_manager.cc | 4 - .../web_modal/web_contents_modal_dialog_manager.h | 2 - .../web_contents_modal_dialog_manager_unittest.cc | 24 - chromium/components/webapk/OWNERS | 6 + .../components/webapk/android/libs/client/BUILD.gn | 39 + .../lib/client/ChromeWebApkHostSignature.java | 76 ++ .../webapk/lib/client/WebApkValidator.java | 439 +++++++ .../webapk/lib/client/WebApkValidatorTest.java | 462 ++++++++ .../webapk/lib/client/WebApkVerifySignature.java | 484 ++++++++ .../lib/client/WebApkVerifySignatureTest.java | 142 +++ .../components/webapk/android/libs/common/BUILD.gn | 12 + .../webapk/lib/common/WebApkConstants.java | 12 + .../webapk/lib/common/WebApkMetaDataKeys.java | 47 + chromium/components/webdata/common/BUILD.gn | 1 + .../webdata/common/web_data_request_manager.cc | 2 +- chromium/components/webdata/common/web_database.cc | 3 +- .../common/web_database_migration_unittest.cc | 34 +- .../components/webdata/common/web_database_table.h | 1 - .../webrtc/media_stream_devices_controller.cc | 43 +- chromium/components/wifi/wifi_service_win.cc | 1 + chromium/components/zucchini/algorithm.h | 2 +- chromium/components/zucchini/arm_utils.h | 2 +- chromium/components/zucchini/buffer_sink.h | 2 +- chromium/components/zucchini/buffer_source.h | 2 +- chromium/components/zucchini/buffer_view.h | 2 +- .../zucchini/heuristic_ensemble_matcher.cc | 1 + chromium/components/zucchini/image_index.h | 2 +- chromium/components/zucchini/patch_writer.h | 2 +- chromium/components/zucchini/rel32_finder.h | 1 - chromium/components/zucchini/suffix_array.h | 2 +- 2953 files changed, 106009 insertions(+), 33201 deletions(-) create mode 100644 chromium/components/arc/intent_helper/custom_tab.cc create mode 100644 chromium/components/arc/intent_helper/custom_tab.h create mode 100644 chromium/components/arc/mojom/ime_mojom_traits.cc create mode 100644 chromium/components/arc/mojom/ime_mojom_traits_unittest.cc delete mode 100644 chromium/components/arc/mojom/oemcrypto_daemon.mojom create mode 100644 chromium/components/arc/net/OWNERS delete mode 100644 chromium/components/autofill/android/autofill_provider_android.cc delete mode 100644 chromium/components/autofill/android/autofill_provider_android.h delete mode 100644 chromium/components/autofill/android/form_data_android.cc delete mode 100644 chromium/components/autofill/android/form_data_android.h delete mode 100644 chromium/components/autofill/android/form_field_data_android.cc delete mode 100644 chromium/components/autofill/android/form_field_data_android.h delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderImpl.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderUMA.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java delete mode 100644 chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java delete mode 100644 chromium/components/autofill/android/junit/BUILD.gn delete mode 100644 chromium/components/autofill/android/junit/OWNERS delete mode 100644 chromium/components/autofill/android/junit/src/org/chromium/components/autofill/AutofillProviderImplTest.java create mode 100644 chromium/components/autofill/android/provider/BUILD.gn create mode 100644 chromium/components/autofill/android/provider/OWNERS create mode 100644 chromium/components/autofill/android/provider/autofill_provider_android.cc create mode 100644 chromium/components/autofill/android/provider/autofill_provider_android.h create mode 100644 chromium/components/autofill/android/provider/form_data_android.cc create mode 100644 chromium/components/autofill/android/provider/form_data_android.h create mode 100644 chromium/components/autofill/android/provider/form_field_data_android.cc create mode 100644 chromium/components/autofill/android/provider/form_field_data_android.h create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormData.java create mode 100644 chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java create mode 100644 chromium/components/autofill/android/provider/junit/BUILD.gn create mode 100644 chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java create mode 100644 chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc create mode 100644 chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc create mode 100644 chromium/components/autofill/core/browser/payments/autofill_offer_manager.h create mode 100644 chromium/components/autofill/core/browser/payments/test_internal_authenticator.cc delete mode 100644 chromium/components/autofill_assistant/browser/actions/fallback_handler/fallback_data.cc delete mode 100644 chromium/components/autofill_assistant/browser/actions/fallback_handler/fallback_data.h create mode 100644 chromium/components/autofill_assistant/browser/actions/show_progress_bar_action_unittest.cc create mode 100644 chromium/components/autofill_assistant/browser/field_formatter.cc create mode 100644 chromium/components/autofill_assistant/browser/field_formatter.h create mode 100644 chromium/components/autofill_assistant/browser/field_formatter_unittest.cc create mode 100644 chromium/components/autofill_assistant/browser/radio_button_controller.cc create mode 100644 chromium/components/autofill_assistant/browser/radio_button_controller.h create mode 100644 chromium/components/autofill_assistant/browser/radio_button_controller_unittest.cc delete mode 100644 chromium/components/base32/base32_test_util.cc delete mode 100644 chromium/components/base32/base32_test_util.h delete mode 100644 chromium/components/blacklist/OWNERS delete mode 100644 chromium/components/blacklist/README.md delete mode 100644 chromium/components/blacklist/opt_out_blacklist/BUILD.gn delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item_unittest.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_unittest.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/opt_out_store.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/sql/BUILD.gn delete mode 100644 chromium/components/blacklist/opt_out_blacklist/sql/DEPS delete mode 100644 chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.cc delete mode 100644 chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h delete mode 100644 chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql_unittest.cc create mode 100644 chromium/components/blocked_content/BUILD.gn create mode 100644 chromium/components/blocked_content/DEPS create mode 100644 chromium/components/blocked_content/OWNERS create mode 100644 chromium/components/blocked_content/android/BUILD.gn create mode 100644 chromium/components/blocked_content/android/DEPS create mode 100644 chromium/components/blocked_content/android/popup_blocked_infobar_delegate.cc create mode 100644 chromium/components/blocked_content/android/popup_blocked_infobar_delegate.h create mode 100644 chromium/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc create mode 100644 chromium/components/blocked_content/android/res/drawable-hdpi/infobar_blocked_popups.png create mode 100644 chromium/components/blocked_content/android/res/drawable-mdpi/infobar_blocked_popups.png create mode 100644 chromium/components/blocked_content/android/res/drawable-xhdpi/infobar_blocked_popups.png create mode 100644 chromium/components/blocked_content/android/res/drawable-xxhdpi/infobar_blocked_popups.png create mode 100644 chromium/components/blocked_content/android/res/drawable-xxxhdpi/infobar_blocked_popups.png create mode 100644 chromium/components/blocked_content/list_item_position.cc create mode 100644 chromium/components/blocked_content/list_item_position.h create mode 100644 chromium/components/blocked_content/popup_blocker.cc create mode 100644 chromium/components/blocked_content/popup_blocker.h create mode 100644 chromium/components/blocked_content/popup_blocker_tab_helper.cc create mode 100644 chromium/components/blocked_content/popup_blocker_tab_helper.h create mode 100644 chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc create mode 100644 chromium/components/blocked_content/popup_navigation_delegate.h create mode 100644 chromium/components/blocked_content/popup_opener_tab_helper.cc create mode 100644 chromium/components/blocked_content/popup_opener_tab_helper.h create mode 100644 chromium/components/blocked_content/popup_tracker.cc create mode 100644 chromium/components/blocked_content/popup_tracker.h create mode 100644 chromium/components/blocked_content/pref_names.cc create mode 100644 chromium/components/blocked_content/pref_names.h create mode 100644 chromium/components/blocked_content/safe_browsing_triggered_popup_blocker.cc create mode 100644 chromium/components/blocked_content/safe_browsing_triggered_popup_blocker.h create mode 100644 chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc create mode 100644 chromium/components/blocked_content/url_list_manager.cc create mode 100644 chromium/components/blocked_content/url_list_manager.h create mode 100644 chromium/components/blocked_content_strings.grdp create mode 100644 chromium/components/blocklist/OWNERS create mode 100644 chromium/components/blocklist/README.md create mode 100644 chromium/components/blocklist/opt_out_blocklist/BUILD.gn create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_data.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_data.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_delegate.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_item.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_item.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_item_unittest.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_blocklist_unittest.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/opt_out_store.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/sql/BUILD.gn create mode 100644 chromium/components/blocklist/opt_out_blocklist/sql/DEPS create mode 100644 chromium/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.cc create mode 100644 chromium/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql.h create mode 100644 chromium/components/blocklist/opt_out_blocklist/sql/opt_out_store_sql_unittest.cc create mode 100644 chromium/components/browser_ui/android/bottomsheet/DEPS create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/BUILD.gn create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/res/layout/bottom_sheet.xml create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/res/values/dimens.xml create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/test/BUILD.gn create mode 100644 chromium/components/browser_ui/client_certificate/OWNERS create mode 100644 chromium/components/browser_ui/client_certificate/android/BUILD.gn create mode 100644 chromium/components/browser_ui/client_certificate/android/DEPS create mode 100644 chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java create mode 100644 chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java create mode 100644 chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.cc create mode 100644 chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.h create mode 100644 chromium/components/browser_ui/http_auth/android/BUILD.gn create mode 100644 chromium/components/browser_ui/http_auth/android/DEPS create mode 100644 chromium/components/browser_ui/http_auth/android/OWNERS create mode 100644 chromium/components/browser_ui/http_auth/android/java/res/OWNERS create mode 100644 chromium/components/browser_ui/http_auth/android/java/res/layout/http_auth_dialog.xml create mode 100644 chromium/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java create mode 100644 chromium/components/browser_ui/media/OWNERS create mode 100644 chromium/components/browser_ui/media/android/BUILD.gn create mode 100644 chromium/components/browser_ui/media/android/DEPS create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManager.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java create mode 100644 chromium/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java create mode 100644 chromium/components/browser_ui/settings/android/java/res/layout/settings_action_bar_shadow.xml create mode 100644 chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java create mode 100644 chromium/components/browser_ui/share/DEPS create mode 100644 chromium/components/browser_ui/share/OWNERS create mode 100644 chromium/components/browser_ui/share/android/BUILD.gn create mode 100644 chromium/components/browser_ui/share/android/java/res/layout/share_dialog_item.xml create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.java create mode 100644 chromium/components/browser_ui/site_settings/android/java/res/drawable/settings_bluetooth.xml delete mode 100644 chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java create mode 100644 chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/browser_ui_strings_grd/scheduled_for_later.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-hdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-mdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xxxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_security_grey.xml create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_update_grey.xml create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/StringUtils.java create mode 100644 chromium/components/browser_ui/webshare/OWNERS create mode 100644 chromium/components/browser_ui/webshare/android/BUILD.gn create mode 100644 chromium/components/browser_ui/webshare/android/DEPS create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java create mode 100644 chromium/components/cast_channel/README.md create mode 100644 chromium/components/cast_channel/cast_auth_util_fuzzer.cc create mode 100644 chromium/components/cast_channel/cast_framer_ingest_fuzzer.cc create mode 100644 chromium/components/cast_channel/cast_framer_serialize_fuzzer.cc delete mode 100644 chromium/components/cast_channel/cast_message_fuzzer.cc create mode 100644 chromium/components/cast_channel/cast_message_util_fuzzer.cc create mode 100644 chromium/components/cast_channel/fuzz.dict delete mode 100644 chromium/components/cast_channel/proto/authority_keys.proto delete mode 100644 chromium/components/cast_channel/proto/cast_channel.proto create mode 100644 chromium/components/cast_channel/proto/fuzzer_inputs.proto delete mode 100644 chromium/components/chromeos_camera/camera_app_helper_impl.cc delete mode 100644 chromium/components/chromeos_camera/camera_app_helper_impl.h delete mode 100644 chromium/components/chromeos_camera/common/camera_app_helper.mojom create mode 100644 chromium/components/components_strings_grd/IDS_OPTIONS_ADVANCED_SECTION_TITLE_SAFETY_CHECK.png.sha1 create mode 100644 chromium/components/content_settings/android/DEPS create mode 100644 chromium/components/content_settings/android/cookie_controls_bridge.cc create mode 100644 chromium/components/content_settings/android/cookie_controls_bridge.h create mode 100644 chromium/components/content_settings/android/java/src/org/chromium/components/content_settings/CookieControlsBridge.java create mode 100644 chromium/components/content_settings/android/java_templates/PrefNames.java.tmpl create mode 100644 chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.cc create mode 100644 chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.h create mode 100644 chromium/components/content_settings/browser/ui/BUILD.gn create mode 100644 chromium/components/content_settings/browser/ui/cookie_controls_controller.cc create mode 100644 chromium/components/content_settings/browser/ui/cookie_controls_controller.h create mode 100644 chromium/components/content_settings/browser/ui/cookie_controls_view.h create mode 100644 chromium/components/content_settings/core/browser/content_settings_provider.cc create mode 100644 chromium/components/data_use_measurement/core/data_use_pref_names.h create mode 100644 chromium/components/data_use_measurement/core/data_use_tracker_prefs.cc create mode 100644 chromium/components/data_use_measurement/core/data_use_tracker_prefs.h create mode 100644 chromium/components/data_use_measurement/core/data_use_tracker_prefs_unittest.cc delete mode 100644 chromium/components/dom_distiller/content/common/mojom/distiller_page_notifier_service.mojom delete mode 100644 chromium/components/dom_distiller/content/renderer/distiller_page_notifier_service_impl.cc delete mode 100644 chromium/components/dom_distiller/content/renderer/distiller_page_notifier_service_impl.h create mode 100644 chromium/components/download/public/common/download_schedule.cc create mode 100644 chromium/components/download/public/common/download_schedule.h create mode 100644 chromium/components/download/public/common/download_schedule_unittest.cc create mode 100644 chromium/components/embedder_support/origin_trials/features.cc create mode 100644 chromium/components/embedder_support/origin_trials/features.h create mode 100644 chromium/components/enterprise/BUILD.gn create mode 100644 chromium/components/enterprise/DEPS create mode 100644 chromium/components/enterprise/OWNERS create mode 100644 chromium/components/enterprise/browser/reporting/policy_info.cc create mode 100644 chromium/components/enterprise/browser/reporting/policy_info.h create mode 100644 chromium/components/enterprise/browser/reporting/report_request_definition.h create mode 100644 chromium/components/enterprise/common/BUILD.gn create mode 100644 chromium/components/enterprise/common/proto/BUILD.gn create mode 100644 chromium/components/enterprise/common/proto/connectors.proto create mode 100644 chromium/components/enterprise/common/strings.cc create mode 100644 chromium/components/enterprise/common/strings.h create mode 100644 chromium/components/exo/drag_drop_operation_unittest.cc create mode 100644 chromium/components/exo/server/BUILD.gn create mode 100644 chromium/components/exo/server/OWNERS create mode 100644 chromium/components/exo/server/wayland_server_controller.cc create mode 100644 chromium/components/exo/server/wayland_server_controller.h create mode 100644 chromium/components/exo/wayland/xdg_shell.cc create mode 100644 chromium/components/exo/wayland/xdg_shell.h create mode 100644 chromium/components/federated_learning/BUILD.gn create mode 100644 chromium/components/federated_learning/DEPS create mode 100644 chromium/components/federated_learning/OWNERS create mode 100644 chromium/components/federated_learning/README.md create mode 100644 chromium/components/federated_learning/floc_blocklist_service.cc create mode 100644 chromium/components/federated_learning/floc_blocklist_service.h create mode 100644 chromium/components/federated_learning/floc_blocklist_service_unittest.cc create mode 100644 chromium/components/federated_learning/floc_constants.cc create mode 100644 chromium/components/federated_learning/floc_constants.h create mode 100644 chromium/components/federated_learning/floc_id.cc create mode 100644 chromium/components/federated_learning/floc_id.h create mode 100644 chromium/components/federated_learning/proto/BUILD.gn create mode 100644 chromium/components/federated_learning/proto/blocklist.proto create mode 100644 chromium/components/federated_learning/sim_hash.cc create mode 100644 chromium/components/federated_learning/sim_hash.h create mode 100644 chromium/components/federated_learning/sim_hash_unittest.cc create mode 100644 chromium/components/feed/core/proto/v2/packing.proto create mode 100644 chromium/components/flags_ui/flags_ui_metrics.cc create mode 100644 chromium/components/flags_ui/flags_ui_metrics.h create mode 100644 chromium/components/infobars/android/BUILD.gn create mode 100644 chromium/components/infobars/android/DEPS create mode 100644 chromium/components/infobars/android/OWNERS create mode 100644 chromium/components/infobars/android/java/src/org/chromium/components/infobars/InfoBarControlLayout.java create mode 100644 chromium/components/infobars/android/java/src/org/chromium/components/infobars/InfoBarControlLayoutTest.java create mode 100644 chromium/components/infobars/android/java/src/org/chromium/components/infobars/InfoBarInteractionHandler.java create mode 100644 chromium/components/infobars/android/java/src/org/chromium/components/infobars/InfoBarLayout.java create mode 100644 chromium/components/infobars/android/java/src/org/chromium/components/infobars/InfoBarMessageView.java create mode 100644 chromium/components/infobars/android/res/drawable-hdpi/infobar_shadow_left.9.png create mode 100644 chromium/components/infobars/android/res/drawable-hdpi/infobar_shadow_top.png create mode 100644 chromium/components/infobars/android/res/drawable-mdpi/infobar_shadow_left.9.png create mode 100644 chromium/components/infobars/android/res/drawable-mdpi/infobar_shadow_top.png create mode 100644 chromium/components/infobars/android/res/drawable-xhdpi/infobar_shadow_left.9.png create mode 100644 chromium/components/infobars/android/res/drawable-xhdpi/infobar_shadow_top.png create mode 100644 chromium/components/infobars/android/res/drawable-xxhdpi/infobar_shadow_left.9.png create mode 100644 chromium/components/infobars/android/res/drawable-xxhdpi/infobar_shadow_top.png create mode 100644 chromium/components/infobars/android/res/drawable-xxxhdpi/infobar_shadow_left.9.png create mode 100644 chromium/components/infobars/android/res/drawable-xxxhdpi/infobar_shadow_top.png create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_icon_with_description.xml create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_message.xml create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_spinner.xml create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_spinner_drop_down.xml create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_spinner_view.xml create mode 100644 chromium/components/infobars/android/res/layout/infobar_control_toggle.xml create mode 100644 chromium/components/infobars/android/res/values/dimens.xml create mode 100644 chromium/components/infobars/android/res/values/ids.xml create mode 100644 chromium/components/infobars/content/BUILD.gn create mode 100644 chromium/components/infobars/content/DEPS create mode 100644 chromium/components/infobars/content/content_infobar_manager.cc create mode 100644 chromium/components/infobars/content/content_infobar_manager.h create mode 100644 chromium/components/js_injection/DEPS create mode 100644 chromium/components/js_injection/OWNERS create mode 100644 chromium/components/js_injection/README.md create mode 100644 chromium/components/js_injection/browser/BUILD.gn create mode 100644 chromium/components/js_injection/browser/js_communication_host.cc create mode 100644 chromium/components/js_injection/browser/js_communication_host.h create mode 100644 chromium/components/js_injection/browser/js_to_browser_messaging.cc create mode 100644 chromium/components/js_injection/browser/js_to_browser_messaging.h create mode 100644 chromium/components/js_injection/browser/web_message.cc create mode 100644 chromium/components/js_injection/browser/web_message.h create mode 100644 chromium/components/js_injection/browser/web_message_host.h create mode 100644 chromium/components/js_injection/browser/web_message_host_factory.h create mode 100644 chromium/components/js_injection/browser/web_message_reply_proxy.h create mode 100644 chromium/components/js_injection/common/BUILD.gn create mode 100644 chromium/components/js_injection/common/OWNERS create mode 100644 chromium/components/js_injection/common/interfaces.mojom create mode 100644 chromium/components/js_injection/common/origin_matcher.cc create mode 100644 chromium/components/js_injection/common/origin_matcher.h create mode 100644 chromium/components/js_injection/common/origin_matcher.mojom create mode 100644 chromium/components/js_injection/common/origin_matcher_internal.cc create mode 100644 chromium/components/js_injection/common/origin_matcher_internal.h create mode 100644 chromium/components/js_injection/common/origin_matcher_mojom_traits.cc create mode 100644 chromium/components/js_injection/common/origin_matcher_mojom_traits.h create mode 100644 chromium/components/js_injection/common/origin_matcher_unittest.cc create mode 100644 chromium/components/js_injection/renderer/BUILD.gn create mode 100644 chromium/components/js_injection/renderer/DEPS create mode 100644 chromium/components/js_injection/renderer/js_binding.cc create mode 100644 chromium/components/js_injection/renderer/js_binding.h create mode 100644 chromium/components/js_injection/renderer/js_communication.cc create mode 100644 chromium/components/js_injection/renderer/js_communication.h create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_CONNECTORS_EVENT.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_CONNECTORS_VISIBLE_DATA.png.sha1 delete mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_DATA_LOSS_PREVENTION_PERMISSIONS.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_EVENT.png.sha1 delete mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_VISIBLE_DATA.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_FILE_ATTACHED_EVENT.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_FILE_ATTACHED_VISIBLE_DATA.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_FILE_DOWNLOADED_EVENT.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_FILE_DOWNLOADED_VISIBLE_DATA.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_REPORT_APP_INFO_AND_ACTIVITY.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_TEXT_ENTERED_EVENT.png.sha1 create mode 100644 chromium/components/management_strings_grdp/IDS_MANAGEMENT_TEXT_ENTERED_VISIBLE_DATA.png.sha1 create mode 100644 chromium/components/metal_util/switches.cc create mode 100644 chromium/components/metal_util/switches.h create mode 100644 chromium/components/metrics/content/DEPS create mode 100644 chromium/components/metrics/content/gpu_metrics_provider.cc create mode 100644 chromium/components/metrics/content/gpu_metrics_provider.h create mode 100644 chromium/components/metrics/content/rendering_perf_metrics_provider.cc create mode 100644 chromium/components/metrics/content/rendering_perf_metrics_provider.h create mode 100644 chromium/components/metrics/content/subprocess_metrics_provider.cc create mode 100644 chromium/components/metrics/content/subprocess_metrics_provider.h create mode 100644 chromium/components/metrics/content/subprocess_metrics_provider_unittest.cc delete mode 100644 chromium/components/metrics/gpu/DEPS delete mode 100644 chromium/components/metrics/gpu/gpu_metrics_provider.cc delete mode 100644 chromium/components/metrics/gpu/gpu_metrics_provider.h delete mode 100644 chromium/components/metrics/gpu/rendering_perf_metrics_provider.cc delete mode 100644 chromium/components/metrics/gpu/rendering_perf_metrics_provider.h create mode 100644 chromium/components/metrics/log_decoder_unittest.cc delete mode 100644 chromium/components/metrics/net/wifi_access_point_info_provider.cc delete mode 100644 chromium/components/metrics/net/wifi_access_point_info_provider.h delete mode 100644 chromium/components/metrics/net/wifi_access_point_info_provider_chromeos.cc delete mode 100644 chromium/components/metrics/net/wifi_access_point_info_provider_chromeos.h create mode 100644 chromium/components/metrics/unsent_log_store_metrics.cc create mode 100644 chromium/components/metrics/unsent_log_store_metrics_impl_unittest.cc create mode 100644 chromium/components/offline_items_collection/core/android/java/src/org/chromium/components/offline_items_collection/OfflineItemSchedule.java create mode 100644 chromium/components/offline_items_collection/core/android/native_java_unittests/src/org/chromium/components/offline_items_collection/bridges/OfflineItemBridgeUnitTest.java create mode 100644 chromium/components/offline_items_collection/core/android/offline_item_bridge_unittest.cc create mode 100644 chromium/components/offline_items_collection/core/offline_item_unittest.cc delete mode 100644 chromium/components/open_from_clipboard/clipboard_recent_content_features.cc delete mode 100644 chromium/components/open_from_clipboard/clipboard_recent_content_features.h create mode 100644 chromium/components/page_info/android/java/res/layout/page_info_subpage.xml create mode 100644 chromium/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoConnectionController.java create mode 100644 chromium/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java create mode 100644 chromium/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java create mode 100644 chromium/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoSubpage.java create mode 100644 chromium/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoSubpageController.java create mode 100644 chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_DELETE_HID_DEVICE.png.sha1 create mode 100644 chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_HID_DEVICE_SECONDARY_LABEL.png.sha1 create mode 100644 chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_TYPE_HID.png.sha1 create mode 100644 chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc create mode 100644 chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h create mode 100644 chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.cc create mode 100644 chromium/components/paint_preview/browser/service_sandbox_type.h create mode 100644 chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java create mode 100644 chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java create mode 100644 chromium/components/password_manager/core/browser/bulk_leak_check_service_interface.cc create mode 100644 chromium/components/password_manager/core/browser/bulk_leak_check_service_interface.h create mode 100644 chromium/components/password_manager/core/browser/export/OWNERS create mode 100644 chromium/components/password_manager/core/browser/mock_bulk_leak_check_service.cc create mode 100644 chromium/components/password_manager/core/browser/mock_bulk_leak_check_service.h create mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_manager.cc create mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_manager.h create mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_manager_unittest.cc delete mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_provider.cc delete mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_provider.h delete mode 100644 chromium/components/password_manager/core/browser/ui/compromised_credentials_provider_unittest.cc create mode 100644 chromium/components/password_manager/core/browser/ui/post_save_compromised_helper.cc create mode 100644 chromium/components/password_manager/core/browser/ui/post_save_compromised_helper.h create mode 100644 chromium/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc delete mode 100644 chromium/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION.png.sha1 create mode 100644 chromium/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_RE_SIGNIN_ACCOUNT_STORE.png.sha1 create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/IPaymentDetailsUpdateService.aidl create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback.aidl create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/JniPaymentApp.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/JourneyLogger.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/MojoStructCollection.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PackageManagerDelegate.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateService.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateServiceHelper.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestSpec.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestUpdateEventListener.java create mode 100644 chromium/components/payments/content/android/java/src/org/chromium/components/payments/payment_details_update_service.aidl create mode 100644 chromium/components/payments/content/android/jni_payment_app.cc create mode 100644 chromium/components/payments/content/android/jni_payment_app.h create mode 100644 chromium/components/payments/content/android/payment_feature_list.cc create mode 100644 chromium/components/payments/content/android/payment_feature_list.h create mode 100644 chromium/components/payments/content/android/payment_request_spec.cc create mode 100644 chromium/components/payments/content/android/payment_request_spec.h create mode 100644 chromium/components/payments/content/android/payment_request_update_event_listener.cc create mode 100644 chromium/components/payments/content/android/payment_request_update_event_listener.h create mode 100644 chromium/components/payments/content/autofill_payment_app.cc create mode 100644 chromium/components/payments/content/autofill_payment_app.h create mode 100644 chromium/components/payments/content/autofill_payment_app_unittest.cc create mode 100644 chromium/components/payments/content/payment_app.cc create mode 100644 chromium/components/payments/content/payment_app.h delete mode 100644 chromium/components/payments/core/autofill_payment_app.cc delete mode 100644 chromium/components/payments/core/autofill_payment_app.h delete mode 100644 chromium/components/payments/core/autofill_payment_app_unittest.cc delete mode 100644 chromium/components/payments/core/mock_payment_request_delegate.cc delete mode 100644 chromium/components/payments/core/mock_payment_request_delegate.h delete mode 100644 chromium/components/payments/core/payment_app.cc delete mode 100644 chromium/components/payments/core/payment_app.h create mode 100644 chromium/components/payments/core/payment_request_delegate.cc create mode 100644 chromium/components/pdf_strings_grdp/IDS_PDF_DOWNLOAD_EDITED.png.sha1 create mode 100644 chromium/components/pdf_strings_grdp/IDS_PDF_DOWNLOAD_ORIGINAL.png.sha1 create mode 100644 chromium/components/performance_manager/decorators/site_data_recorder.cc create mode 100644 chromium/components/performance_manager/decorators/site_data_recorder.h create mode 100644 chromium/components/performance_manager/decorators/site_data_recorder_unittest.cc create mode 100644 chromium/components/performance_manager/decorators/v8_per_frame_memory_decorator.cc create mode 100644 chromium/components/performance_manager/decorators/v8_per_frame_memory_decorator_unittest.cc create mode 100644 chromium/components/performance_manager/owned_objects.h create mode 100644 chromium/components/performance_manager/owned_objects_unittest.cc create mode 100644 chromium/components/performance_manager/public/decorators/v8_per_frame_memory_decorator.h create mode 100644 chromium/components/performance_manager/public/performance_manager_owned.h create mode 100644 chromium/components/performance_manager/public/performance_manager_registered.h create mode 100644 chromium/components/performance_manager/registered_objects.h create mode 100644 chromium/components/performance_manager/registered_objects_unittest.cc create mode 100644 chromium/components/permissions/contexts/OWNERS create mode 100644 chromium/components/permissions/contexts/webxr_permission_context.cc create mode 100644 chromium/components/permissions/contexts/webxr_permission_context.h create mode 100644 chromium/components/permissions_strings_grdp/IDS_AR_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_CLIPBOARD_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_GEOLOCATION_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_CHIP.png.sha1 delete mode 100644 chromium/components/permissions_strings_grdp/IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_ONLY_PERMISSION_FRAGMENT.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_PERMISSION_FRAGMENT.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_MEDIA_CAPTURE_VIDEO_AND_AUDIO_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_MIDI_SYSEX_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_NOTIFICATION_PERMISSIONS_CHIP.png.sha1 create mode 100644 chromium/components/permissions_strings_grdp/IDS_VR_PERMISSION_CHIP.png.sha1 create mode 100644 chromium/components/policy_strings_grdp/IDS_COPY_POLICIES_JSON.png.sha1 create mode 100644 chromium/components/policy_strings_grdp/IDS_POLICY_COPY_VALUE.png.sha1 create mode 100644 chromium/components/policy_strings_grdp/IDS_POLICY_INVALID.png.sha1 create mode 100644 chromium/components/policy_strings_grdp/IDS_POLICY_LABEL_FUTURE.png.sha1 create mode 100644 chromium/components/prefs/android/BUILD.gn create mode 100644 chromium/components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java create mode 100644 chromium/components/prefs/android/pref_service_android.cc create mode 100644 chromium/components/prefs/android/pref_service_android.h create mode 100644 chromium/components/prerender/OWNERS create mode 100644 chromium/components/prerender/common/BUILD.gn create mode 100644 chromium/components/prerender/common/DEPS create mode 100644 chromium/components/prerender/common/OWNERS create mode 100644 chromium/components/prerender/common/prerender_canceler.mojom create mode 100644 chromium/components/prerender/common/prerender_final_status.cc create mode 100644 chromium/components/prerender/common/prerender_final_status.h create mode 100644 chromium/components/prerender/common/prerender_messages.h create mode 100644 chromium/components/prerender/common/prerender_origin.cc create mode 100644 chromium/components/prerender/common/prerender_origin.h create mode 100644 chromium/components/prerender/common/prerender_types.mojom delete mode 100644 chromium/components/previews/core/previews_black_list.cc delete mode 100644 chromium/components/previews/core/previews_black_list.h delete mode 100644 chromium/components/previews/core/previews_black_list_unittest.cc create mode 100644 chromium/components/previews/core/previews_block_list.cc create mode 100644 chromium/components/previews/core/previews_block_list.h create mode 100644 chromium/components/previews/core/previews_block_list_unittest.cc create mode 100644 chromium/components/printing/browser/service_sandbox_type.h create mode 100644 chromium/components/query_tiles/internal/black_hole_log_sink.cc create mode 100644 chromium/components/query_tiles/internal/black_hole_log_sink.h create mode 100644 chromium/components/query_tiles/internal/log_sink.h create mode 100644 chromium/components/query_tiles/internal/log_source.h create mode 100644 chromium/components/query_tiles/internal/logger_impl.cc create mode 100644 chromium/components/query_tiles/internal/logger_impl.h create mode 100644 chromium/components/query_tiles/logger.h create mode 100644 chromium/components/resources/android/blocked_content_resource_id.h create mode 100644 chromium/components/resources/default_100_percent/sadplugin.png create mode 100644 chromium/components/resources/default_100_percent/webview-crash.png create mode 100644 chromium/components/resources/default_200_percent/sadplugin.png create mode 100644 chromium/components/resources/default_200_percent/webview-crash.png create mode 100644 chromium/components/safe_browsing/core/realtime/url_lookup_service_base.cc create mode 100644 chromium/components/safe_browsing/core/realtime/url_lookup_service_base.h create mode 100644 chromium/components/safe_browsing/ios/browser/BUILD.gn create mode 100644 chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h create mode 100644 chromium/components/safe_browsing/ios/browser/safe_browsing_url_allow_list.mm delete mode 100644 chromium/components/search_provider_logos/features.cc delete mode 100644 chromium/components/search_provider_logos/features.h create mode 100644 chromium/components/security_interstitials/content/insecure_form_blocking_page.cc create mode 100644 chromium/components/security_interstitials/content/insecure_form_blocking_page.h create mode 100644 chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc create mode 100644 chromium/components/security_interstitials/content/insecure_form_navigation_throttle.h create mode 100644 chromium/components/security_interstitials/core/browser/resources/interstitial_insecureform.css create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_BACK_BUTTON.png.sha1 create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_HEADING.png.sha1 create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_PRIMARY_PARAGRAPH.png.sha1 create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_SUBMIT_BUTTON.png.sha1 create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_INSECURE_FORM_TITLE.png.sha1 create mode 100644 chromium/components/security_interstitials_strings_grdp/IDS_LOOKALIKE_URL_BACK_TO_SAFETY.png.sha1 delete mode 100644 chromium/components/security_state/core/security_state_pref_names.cc delete mode 100644 chromium/components/security_state/core/security_state_pref_names.h delete mode 100644 chromium/components/send_tab_to_self/send_tab_to_self_metrics.cc delete mode 100644 chromium/components/send_tab_to_self/send_tab_to_self_metrics.h create mode 100644 chromium/components/services/app_service/BUILD.gn create mode 100644 chromium/components/services/app_service/DEPS create mode 100644 chromium/components/services/app_service/app_service_impl.cc create mode 100644 chromium/components/services/app_service/app_service_impl.h create mode 100644 chromium/components/services/app_service/app_service_impl_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/app_registry_cache.cc create mode 100644 chromium/components/services/app_service/public/cpp/app_registry_cache.h create mode 100644 chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/app_registry_cache_wrapper.cc create mode 100644 chromium/components/services/app_service/public/cpp/app_registry_cache_wrapper.h create mode 100644 chromium/components/services/app_service/public/cpp/app_update.cc create mode 100644 chromium/components/services/app_service/public/cpp/app_update.h create mode 100644 chromium/components/services/app_service/public/cpp/app_update_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_cache.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_cache.h create mode 100644 chromium/components/services/app_service/public/cpp/icon_cache_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_coalescer.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_coalescer.h create mode 100644 chromium/components/services/app_service/public/cpp/icon_coalescer_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_loader.cc create mode 100644 chromium/components/services/app_service/public/cpp/icon_loader.h create mode 100644 chromium/components/services/app_service/public/cpp/instance.cc create mode 100644 chromium/components/services/app_service/public/cpp/instance.h create mode 100644 chromium/components/services/app_service/public/cpp/instance_registry.cc create mode 100644 chromium/components/services/app_service/public/cpp/instance_registry.h create mode 100644 chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/instance_update.cc create mode 100644 chromium/components/services/app_service/public/cpp/instance_update.h create mode 100644 chromium/components/services/app_service/public/cpp/instance_update_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/intent_filter_util.cc create mode 100644 chromium/components/services/app_service/public/cpp/intent_filter_util.h create mode 100644 chromium/components/services/app_service/public/cpp/intent_test_util.cc create mode 100644 chromium/components/services/app_service/public/cpp/intent_test_util.h create mode 100644 chromium/components/services/app_service/public/cpp/intent_util_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_converter.h create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_list.cc create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_list.h create mode 100644 chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc create mode 100644 chromium/components/services/app_service/public/cpp/publisher_base.cc create mode 100644 chromium/components/services/app_service/public/cpp/publisher_base.h create mode 100644 chromium/components/services/app_service/public/cpp/stub_icon_loader.cc create mode 100644 chromium/components/services/app_service/public/cpp/stub_icon_loader.h create mode 100644 chromium/components/services/app_service/public/mojom/app_service.mojom create mode 100644 chromium/components/services/paint_preview_compositor/skp_result.cc create mode 100644 chromium/components/services/paint_preview_compositor/skp_result.h create mode 100644 chromium/components/services/storage/public/mojom/blob_storage_context.mojom create mode 100644 chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccessTokenData.java create mode 100644 chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc create mode 100644 chromium/components/site_isolation/features.cc create mode 100644 chromium/components/site_isolation/features.h create mode 100644 chromium/components/site_isolation/pref_names.cc create mode 100644 chromium/components/site_isolation/pref_names.h create mode 100644 chromium/components/site_isolation/site_isolation_policy.cc create mode 100644 chromium/components/site_isolation/site_isolation_policy.h create mode 100644 chromium/components/site_isolation/site_isolation_policy_unittest.cc create mode 100644 chromium/components/speech/BUILD.gn create mode 100644 chromium/components/speech/DEPS create mode 100644 chromium/components/speech/downstream_loader.cc create mode 100644 chromium/components/speech/downstream_loader.h create mode 100644 chromium/components/speech/downstream_loader_client.h create mode 100644 chromium/components/speech/upstream_loader.cc create mode 100644 chromium/components/speech/upstream_loader.h create mode 100644 chromium/components/speech/upstream_loader_client.h create mode 100644 chromium/components/translate/content/android/BUILD.gn create mode 100644 chromium/components/ukm/field_trials_provider_helper.cc create mode 100644 chromium/components/ukm/field_trials_provider_helper.h create mode 100644 chromium/components/user_prefs/android/BUILD.gn create mode 100644 chromium/components/user_prefs/android/DEPS create mode 100644 chromium/components/user_prefs/android/java/src/org/chromium/components/user_prefs/UserPrefs.java create mode 100644 chromium/components/user_prefs/android/user_prefs_android.cc delete mode 100644 chromium/components/vector_icons/camera_pan_tilt_zoom.icon create mode 100644 chromium/components/viz/common/delegated_ink_metadata.h create mode 100644 chromium/components/viz/common/resources/resource_format_utils_mac.mm create mode 100644 chromium/components/viz/service/display_embedder/output_presenter.cc create mode 100644 chromium/components/viz/service/display_embedder/output_presenter.h create mode 100644 chromium/components/viz/service/display_embedder/output_presenter_fuchsia.cc create mode 100644 chromium/components/viz/service/display_embedder/output_presenter_fuchsia.h create mode 100644 chromium/components/viz/service/display_embedder/output_presenter_gl.cc create mode 100644 chromium/components/viz/service/display_embedder/output_presenter_gl.h create mode 100644 chromium/components/webapk/OWNERS create mode 100644 chromium/components/webapk/android/libs/client/BUILD.gn create mode 100644 chromium/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/ChromeWebApkHostSignature.java create mode 100644 chromium/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java create mode 100644 chromium/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java create mode 100644 chromium/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkVerifySignature.java create mode 100644 chromium/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkVerifySignatureTest.java create mode 100644 chromium/components/webapk/android/libs/common/BUILD.gn create mode 100644 chromium/components/webapk/android/libs/common/src/org/chromium/components/webapk/lib/common/WebApkConstants.java create mode 100644 chromium/components/webapk/android/libs/common/src/org/chromium/components/webapk/lib/common/WebApkMetaDataKeys.java (limited to 'chromium/components') diff --git a/chromium/components/BUILD.gn b/chromium/components/BUILD.gn index 6a77bf77979..21add9fa1f4 100644 --- a/chromium/components/BUILD.gn +++ b/chromium/components/BUILD.gn @@ -60,8 +60,8 @@ test("components_unittests") { "//components/autofill/core/browser:unit_tests", "//components/autofill/core/common:unit_tests", "//components/base32:unit_tests", - "//components/blacklist/opt_out_blacklist:unit_tests", - "//components/blacklist/opt_out_blacklist/sql:unit_tests", + "//components/blocklist/opt_out_blocklist:unit_tests", + "//components/blocklist/opt_out_blocklist/sql:unit_tests", "//components/bookmarks/browser:unit_tests", "//components/bookmarks/managed:unit_tests", "//components/browsing_data/core:unit_tests", @@ -84,6 +84,7 @@ test("components_unittests") { "//components/favicon/core:unit_tests", "//components/favicon_base:unit_tests", "//components/feature_engagement:unit_tests", + "//components/federated_learning:unit_tests", "//components/feed:unit_tests", "//components/filename_generation:unit_tests", "//components/flags_ui:unit_tests", @@ -220,6 +221,7 @@ test("components_unittests") { "//components/autofill/content/browser:unit_tests", "//components/autofill/content/renderer:unit_tests", "//components/autofill/core/common/mojom:unit_tests", + "//components/blocked_content:unit_tests", "//components/browsing_data/content:unit_tests", "//components/captive_portal/content:unit_tests", "//components/cast_certificate:unit_tests", @@ -240,6 +242,7 @@ test("components_unittests") { "//components/history/content/browser:unit_tests", "//components/invalidation/impl:unit_tests", "//components/javascript_dialogs:unit_tests", + "//components/js_injection/common:unit_tests", "//components/keyed_service/content:unit_tests", "//components/language/content/browser:unit_tests", "//components/link_header_util:unit_tests", @@ -275,6 +278,7 @@ test("components_unittests") { "//components/services/paint_preview_compositor:unit_tests", "//components/services/quarantine:unit_tests", "//components/services/storage:tests", + "//components/site_isolation:unit_tests", "//components/spellcheck/browser:unit_tests", "//components/spellcheck/renderer:unit_tests", "//components/subresource_filter/content/browser:unit_tests", @@ -328,6 +332,7 @@ test("components_unittests") { "//components/gcm_driver/instance_id/android:instance_id_driver_java", "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java", "//components/invalidation/impl", + "//components/offline_items_collection/core:native_java_unittests_java", "//components/paint_preview/browser/android:java", "//components/paint_preview/browser/android:unit_tests", "//components/paint_preview/player/android:unit_tests", @@ -537,6 +542,7 @@ if (!is_ios && !is_fuchsia) { "autofill/content/renderer/field_data_manager_browsertest.cc", "autofill/content/renderer/form_autofill_util_browsertest.cc", "autofill/content/renderer/form_cache_browsertest.cc", + "autofill/content/renderer/html_based_username_detector_browsertest.cc", "autofill/content/renderer/password_form_conversion_utils_browsertest.cc", "browsing_data/content/browsing_data_helper_browsertest.h", "browsing_data/content/cache_storage_helper_browsertest.cc", @@ -743,8 +749,14 @@ if (!is_ios && !is_fuchsia) { if (is_android) { junit_binary("components_junit_tests") { deps = [ - "//components/autofill/android/junit:components_autofill_junit_tests", + "//components/autofill/android/provider/junit:components_autofill_junit_tests", "//components/background_task_scheduler:components_background_task_scheduler_junit_tests", + "//components/browser_ui/android/bottomsheet/internal:junit_tests", + "//components/browser_ui/client_certificate/android:junit", + "//components/browser_ui/media/android:junit", + "//components/browser_ui/util/android:junit", + "//components/browser_ui/webshare/android:junit", + "//components/browser_ui/widget/android:junit", "//components/embedder_support/android:components_embedder_support_junit_tests", "//components/gcm_driver/android:components_gcm_driver_junit_tests", "//components/permissions/android:components_permissions_junit_tests", @@ -752,6 +764,7 @@ if (is_android) { "//components/query_tiles:query_tiles_junit_tests", "//components/signin/core/browser/android:components_signin_junit_tests", "//components/variations/android:components_variations_junit_tests", + "//components/webapk/android/libs/client:junit", ] } } diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS index 9955d15d774..9c774c6881e 100644 --- a/chromium/components/OWNERS +++ b/chromium/components/OWNERS @@ -54,3 +54,7 @@ per-file *.xtb=file://tools/translation/TRANSLATION_OWNERS # making structural changes, please get a review from one of the overall # components OWNERS. per-file BUILD.gn=* + +# Service sandbox specialization must be reviewed by SECURITY_OWNERS +per-file service_sandbox_type.h=set noparent +per-file service_sandbox_type.h=file://ipc/SECURITY_OWNERS \ No newline at end of file diff --git a/chromium/components/about_ui/resources/about_credits.js b/chromium/components/about_ui/resources/about_credits.js index 3e1b49bc743..b7c981db5f7 100644 --- a/chromium/components/about_ui/resources/about_credits.js +++ b/chromium/components/about_ui/resources/about_credits.js @@ -18,7 +18,8 @@ document.addEventListener('DOMContentLoaded', function() { }, }); - // TODO remove an empty string argument once supported + // TODO(Jun.Kokatsu@microsoft.com): remove an empty string argument + // once supported. // https://github.com/w3c/webappsec-trusted-types/issues/278 keyboardUtils.src = staticURLPolicy.createScriptURL(''); document.body.appendChild(keyboardUtils); diff --git a/chromium/components/account_id/account_id.cc b/chromium/components/account_id/account_id.cc index 86634b72b04..ddb4c099645 100644 --- a/chromium/components/account_id/account_id.cc +++ b/chromium/components/account_id/account_id.cc @@ -11,6 +11,7 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "base/strings/string_util.h" #include "base/values.h" #include "google_apis/gaia/gaia_auth_util.h" diff --git a/chromium/components/arc/BUILD.gn b/chromium/components/arc/BUILD.gn index 2aa5e37f350..bab7b96d2ed 100644 --- a/chromium/components/arc/BUILD.gn +++ b/chromium/components/arc/BUILD.gn @@ -34,6 +34,8 @@ static_library("arc") { "intent_helper/arc_intent_helper_bridge.h", "intent_helper/arc_intent_helper_observer.h", "intent_helper/control_camera_app_delegate.h", + "intent_helper/custom_tab.cc", + "intent_helper/custom_tab.h", "intent_helper/factory_reset_delegate.h", "intent_helper/intent_constants.cc", "intent_helper/intent_constants.h", @@ -108,7 +110,7 @@ static_library("arc") { # TODO(crbug.com/853604): After fully migrating the intent picker to query # directly from App Service, we will deprecated the match functionality # in intent_filter and this dependency will be removed. - "//components/services/app_service/public/cpp:intent_util", + "//components/services/app_service/public/cpp:intents", "//components/session_manager/core", "//components/timers", "//components/url_formatter", @@ -231,6 +233,7 @@ static_library("arc_base") { deps = [ "//ash/public/cpp", + "//ash/public/cpp/external_arc", "//base", "//chromeos/constants", "//chromeos/cryptohome", diff --git a/chromium/components/arc/DEPS b/chromium/components/arc/DEPS index 06ecd3447d5..3899f4e8900 100644 --- a/chromium/components/arc/DEPS +++ b/chromium/components/arc/DEPS @@ -26,6 +26,7 @@ include_rules = [ "+third_party/skia", "+ui/base", "+ui/display", + "+ui/events", "+ui/gfx/geometry", "+ui/gfx/range/range.h", ] diff --git a/chromium/components/arc/arc_features.cc b/chromium/components/arc/arc_features.cc index 86fe498c989..8f9fb29823d 100644 --- a/chromium/components/arc/arc_features.cc +++ b/chromium/components/arc/arc_features.cc @@ -44,6 +44,11 @@ const base::Feature kEnableDocumentsProviderInFilesAppFeature{ const base::Feature kEnableRegularToChildTransitionFeature{ "ArcEnableRegularToChildTransition", base::FEATURE_ENABLED_BY_DEFAULT}; +// Controls whether secondary accounts are added to ARC++ for child user. +// This is added temporarily to allow further investigation. +const base::Feature kEnableSecondaryAccountsForChildExperiment{ + "ArcEnableSecondaryAccountForChild", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether we should delegate audio focus requests from ARC to Chrome. const base::Feature kEnableUnifiedAudioFocusFeature{ "ArcEnableUnifiedAudioFocus", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -56,7 +61,7 @@ const base::Feature kFilePickerExperimentFeature{ // Note, that we keep the original feature name to preserve // corresponding metrics. const base::Feature kNativeBridgeToggleFeature{ - "ArcNativeBridgeExperiment", base::FEATURE_ENABLED_BY_DEFAULT}; + "ArcNativeBridgeExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls ARC picture-in-picture feature. If this is enabled, then Android // will control which apps can enter PIP. If this is disabled, then ARC PIP diff --git a/chromium/components/arc/arc_features.h b/chromium/components/arc/arc_features.h index 7e1a6daac53..08b58f7fdf0 100644 --- a/chromium/components/arc/arc_features.h +++ b/chromium/components/arc/arc_features.h @@ -20,6 +20,7 @@ extern const base::Feature kEnableApplicationZoomFeature; extern const base::Feature kEnableChildToRegularTransitionFeature; extern const base::Feature kEnableDocumentsProviderInFilesAppFeature; extern const base::Feature kEnableRegularToChildTransitionFeature; +extern const base::Feature kEnableSecondaryAccountsForChildExperiment; extern const base::Feature kEnableUnifiedAudioFocusFeature; extern const base::Feature kFilePickerExperimentFeature; extern const base::Feature kNativeBridgeToggleFeature; diff --git a/chromium/components/arc/arc_features_parser.cc b/chromium/components/arc/arc_features_parser.cc index 9dfc871dc0f..75ec90b1ed7 100644 --- a/chromium/components/arc/arc_features_parser.cc +++ b/chromium/components/arc/arc_features_parser.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" +#include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -30,19 +31,16 @@ constexpr const base::FilePath::CharType kArcFeaturesJsonFile[] = base::Optional ParseFeaturesJson(base::StringPiece input_json) { ArcFeatures arc_features; - int error_code; - std::string error_msg; - std::unique_ptr json_value = - base::JSONReader::ReadAndReturnErrorDeprecated( - input_json, base::JSON_PARSE_RFC, &error_code, &error_msg); - if (!json_value || !json_value->is_dict()) { - LOG(ERROR) << "Error parsing feature JSON: " << error_msg; + base::JSONReader::ValueWithError parsed_json = + base::JSONReader::ReadAndReturnValueWithError(input_json); + if (!parsed_json.value || !parsed_json.value->is_dict()) { + LOG(ERROR) << "Error parsing feature JSON: " << parsed_json.error_message; return base::nullopt; } // Parse each item under features. const base::Value* feature_list = - json_value->FindKeyOfType("features", base::Value::Type::LIST); + parsed_json.value->FindKeyOfType("features", base::Value::Type::LIST); if (!feature_list) { LOG(ERROR) << "No feature list in JSON."; return base::nullopt; @@ -65,8 +63,9 @@ base::Optional ParseFeaturesJson(base::StringPiece input_json) { } // Parse each item under unavailable_features. - const base::Value* unavailable_feature_list = json_value->FindKeyOfType( - "unavailable_features", base::Value::Type::LIST); + const base::Value* unavailable_feature_list = + parsed_json.value->FindKeyOfType("unavailable_features", + base::Value::Type::LIST); if (!unavailable_feature_list) { LOG(ERROR) << "No unavailable feature list in JSON."; return base::nullopt; @@ -85,8 +84,8 @@ base::Optional ParseFeaturesJson(base::StringPiece input_json) { } // Parse each item under properties. - const base::Value* properties = - json_value->FindKeyOfType("properties", base::Value::Type::DICTIONARY); + const base::Value* properties = parsed_json.value->FindKeyOfType( + "properties", base::Value::Type::DICTIONARY); if (!properties) { LOG(ERROR) << "No properties in JSON."; return base::nullopt; @@ -101,7 +100,7 @@ base::Optional ParseFeaturesJson(base::StringPiece input_json) { } // Parse the Play Store version - const base::Value* play_version = json_value->FindKeyOfType( + const base::Value* play_version = parsed_json.value->FindKeyOfType( "play_store_version", base::Value::Type::STRING); if (!play_version) { LOG(ERROR) << "No Play Store version in JSON."; diff --git a/chromium/components/arc/arc_util.cc b/chromium/components/arc/arc_util.cc index 7b0e268efe2..ddc04468c0e 100644 --- a/chromium/components/arc/arc_util.cc +++ b/chromium/components/arc/arc_util.cc @@ -11,6 +11,9 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" #include "base/optional.h" #include "base/strings/string_number_conversions.h" #include "chromeos/constants/chromeos_switches.h" @@ -303,7 +306,6 @@ bool IsArcPlayAutoInstallDisabled() { chromeos::switches::kArcDisablePlayAutoInstall); } -// static int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) { const auto* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(chromeos::switches::kArcScale)) { @@ -333,4 +335,26 @@ int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) { kDefaultDensityDpi); } +bool GenerateFirstStageFstab(const base::FilePath& combined_property_file_name, + const base::FilePath& fstab_path) { + DCHECK(IsArcVmEnabled()); + // The file is exposed to the guest by crosvm via /sys/firmware/devicetree, + // which in turn allows the guest's init process to mount /vendor very early, + // in its first stage (device) initialization step. crosvm also special-cases + // #dt-vendor line and expose |combined_property_file_name| via the device + // tree file system too. This also allow the init process to load the expanded + // properties very early even before all file systems are mounted. + // + // The device name for /vendor has to match what arc_vm_client_adapter.cc + // configures. + constexpr const char kFirstStageFstabTemplate[] = + "/dev/block/vdb /vendor squashfs ro,noatime,nosuid,nodev " + "wait,check,formattable,reservedsize=128M\n" + "#dt-vendor build.prop %s default default\n"; + return base::WriteFile( + fstab_path, + base::StringPrintf(kFirstStageFstabTemplate, + combined_property_file_name.value().c_str())); +} + } // namespace arc diff --git a/chromium/components/arc/arc_util.h b/chromium/components/arc/arc_util.h index 644b63499de..da0fe3d4cd0 100644 --- a/chromium/components/arc/arc_util.h +++ b/chromium/components/arc/arc_util.h @@ -18,6 +18,7 @@ class Window; namespace base { class CommandLine; +class FilePath; } // namespace base namespace user_manager { @@ -149,6 +150,12 @@ void SetArcCpuRestriction(CpuRestrictionState cpu_restriction_state); // factor used on chrome. int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor); +// Generates a file called first stage fstab at |fstab_path| which is exported +// by crosvm to the guest via the device tree so the guest can read certain +// files in its init's first stage. +bool GenerateFirstStageFstab(const base::FilePath& combined_property_file_name, + const base::FilePath& fstab_path); + } // namespace arc #endif // COMPONENTS_ARC_ARC_UTIL_H_ diff --git a/chromium/components/arc/arc_util_unittest.cc b/chromium/components/arc/arc_util_unittest.cc index 1dd651d631c..165f40d04b6 100644 --- a/chromium/components/arc/arc_util_unittest.cc +++ b/chromium/components/arc/arc_util_unittest.cc @@ -10,6 +10,8 @@ #include "ash/public/cpp/app_types.h" #include "base/base_switches.h" #include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/test/scoped_feature_list.h" @@ -275,5 +277,32 @@ TEST_F(ArcUtilTest, ScaleFactorToDensity) { EXPECT_EQ(240, GetLcdDensityForDeviceScaleFactor(2.0)); } +TEST_F(ArcUtilTest, GenerateFirstStageFstab) { + constexpr const char kFakeCombinedBuildPropPath[] = "/path/to/build.prop"; + constexpr const char kAnotherFakeCombinedBuildPropPath[] = + "/foo/bar/baz.prop"; + + auto* command_line = base::CommandLine::ForCurrentProcess(); + command_line->InitFromArgv({"", "--enable-arcvm"}); + + std::string content; + base::ScopedTempDir dir; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + const base::FilePath fstab(dir.GetPath().Append("fstab")); + + // Generate the fstab and verify the content. + EXPECT_TRUE(GenerateFirstStageFstab( + base::FilePath(kFakeCombinedBuildPropPath), fstab)); + EXPECT_TRUE(base::ReadFileToString(fstab, &content)); + EXPECT_NE(std::string::npos, content.find(kFakeCombinedBuildPropPath)); + + // Generate the fstab again with the other prop file and verify the content. + EXPECT_TRUE(GenerateFirstStageFstab( + base::FilePath(kAnotherFakeCombinedBuildPropPath), fstab)); + EXPECT_TRUE(base::ReadFileToString(fstab, &content)); + EXPECT_EQ(std::string::npos, content.find(kFakeCombinedBuildPropPath)); + EXPECT_NE(std::string::npos, content.find(kAnotherFakeCombinedBuildPropPath)); +} + } // namespace } // namespace arc diff --git a/chromium/components/arc/camera/arc_camera_bridge.cc b/chromium/components/arc/camera/arc_camera_bridge.cc index bae4746abb7..59588699993 100644 --- a/chromium/components/arc/camera/arc_camera_bridge.cc +++ b/chromium/components/arc/camera/arc_camera_bridge.cc @@ -16,6 +16,7 @@ #include "components/arc/session/arc_bridge_service.h" #include "crypto/random.h" #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/invitation.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -53,9 +54,10 @@ class ArcCameraBridge::PendingStartCameraServiceResult { mojo::ScopedMessagePipeHandle pipe, ArcCameraBridge::StartCameraServiceCallback callback) : owner_(owner), - service_(mojom::CameraServicePtrInfo(std::move(pipe), 0u)), + service_( + mojo::PendingRemote(std::move(pipe), 0u)), callback_(std::move(callback)) { - service_.set_connection_error_handler( + service_.set_disconnect_handler( base::BindOnce(&PendingStartCameraServiceResult::OnError, weak_ptr_factory_.GetWeakPtr())); service_.QueryVersion( @@ -78,13 +80,13 @@ class ArcCameraBridge::PendingStartCameraServiceResult { // Runs the callback and removes this object from the owner. void Finish() { DCHECK(callback_); - std::move(callback_).Run(std::move(service_)); + std::move(callback_).Run(service_.Unbind()); // Destructs |this|. owner_->pending_start_camera_service_results_.erase(this); } ArcCameraBridge* const owner_; - mojom::CameraServicePtr service_; + mojo::Remote service_; ArcCameraBridge::StartCameraServiceCallback callback_; base::WeakPtrFactory weak_ptr_factory_{this}; diff --git a/chromium/components/arc/ime/arc_ime_service.cc b/chromium/components/arc/ime/arc_ime_service.cc index 7913a6baa35..4718428b020 100644 --- a/chromium/components/arc/ime/arc_ime_service.cc +++ b/chromium/components/arc/ime/arc_ime_service.cc @@ -7,14 +7,17 @@ #include #include "ash/keyboard/ui/keyboard_ui_controller.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/memory/singleton.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "chromeos/constants/chromeos_features.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h" #include "components/arc/arc_util.h" #include "components/arc/ime/arc_ime_bridge_impl.h" #include "components/exo/wm_helper.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" @@ -45,11 +48,12 @@ double GetDefaultDeviceScaleFactor() { class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate { public: explicit ArcWindowDelegateImpl(ArcImeService* ime_service) - : ime_service_(ime_service) {} + : ime_service_(ime_service) {} ~ArcWindowDelegateImpl() override = default; bool IsInArcAppWindow(const aura::Window* window) const override { + // WMHelper is not craeted in browser_tests. if (!exo::WMHelper::HasInstance()) return false; aura::Window* active = exo::WMHelper::GetInstance()->GetActiveWindow(); @@ -71,7 +75,7 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate { } void RegisterFocusObserver() override { - // WMHelper is not created in tests. + // WMHelper is not craeted in browser_tests. if (!exo::WMHelper::HasInstance()) return; exo::WMHelper::GetInstance()->AddFocusObserver(ime_service_); @@ -93,7 +97,7 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate { } bool IsImeBlocked(aura::Window* window) const override { - // WMHelper is not created in tests. + // WMHelper is not craeted in browser_tests. if (!exo::WMHelper::HasInstance()) return false; return exo::WMHelper::GetInstance()->IsImeBlocked(window); @@ -137,8 +141,15 @@ ArcImeService* ArcImeService::GetForBrowserContext( ArcImeService::ArcImeService(content::BrowserContext* context, ArcBridgeService* bridge_service) + : ArcImeService(context, + bridge_service, + std::make_unique(this)) {} + +ArcImeService::ArcImeService(content::BrowserContext* context, + ArcBridgeService* bridge_service, + std::unique_ptr delegate) : ime_bridge_(new ArcImeBridgeImpl(this, bridge_service)), - arc_window_delegate_(new ArcWindowDelegateImpl(this)), + arc_window_delegate_(std::move(delegate)), ime_type_(ui::TEXT_INPUT_TYPE_NONE), ime_flags_(ui::TEXT_INPUT_FLAG_NONE), is_personalized_learning_allowed_(false), @@ -174,11 +185,6 @@ void ArcImeService::SetImeBridgeForTesting( ime_bridge_ = std::move(test_ime_bridge); } -void ArcImeService::SetArcWindowDelegateForTesting( - std::unique_ptr delegate) { - arc_window_delegate_ = std::move(delegate); -} - ui::InputMethod* ArcImeService::GetInputMethod() { return arc_window_delegate_->GetInputMethodForWindow(focused_arc_window_); } @@ -606,6 +612,13 @@ bool ArcImeService::SetCompositionFromExistingText( return false; } +bool ArcImeService::SetAutocorrectRange(const base::string16& autocorrect_text, + const gfx::Range& range) { + // TODO(https:://crbug.com/1091088): Implement this method. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + // static void ArcImeService::SetOverrideDefaultDeviceScaleFactorForTesting( base::Optional scale_factor) { diff --git a/chromium/components/arc/ime/arc_ime_service.h b/chromium/components/arc/ime/arc_ime_service.h index bc12839d20c..4a48b317eb9 100644 --- a/chromium/components/arc/ime/arc_ime_service.h +++ b/chromium/components/arc/ime/arc_ime_service.h @@ -49,10 +49,6 @@ class ArcImeService : public KeyedService, // or nullptr if the browser |context| is not allowed to use ARC. static ArcImeService* GetForBrowserContext(content::BrowserContext* context); - ArcImeService(content::BrowserContext* context, - ArcBridgeService* bridge_service); - ~ArcImeService() override; - class ArcWindowDelegate { public: virtual ~ArcWindowDelegate() = default; @@ -67,13 +63,14 @@ class ArcImeService : public KeyedService, virtual bool IsImeBlocked(aura::Window* window) const = 0; }; + ArcImeService(content::BrowserContext* context, + ArcBridgeService* bridge_service); + + ~ArcImeService() override; + // Injects the custom IPC bridge object for testing purpose only. void SetImeBridgeForTesting(std::unique_ptr test_ime_bridge); - // Injects the custom delegate for ARC windows, for testing purpose only. - void SetArcWindowDelegateForTesting( - std::unique_ptr delegate); - // Overridden from aura::EnvObserver: void OnWindowInitialized(aura::Window* new_window) override; @@ -149,6 +146,8 @@ class ArcImeService : public KeyedService, bool SetCompositionFromExistingText( const gfx::Range& range, const std::vector& ui_ime_text_spans) override; + bool SetAutocorrectRange(const base::string16& autocorrect_text, + const gfx::Range& range) override; // Normally, the default device scale factor is used to convert from DPI to // physical pixels. This method provides a way to override it for testing. @@ -156,6 +155,13 @@ class ArcImeService : public KeyedService, base::Optional scale_factor); private: + friend class ArcImeServiceTest; + + // Injects the custom delegate for ARC windows, for testing purpose only. + ArcImeService(content::BrowserContext* context, + ArcBridgeService* bridge_service, + std::unique_ptr delegate); + ui::InputMethod* GetInputMethod(); // Detaches from the IME associated with the |old_window|, and attaches to the diff --git a/chromium/components/arc/ime/arc_ime_service_unittest.cc b/chromium/components/arc/ime/arc_ime_service_unittest.cc index f38b5efd768..2b0f0ac4ff9 100644 --- a/chromium/components/arc/ime/arc_ime_service_unittest.cc +++ b/chromium/components/arc/ime/arc_ime_service_unittest.cc @@ -12,6 +12,8 @@ #include "base/memory/ptr_util.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "chromeos/constants/chromeos_features.h" #include "components/arc/mojom/ime.mojom.h" #include "components/arc/session/arc_bridge_service.h" #include "testing/gtest/include/gtest/gtest.h" @@ -207,16 +209,17 @@ class ArcImeServiceTest : public testing::Test { private: void SetUp() override { arc_bridge_service_ = std::make_unique(); - instance_ = - std::make_unique(nullptr, arc_bridge_service_.get()); - fake_arc_ime_bridge_ = new FakeArcImeBridge(); - instance_->SetImeBridgeForTesting(base::WrapUnique(fake_arc_ime_bridge_)); fake_input_method_ = std::make_unique(); + auto delegate = + std::make_unique(fake_input_method_.get()); + fake_window_delegate_ = delegate.get(); + + instance_ = base::WrapUnique(new ArcImeService( + nullptr, arc_bridge_service_.get(), std::move(delegate))); + fake_arc_ime_bridge_ = new FakeArcImeBridge(); + instance_->SetImeBridgeForTesting(base::WrapUnique(fake_arc_ime_bridge_)); - fake_window_delegate_ = new FakeArcWindowDelegate(fake_input_method_.get()); - instance_->SetArcWindowDelegateForTesting( - base::WrapUnique(fake_window_delegate_)); arc_win_ = fake_window_delegate_->CreateFakeArcWindow(); } @@ -226,6 +229,7 @@ class ArcImeServiceTest : public testing::Test { fake_window_delegate_ = nullptr; fake_arc_ime_bridge_ = nullptr; instance_.reset(); + fake_input_method_.reset(); arc_bridge_service_.reset(); } }; diff --git a/chromium/components/arc/intent_helper/DEPS b/chromium/components/arc/intent_helper/DEPS index 8f0dc0b632f..7f66c60dc3b 100644 --- a/chromium/components/arc/intent_helper/DEPS +++ b/chromium/components/arc/intent_helper/DEPS @@ -8,6 +8,8 @@ include_rules = [ "+components/services/app_service/public/cpp", "+components/url_formatter", "+services/service_manager/public/cpp/connector.h", + "+ui/aura", "+ui/base", "+ui/gfx", + "+ui/views", ] diff --git a/chromium/components/arc/intent_helper/activity_icon_loader.cc b/chromium/components/arc/intent_helper/activity_icon_loader.cc index 5bcebf5a201..bbc02cc8245 100644 --- a/chromium/components/arc/intent_helper/activity_icon_loader.cc +++ b/chromium/components/arc/intent_helper/activity_icon_loader.cc @@ -13,6 +13,7 @@ #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/task/post_task.h" +#include "base/task/thread_pool.h" #include "components/arc/arc_service_manager.h" #include "components/arc/arc_util.h" #include "components/arc/session/arc_bridge_service.h" @@ -252,7 +253,7 @@ void ActivityIconLoader::OnIconsReady( OnIconsReadyCallback cb, std::vector icons) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - base::PostTaskAndReplyWithResult( + base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&ResizeAndEncodeIcons, std::move(icons), scale_factor_), base::BindOnce(&ActivityIconLoader::OnIconsResized, 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 8878c8f8d6b..ba948dc3077 100644 --- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc +++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc @@ -144,7 +144,6 @@ 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. - // TODO(mash): Support this functionality without ash::Shell access in Chrome. ash::NewWindowDelegate::GetInstance()->OpenFileManager(); } 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 c48c1ff04bf..d1b8a7aa6e1 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 @@ -24,9 +24,11 @@ IntentFilter GetIntentFilter(const std::string& host, const std::string& pkg_name) { std::vector authorities; authorities.emplace_back(host, /*port=*/-1); - return IntentFilter(pkg_name, std::move(authorities), + return IntentFilter(pkg_name, /*actions=*/std::vector(), + std::move(authorities), std::vector(), - std::vector()); + /*schemes=*/std::vector(), + /*mime_types=*/std::vector()); } } // namespace diff --git a/chromium/components/arc/intent_helper/custom_tab.cc b/chromium/components/arc/intent_helper/custom_tab.cc new file mode 100644 index 00000000000..5af4709668a --- /dev/null +++ b/chromium/components/arc/intent_helper/custom_tab.cc @@ -0,0 +1,160 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/arc/intent_helper/custom_tab.h" + +#include +#include +#include + +#include "base/threading/sequenced_task_runner_handle.h" +#include "components/exo/surface.h" +#include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace arc { + +namespace { + +// Enumerates surfaces under the window. +void EnumerateSurfaces(aura::Window* window, std::vector* out) { + auto* surface = exo::Surface::AsSurface(window); + if (surface) + out->push_back(surface); + for (aura::Window* child : window->children()) + EnumerateSurfaces(child, out); +} + +} // namespace + +CustomTab::CustomTab(aura::Window* arc_app_window, + int32_t surface_id, + int32_t top_margin) + : arc_app_window_(arc_app_window), + surface_id_(surface_id), + top_margin_(top_margin) { + other_windows_observer_.Add(arc_app_window_); + + host_->set_owned_by_client(); + auto* const widget = views::Widget::GetWidgetForNativeWindow(arc_app_window_); + DCHECK(widget); + widget->GetContentsView()->AddChildView(host_.get()); +} + +CustomTab::~CustomTab() = default; + +void CustomTab::Attach(gfx::NativeView view) { + DCHECK(view); + DCHECK(!GetHostView()); + host_->Attach(view); + aura::Window* const container = host_->GetNativeViewContainer(); + container->SetEventTargeter(std::make_unique()); + other_windows_observer_.Add(container); + EnsureWindowOrders(); + UpdateSurfaceIfNecessary(); +} + +gfx::NativeView CustomTab::GetHostView() { + return host_->native_view(); +} + +void CustomTab::OnWindowHierarchyChanged(const HierarchyChangeParams& params) { + if ((params.receiver == arc_app_window_) && + exo::Surface::AsSurface(params.target) && params.new_parent) + UpdateSurfaceIfNecessary(); +} + +void CustomTab::OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + ui::PropertyChangeReason reason) { + if (surface_window_observer_.IsObserving(window) && + old_bounds.size() != new_bounds.size()) + OnSurfaceBoundsMaybeChanged(window); +} + +void CustomTab::OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) { + if (surfaces_observer_.IsObserving(window) && key == exo::kClientSurfaceIdKey) + UpdateSurfaceIfNecessary(); +} + +void CustomTab::OnWindowStackingChanged(aura::Window* window) { + if (window == host_->GetNativeViewContainer() && + !weak_ptr_factory_.HasWeakPtrs()) { + // Reordering should happen asynchronously -- some entity (like + // views::WindowReorderer) changes the window orders, and then ensures layer + // orders later. Changing order here synchronously leads to inconsistent + // window/layer ordering and causes weird graphical effects. + // TODO(hashimoto): fix the views ordering and remove this handling. + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&CustomTab::EnsureWindowOrders, + weak_ptr_factory_.GetWeakPtr())); + } +} + +void CustomTab::OnWindowDestroying(aura::Window* window) { + if (surfaces_observer_.IsObserving(window)) + surfaces_observer_.Remove(window); + if (surface_window_observer_.IsObserving(window)) + surface_window_observer_.Remove(window); + if (other_windows_observer_.IsObserving(window)) + other_windows_observer_.Remove(window); +} + +void CustomTab::OnSurfaceBoundsMaybeChanged(aura::Window* surface_window) { + DCHECK(surface_window); + gfx::Point origin(0, top_margin_); + gfx::Point bottom_right(surface_window->bounds().width(), + surface_window->bounds().height()); + ConvertPointFromWindow(surface_window, &origin); + ConvertPointFromWindow(surface_window, &bottom_right); + host_->SetBounds(origin.x(), origin.y(), bottom_right.x() - origin.x(), + bottom_right.y() - origin.y()); +} + +void CustomTab::EnsureWindowOrders() { + aura::Window* const container = host_->GetNativeViewContainer(); + if (container) + container->parent()->StackChildAtTop(container); +} + +void CustomTab::ConvertPointFromWindow(aura::Window* window, + gfx::Point* point) { + views::Widget* const widget = host_->GetWidget(); + aura::Window::ConvertPointToTarget(window, widget->GetNativeWindow(), point); + views::View::ConvertPointFromWidget(widget->GetContentsView(), point); +} + +void CustomTab::UpdateSurfaceIfNecessary() { + std::vector surfaces; + EnumerateSurfaces(arc_app_window_, &surfaces); + + // Try to find the surface. + const auto it = std::find_if(surfaces.cbegin(), surfaces.cend(), + [id = surface_id_](const auto* surface) { + return surface->GetClientSurfaceId() == id; + }); + if (it == surfaces.cend()) { + for (auto* surface : surfaces) { + if (!surface->GetClientSurfaceId() && + !surfaces_observer_.IsObserving(surface->window())) + surfaces_observer_.Add(surface->window()); + } + } else { + surfaces_observer_.RemoveAll(); + + auto* const window = (*it)->window(); + if (!surface_window_observer_.IsObserving(window)) { + surface_window_observer_.RemoveAll(); + surface_window_observer_.Add(window); + OnSurfaceBoundsMaybeChanged(window); + } + } +} + +} // namespace arc diff --git a/chromium/components/arc/intent_helper/custom_tab.h b/chromium/components/arc/intent_helper/custom_tab.h new file mode 100644 index 00000000000..01815f2c56c --- /dev/null +++ b/chromium/components/arc/intent_helper/custom_tab.h @@ -0,0 +1,76 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_ARC_INTENT_HELPER_CUSTOM_TAB_H_ +#define COMPONENTS_ARC_INTENT_HELPER_CUSTOM_TAB_H_ + +#include + +#include "base/macros.h" +#include "base/scoped_observer.h" +#include "components/arc/arc_export.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/views/controls/native/native_view_host.h" + +namespace arc { + +// CustomTab is responsible to embed an ARC++ custom tab. +class ARC_EXPORT CustomTab : public aura::WindowObserver { + public: + CustomTab(aura::Window* arc_app_window, + int32_t surface_id, + int32_t top_margin); + CustomTab(const CustomTab&) = delete; + CustomTab& operator=(const CustomTab&) = delete; + ~CustomTab() override; + + void Attach(gfx::NativeView view); + + // Returns the view against which a view or dialog is positioned and parented + // in an CustomTab. + gfx::NativeView GetHostView(); + + // aura::WindowObserver: + void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override; + void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + ui::PropertyChangeReason reason) override; + void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) override; + void OnWindowStackingChanged(aura::Window* window) override; + void OnWindowDestroying(aura::Window* window) override; + + private: + // Updates |host_|'s bounds to deal with changes in the bounds of the + // associated |surface_window|. + void OnSurfaceBoundsMaybeChanged(aura::Window* surface_window); + + // Ensures the window/layer orders for the NativeViewHost. + void EnsureWindowOrders(); + + // Converts the point from the given window to this view. + void ConvertPointFromWindow(aura::Window* window, gfx::Point* point); + + // Looks for the surface with |surface_id_|, and handles resultant changes. + void UpdateSurfaceIfNecessary(); + + std::unique_ptr host_ = + std::make_unique(); + aura::Window* const arc_app_window_; + const int32_t surface_id_, top_margin_; + ScopedObserver surfaces_observer_{this}; + ScopedObserver surface_window_observer_{ + this}; + ScopedObserver other_windows_observer_{ + this}; + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace arc + +#endif // COMPONENTS_ARC_INTENT_HELPER_CUSTOM_TAB_H_ diff --git a/chromium/components/arc/intent_helper/intent_filter.cc b/chromium/components/arc/intent_helper/intent_filter.cc index 51d713dbba1..2cb365a24ec 100644 --- a/chromium/components/arc/intent_helper/intent_filter.cc +++ b/chromium/components/arc/intent_helper/intent_filter.cc @@ -4,10 +4,12 @@ #include "components/arc/intent_helper/intent_filter.h" +#include #include #include "base/compiler_specific.h" #include "base/strings/string_util.h" +#include "components/arc/intent_helper/intent_constants.h" #include "components/arc/mojom/intent_helper.mojom.h" #include "components/services/app_service/public/cpp/intent_util.h" #include "url/gurl.h" @@ -19,12 +21,16 @@ IntentFilter::IntentFilter(IntentFilter&& other) = default; IntentFilter::IntentFilter( const std::string& package_name, + std::vector actions, std::vector authorities, std::vector paths, - std::vector schemes) + std::vector schemes, + std::vector mime_types) : package_name_(package_name), + actions_(std::move(actions)), authorities_(std::move(authorities)), - schemes_(std::move(schemes)) { + schemes_(std::move(schemes)), + mime_types_(std::move(mime_types)) { // In order to register a path we need to have at least one authority. if (!authorities_.empty()) paths_ = std::move(paths); @@ -45,6 +51,15 @@ bool IntentFilter::Match(const GURL& url) const { return false; } + // Don't return match for filters for sharing. + if (std::any_of(actions_.begin(), actions_.end(), + [](const std::string action) { + return action == kIntentActionSend || + action == kIntentActionSendMultiple; + })) { + return false; + } + // 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. diff --git a/chromium/components/arc/intent_helper/intent_filter.h b/chromium/components/arc/intent_helper/intent_filter.h index db457d03827..de205499e0c 100644 --- a/chromium/components/arc/intent_helper/intent_filter.h +++ b/chromium/components/arc/intent_helper/intent_filter.h @@ -70,9 +70,11 @@ class IntentFilter { IntentFilter(); IntentFilter(IntentFilter&& other); IntentFilter(const std::string& package_name, + std::vector actions, std::vector authorities, std::vector paths, - std::vector schemes); + std::vector schemes, + std::vector mime_types); ~IntentFilter(); IntentFilter& operator=(IntentFilter&& other); @@ -80,20 +82,24 @@ class IntentFilter { bool Match(const GURL& url) const; const std::string& package_name() const { return package_name_; } + const std::vector& actions() const { return actions_; } const std::vector& authorities() const { return authorities_; } const std::vector& paths() const { return paths_; } const std::vector& schemes() const { return schemes_; } + const std::vector& mime_types() const { return mime_types_; } private: bool MatchDataAuthority(const GURL& url) const; bool HasDataPath(const GURL& url) const; std::string package_name_; + std::vector actions_; std::vector authorities_; std::vector paths_; std::vector schemes_; + std::vector mime_types_; DISALLOW_COPY_AND_ASSIGN(IntentFilter); }; diff --git a/chromium/components/arc/intent_helper/intent_filter_mojom_traits.cc b/chromium/components/arc/intent_helper/intent_filter_mojom_traits.cc index 16b2b9f0b33..87725074586 100644 --- a/chromium/components/arc/intent_helper/intent_filter_mojom_traits.cc +++ b/chromium/components/arc/intent_helper/intent_filter_mojom_traits.cc @@ -31,8 +31,17 @@ bool StructTraits::Read( if (!data.ReadDataSchemes(&schemes)) return false; - *out = arc::IntentFilter(package_name, std::move(authorities), - std::move(paths), std::move(schemes)); + std::vector actions; + if (!data.ReadActions(&actions)) + return false; + + std::vector mime_types; + if (!data.ReadMimeTypes(&mime_types)) + return false; + + *out = arc::IntentFilter(package_name, std::move(actions), + std::move(authorities), std::move(paths), + std::move(schemes), std::move(mime_types)); return true; } diff --git a/chromium/components/arc/intent_helper/intent_filter_mojom_traits.h b/chromium/components/arc/intent_helper/intent_filter_mojom_traits.h index 636efd1d9f1..772f9299db9 100644 --- a/chromium/components/arc/intent_helper/intent_filter_mojom_traits.h +++ b/chromium/components/arc/intent_helper/intent_filter_mojom_traits.h @@ -16,9 +16,8 @@ namespace mojo { template <> struct StructTraits { - static const base::span actions(const arc::IntentFilter& r) { - // Returns an empty array. - return base::span(); + static const std::vector& actions(const arc::IntentFilter& r) { + return r.actions(); } static const base::span categories(const arc::IntentFilter& r) { // Returns an empty array. @@ -46,6 +45,11 @@ struct StructTraits { return r.package_name(); } + static const std::vector& mime_types( + const arc::IntentFilter& r) { + return r.mime_types(); + } + static bool Read(arc::mojom::IntentFilterDataView data, arc::IntentFilter* out); }; diff --git a/chromium/components/arc/intent_helper/intent_filter_unittest.cc b/chromium/components/arc/intent_helper/intent_filter_unittest.cc index a2158cd26f8..4d65eeee9c3 100644 --- a/chromium/components/arc/intent_helper/intent_filter_unittest.cc +++ b/chromium/components/arc/intent_helper/intent_filter_unittest.cc @@ -40,8 +40,11 @@ class IntentFilterBuilder { } operator IntentFilter() { - return IntentFilter(kPackageName, std::move(authorities_), - std::move(paths_), std::vector()); + return IntentFilter(kPackageName, + /*actions=*/std::vector(), + std::move(authorities_), std::move(paths_), + /*schemes=*/std::vector(), + /*mime_types=*/std::vector()); } private: diff --git a/chromium/components/arc/midis/arc_midis_bridge.cc b/chromium/components/arc/midis/arc_midis_bridge.cc index a0edd30b3a4..94f0dc74bcc 100644 --- a/chromium/components/arc/midis/arc_midis_bridge.cc +++ b/chromium/components/arc/midis/arc_midis_bridge.cc @@ -57,8 +57,8 @@ ArcMidisBridge::~ArcMidisBridge() { } void ArcMidisBridge::OnBootstrapMojoConnection( - mojom::MidisServerRequest request, - mojom::MidisClientPtr client_ptr, + mojo::PendingReceiver receiver, + mojo::PendingRemote client_remote, bool result) { if (!result) { LOG(ERROR) << "ArcMidisBridge had a failure in D-Bus with the daemon."; @@ -70,14 +70,15 @@ void ArcMidisBridge::OnBootstrapMojoConnection( return; } DVLOG(1) << "ArcMidisBridge succeeded with Mojo bootstrapping."; - midis_host_remote_->Connect(std::move(request), std::move(client_ptr)); + midis_host_remote_->Connect(std::move(receiver), std::move(client_remote)); } -void ArcMidisBridge::Connect(mojom::MidisServerRequest request, - mojom::MidisClientPtr client_ptr) { +void ArcMidisBridge::Connect( + mojo::PendingReceiver receiver, + mojo::PendingRemote client_remote) { if (midis_host_remote_.is_bound()) { DVLOG(1) << "Re-using bootstrap connection for MidisServer Connect."; - midis_host_remote_->Connect(std::move(request), std::move(client_ptr)); + midis_host_remote_->Connect(std::move(receiver), std::move(client_remote)); return; } DVLOG(1) << "Bootstrapping the Midis connection via D-Bus."; @@ -101,8 +102,8 @@ void ArcMidisBridge::Connect(mojom::MidisServerRequest request, ->BootstrapMojoConnection( channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(), base::BindOnce(&ArcMidisBridge::OnBootstrapMojoConnection, - weak_factory_.GetWeakPtr(), std::move(request), - std::move(client_ptr))); + weak_factory_.GetWeakPtr(), std::move(receiver), + std::move(client_remote))); } void ArcMidisBridge::OnMojoConnectionError() { diff --git a/chromium/components/arc/midis/arc_midis_bridge.h b/chromium/components/arc/midis/arc_midis_bridge.h index 6bfcf7ba1a0..3b97a95e022 100644 --- a/chromium/components/arc/midis/arc_midis_bridge.h +++ b/chromium/components/arc/midis/arc_midis_bridge.h @@ -12,6 +12,8 @@ #include "base/macros.h" #include "components/arc/mojom/midis.mojom.h" #include "components/keyed_service/core/keyed_service.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" namespace content { @@ -34,13 +36,14 @@ class ArcMidisBridge : public KeyedService, ~ArcMidisBridge() override; // Midis Mojo host interface - void Connect(mojom::MidisServerRequest request, - mojom::MidisClientPtr client_ptr) override; + void Connect(mojo::PendingReceiver receiver, + mojo::PendingRemote client_remote) override; private: - void OnBootstrapMojoConnection(mojom::MidisServerRequest request, - mojom::MidisClientPtr client_ptr, - bool result); + void OnBootstrapMojoConnection( + mojo::PendingReceiver receiver, + mojo::PendingRemote client_remote, + bool result); void OnMojoConnectionError(); ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. diff --git a/chromium/components/arc/mojom/BUILD.gn b/chromium/components/arc/mojom/BUILD.gn index 13896b9dc98..ebb973f531e 100644 --- a/chromium/components/arc/mojom/BUILD.gn +++ b/chromium/components/arc/mojom/BUILD.gn @@ -41,8 +41,6 @@ if (is_chromeos) { "midis.mojom", "net.mojom", "obb_mounter.mojom", - "oemcrypto.mojom", - "oemcrypto_daemon.mojom", "pip.mojom", "policy.mojom", "power.mojom", @@ -69,7 +67,7 @@ if (is_chromeos) { ":camera_intent", ":media", ":notifications", - "//components/chromeos_camera/common:camera_app_helper", + ":oemcrypto", "//media/capture/video/chromeos/mojom:cros_camera", "//mojo/public/mojom/base", "//printing/mojom", @@ -112,21 +110,33 @@ if (is_chromeos) { public_deps = [ "//ui/gfx/geometry/mojom" ] } + mojom("oemcrypto") { + sources = [ "oemcrypto.mojom" ] + } + source_set("mojom_traits") { - sources = [ "ime_mojom_traits.h" ] + sources = [ + "ime_mojom_traits.cc", + "ime_mojom_traits.h", + ] deps = [ ":mojom", "//ui/base/ime:text_input_types", + "//ui/events", ] } source_set("unit_tests") { testonly = true - sources = [ "video_accelerator_mojom_traits_unittest.cc" ] + sources = [ + "ime_mojom_traits_unittest.cc", + "video_accelerator_mojom_traits_unittest.cc", + ] deps = [ ":mojom", + ":mojom_traits", "//media", "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", diff --git a/chromium/components/arc/mojom/accessibility_helper.mojom b/chromium/components/arc/mojom/accessibility_helper.mojom index 1092b141686..e8cfb41a789 100644 --- a/chromium/components/arc/mojom/accessibility_helper.mojom +++ b/chromium/components/arc/mojom/accessibility_helper.mojom @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Next MinVersion: 22 +// Next MinVersion: 23 module arc.mojom; @@ -47,6 +47,22 @@ enum AccessibilityEventType { ASSIST_READING_CONTEXT, }; +// ContentChangeType lists the possible sub types of WINDOW_STATE_CHANGED and +// WINDOW_CONTENT_CHANGED events on Android ordered as same as developer guide +// of return value of getContentChangeTypes. +// https://developer.android.com/reference/android/view/accessibility/AccessibilityEvent?hl=en#getContentChangeTypes() +[Extensible] +enum ContentChangeType { + CONTENT_DESCRIPTION, + STATE_DESCRIPTION, + SUBTREE, + TEXT, + PANE_TITLE, + UNDEFINED, + PANE_APPEARED, + PANE_DISAPPEARED, +}; + // Possible actions that can be performed on an AccessibilityNodeInfo. [Extensible] enum AccessibilityActionType { @@ -129,7 +145,8 @@ enum AccessibilityStringProperty { ROLE_DESCRIPTION, // Chrome only TOOLTIP, PANE_TITLE, - HINT_TEXT + HINT_TEXT, + STATE_DESCRIPTION }; // These fields are taken from int instance members of @@ -355,6 +372,13 @@ enum AccessibilityEventStringProperty { CONTENT_DESCRIPTION, }; +// These fields are taken from List like instance members of +// AccessibilityEvent and AccessibilityRecord. +[Extensible] +enum AccessibilityEventIntListProperty{ + CONTENT_CHANGE_TYPES, +}; + // AccessibilityEventData is a struct to contain info of // AccessibilityEvent in Android. // https://developer.android.com/reference/android/view/accessibility/AccessibilityEvent.html @@ -390,6 +414,8 @@ struct AccessibilityEventData { [MinVersion=21] map? int_properties; [MinVersion=21] map? string_properties; + [MinVersion=23] + map>? int_list_properties; }; // AccessibilityActionData is a struct to contain info of AccessibilityAction in @@ -472,7 +498,8 @@ interface AccessibilityHelperHost { // Next method ID: 12 interface AccessibilityHelperInstance { // Establishes full-duplex communication with the host. - [MinVersion=9] Init@7(AccessibilityHelperHost host) => (); + [MinVersion=9] Init@7( + pending_remote host_remote) => (); // Set a filter on the event types received. SetFilter@2(AccessibilityFilterType filter_type); diff --git a/chromium/components/arc/mojom/app.mojom b/chromium/components/arc/mojom/app.mojom index 25ff611be6a..ad1fa0ecfd9 100644 --- a/chromium/components/arc/mojom/app.mojom +++ b/chromium/components/arc/mojom/app.mojom @@ -87,6 +87,19 @@ enum ShowPackageInfoPage { MANAGE_LINKS = 1, }; +// Describes the raw icon png data published by an Android application. +struct RawIconPngData { + // True if the icon is an adaptive icon, or false otherwise. + bool is_adaptive_icon; + // The raw icon for the non-adaptive icon, or the generated standard icon done + // by the ARC side for the adaptive icon. + array? icon_png_data; + // The foreground image for the adaptive icon. + array? foreground_icon_png_data; + // The background image for the adaptive icon. + array? background_icon_png_data; +}; + // Describes a Play Store app discovery result. struct AppDiscoveryResult { string? launch_intent_uri; @@ -364,10 +377,10 @@ interface AppHost { // Deprecated method IDs: 2, 3, 13 interface AppInstance { // DEPRECATED: Please use Init@21 instead. - InitDeprecated@0(AppHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=26] Init@21(AppHost host_ptr) => (); + [MinVersion=26] Init@21(pending_remote host_remote) => (); [MinVersion=1] CanHandleResolutionDeprecated@4( string package_name, string activity, Rect dimension) => diff --git a/chromium/components/arc/mojom/appfuse.mojom b/chromium/components/arc/mojom/appfuse.mojom index ee81f3c788b..b78d30d99e7 100644 --- a/chromium/components/arc/mojom/appfuse.mojom +++ b/chromium/components/arc/mojom/appfuse.mojom @@ -23,5 +23,5 @@ interface AppfuseHost { // Next Method ID: 1 interface AppfuseInstance { // Establishes full-duplex communication with the host. - Init@0(AppfuseHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/arc_bridge.mojom b/chromium/components/arc/mojom/arc_bridge.mojom index 8f6a38fc033..127c81924de 100644 --- a/chromium/components/arc/mojom/arc_bridge.mojom +++ b/chromium/components/arc/mojom/arc_bridge.mojom @@ -64,164 +64,198 @@ interface ArcBridgeHost { // Notifies Chrome that the AccessibilityHelperInstance interface is ready. [MinVersion=21] OnAccessibilityHelperInstanceReady@127( - AccessibilityHelperInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the AppInstance interface is ready. - OnAppInstanceReady@100(AppInstance instance_ptr); + OnAppInstanceReady@100(pending_remote instance_remote); // Notifies Chrome that the AppPermissionsInstance interface is ready. - [MinVersion=44] OnAppPermissionsInstanceReady@149(AppPermissionsInstance instance_ptr); + [MinVersion=44] OnAppPermissionsInstanceReady@149( + pending_remote instance_remote); // Notifies Chrome that the AppfuseInstance interface is ready. - [MinVersion=40] OnAppfuseInstanceReady@145(AppfuseInstance instance_ptr); + [MinVersion=40] OnAppfuseInstanceReady@145( + pending_remote instance_remote); // Notifies Chrome that the AudioInstance interface is ready. - [MinVersion=8] OnAudioInstanceReady@115(AudioInstance instance_ptr); + [MinVersion=8] OnAudioInstanceReady@115( + pending_remote instance_remote); // Notifies Chrome that the AuthInstance interface is ready. - [MinVersion=1] OnAuthInstanceReady@106(AuthInstance instance_ptr); + [MinVersion=1] OnAuthInstanceReady@106( + pending_remote instance_remote); // Notifies Chrome that the BackupSettingsInstance interface is ready. - [MinVersion=33] OnBackupSettingsInstanceReady@138(BackupSettingsInstance instance_ptr); + [MinVersion=33] OnBackupSettingsInstanceReady@138( + pending_remote instance_remote); // Notifies Chrome that the BluetoothInstance interface is ready. - [MinVersion=9] OnBluetoothInstanceReady@113(BluetoothInstance instance_ptr); + [MinVersion=9] OnBluetoothInstanceReady@113( + pending_remote instance_remote); // Notifies Chrome that the BootPhaseMonitorInstance interface is ready. [MinVersion=19] OnBootPhaseMonitorInstanceReady@125( - BootPhaseMonitorInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the CameraInstance is ready. - [MinVersion=46] OnCameraInstanceReady@151(CameraInstance instance_ptr); + [MinVersion=46] OnCameraInstanceReady@151( + pending_remote instance_remote); // Notifies Chrome that the CastReceiverInstance interface is ready. [MinVersion=27] OnCastReceiverInstanceReady@132( - CastReceiverInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the CertStoreInstance interface is ready. [MinVersion=31] OnCertStoreInstanceReady@136( - CertStoreInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the ClipboardInstance interface is ready. - [MinVersion=2] OnClipboardInstanceReady@109(ClipboardInstance instance_ptr); + [MinVersion=2] OnClipboardInstanceReady@109( + pending_remote instance_remote); // Notifies Chrome that the CrashCollectorInstance interface is ready. [MinVersion=7] OnCrashCollectorInstanceReady@112( - CrashCollectorInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the DiskQuotaInstance interface is ready. - [MinVersion=39] OnDiskQuotaInstanceReady@144(DiskQuotaInstance instance_ptr); + [MinVersion=39] OnDiskQuotaInstanceReady@144( + pending_remote instance_remote); // Notifies Chrome that the EnterpriseReportingInstance interface is ready. [MinVersion=15] OnEnterpriseReportingInstanceReady@122( - EnterpriseReportingInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the FileSystemInstance interface is ready. [MinVersion=13] OnFileSystemInstanceReady@119( - FileSystemInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the ImeInstance interface is ready. - [MinVersion=3] OnImeInstanceReady@110(ImeInstance instance_ptr); + [MinVersion=3] OnImeInstanceReady@110( + pending_remote instance_remote); // Notifies Chrome that the InputMethodManagerInstance interface is ready. [MinVersion=38] OnInputMethodManagerInstanceReady@143( - InputMethodManagerInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the IntentHelperInstance interface is ready. [MinVersion=4] OnIntentHelperInstanceReady@111( - IntentHelperInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the KeymasterInstance interface is ready. - [MinVersion=47] OnKeymasterInstanceReady@152(KeymasterInstance instance_ptr); + [MinVersion=47] OnKeymasterInstanceReady@152( + pending_remote instance_remote); // Notifies Chrome that the KioskInstance interface is ready. - [MinVersion=20] OnKioskInstanceReady@126(KioskInstance instance_ptr); + [MinVersion=20] OnKioskInstanceReady@126( + pending_remote instance_remote); // Notifies Chrome that the LockScreenInstance interface is ready. - [MinVersion=29] OnLockScreenInstanceReady@134(LockScreenInstance instance_ptr); + [MinVersion=29] OnLockScreenInstanceReady@134( + pending_remote instance_remote); // Notifies Chrome that the MediaSessionInstance interface is ready. - [MinVersion=43] OnMediaSessionInstanceReady@148(MediaSessionInstance instance_ptr); + [MinVersion=43] OnMediaSessionInstanceReady@148( + pending_remote instance_remote); // Notifies Chrome that the MetricsInstance interface is ready. - [MinVersion=10] OnMetricsInstanceReady@116(MetricsInstance instance_ptr); + [MinVersion=10] OnMetricsInstanceReady@116( + pending_remote instance_remote); // Notifies Chrome that the MidisInstance interface is ready. - [MinVersion=30] OnMidisInstanceReady@135(MidisInstance instance_ptr); + [MinVersion=30] OnMidisInstanceReady@135( + pending_remote instance_remote); // Notifies Chrome that the NetInstance interface is ready. - [MinVersion=5] OnNetInstanceReady@108(NetInstance instance_ptr); + [MinVersion=5] OnNetInstanceReady@108( + pending_remote instance_remote); // Notifies Chrome that the NotificationsInstance interface is ready. - OnNotificationsInstanceReady@102(NotificationsInstance instance_ptr); + OnNotificationsInstanceReady@102( + pending_remote instance_remote); // Notifies Chrome that the ObbMounter interface is ready. - [MinVersion=14] OnObbMounterInstanceReady@120(ObbMounterInstance instance_ptr); + [MinVersion=14] OnObbMounterInstanceReady@120( + pending_remote instance_remote); // Notifies Chrome that the OemCryptoInstance interface is ready. - [MinVersion=28] OnOemCryptoInstanceReady@133(OemCryptoInstance instance_ptr); + [MinVersion=28] OnOemCryptoInstanceReady@133( + pending_remote instance_remote); // Notifies Chrome that the PipInstance interface is ready. - [MinVersion=41] OnPipInstanceReady@146(PipInstance instance_ptr); + [MinVersion=41] OnPipInstanceReady@146( + pending_remote instance_remote); // Notifies Chrome that the PolicyInstance interface is ready. - [MinVersion=7] OnPolicyInstanceReady@114(PolicyInstance instance_ptr); + [MinVersion=7] OnPolicyInstanceReady@114( + pending_remote instance_remote); // Notifies Chrome that the PowerInstance interface is ready. - OnPowerInstanceReady@103(PowerInstance instance_ptr); + OnPowerInstanceReady@103(pending_remote instance_remote); // Notifies Chrome that the PrintSpoolerInstance interface is ready. [MinVersion=45] OnPrintSpoolerInstanceReady@150( - PrintSpoolerInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the ProcessInstance interface is ready. - OnProcessInstanceReady@104(ProcessInstance instance_ptr); + OnProcessInstanceReady@104(pending_remote instance_remote); // Notifies Chrome that the PropertyInstance interface is ready. - [MinVersion=42] OnPropertyInstanceReady@147(PropertyInstance instance_ptr); + [MinVersion=42] OnPropertyInstanceReady@147( + pending_remote instance_remote); // Notifies Chrome that the RotationLockInstance interface is ready. - [MinVersion=32] OnRotationLockInstanceReady@137(RotationLockInstance instance_ptr); + [MinVersion=32] OnRotationLockInstanceReady@137( + pending_remote instance_remote); // Notifies Chrome that the ScreenCaptureInstance interface is ready. - [MinVersion=35] OnScreenCaptureInstanceReady@140(ScreenCaptureInstance instance_ptr); + [MinVersion=35] OnScreenCaptureInstanceReady@140( + pending_remote instance_remote); // Notifies Chrome that the SmartCardManagerInstance interface is ready. [MinVersion=48] OnSmartCardManagerInstanceReady@153( - SmartCardManagerInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the StorageManagerInstance interface is ready. - [MinVersion=12] OnStorageManagerInstanceReady@118(StorageManagerInstance instance_ptr); + [MinVersion=12] OnStorageManagerInstanceReady@118( + pending_remote instance_remote); // Notifies Chrome that the TimerInstance interface is ready. - [MinVersion=36] OnTimerInstanceReady@141(TimerInstance instance_ptr); + [MinVersion=36] OnTimerInstanceReady@141( + pending_remote instance_remote); // Notifies Chrome that the TracingInstance interface is ready. - [MinVersion=22] OnTracingInstanceReady@128(TracingInstance instance_ptr); + [MinVersion=22] OnTracingInstanceReady@128( + pending_remote instance_remote); // Notifies Chrome that the TtsInstance interface is ready. - [MinVersion=17] OnTtsInstanceReady@123(TtsInstance instance_ptr); + [MinVersion=17] OnTtsInstanceReady@123( + pending_remote instance_remote); // Notifies Chrome that the UsbHostInstance interface is ready. - [MinVersion=34] OnUsbHostInstanceReady@139(UsbHostInstance instance_ptr); + [MinVersion=34] OnUsbHostInstanceReady@139( + pending_remote instance_remote); // Notifies Chrome that the VideoInstance interface is ready. - [MinVersion=6] OnVideoInstanceReady@107(VideoInstance instance_ptr); + [MinVersion=6] OnVideoInstanceReady@107( + pending_remote instance_remote); // Notifies Chrome that the VoiceInteractionArcHomeInstance is ready. [MinVersion=24] OnVoiceInteractionArcHomeInstanceReady@130( - VoiceInteractionArcHomeInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the VoiceInteractionFrameworkInstance is ready. [MinVersion=23] OnVoiceInteractionFrameworkInstanceReady@129( - VoiceInteractionFrameworkInstance instance_ptr); + pending_remote instance_remote); // Notifies Chrome that the VolumeMounter interface is ready. - [MinVersion=25] OnVolumeMounterInstanceReady@131(VolumeMounterInstance instance_ptr); + [MinVersion=25] OnVolumeMounterInstanceReady@131( + pending_remote instance_remote); // Notifies Chrome that the WakeLockInstance interface is ready. - [MinVersion=37] OnWakeLockInstanceReady@142(WakeLockInstance instance_ptr); + [MinVersion=37] OnWakeLockInstanceReady@142( + pending_remote instance_remote); // Notifies Chrome that the WallpaperInstance interface is ready. - [MinVersion=18] OnWallpaperInstanceReady@124(WallpaperInstance instance_ptr); + [MinVersion=18] OnWallpaperInstanceReady@124( + pending_remote instance_remote); }; diff --git a/chromium/components/arc/mojom/audio.mojom b/chromium/components/arc/mojom/audio.mojom index eceb20e4cdf..6d90a02569a 100644 --- a/chromium/components/arc/mojom/audio.mojom +++ b/chromium/components/arc/mojom/audio.mojom @@ -27,10 +27,10 @@ interface AudioHost { // Next method ID: 4 interface AudioInstance { // DEPRECATED: Please use Init@3 instead. - [MinVersion=1] InitDeprecated@1(AudioHost host); + [MinVersion=1] InitDeprecated@1(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=4] Init@3(AudioHost host) => (); + [MinVersion=4] Init@3(pending_remote host_remote) => (); // Notify plug states of headphone, microphone, etc. Each switch state is // represented by the corresponding bit, if the bit is set then the switch diff --git a/chromium/components/arc/mojom/auth.mojom b/chromium/components/arc/mojom/auth.mojom index beea834ece7..caa93bdea8f 100644 --- a/chromium/components/arc/mojom/auth.mojom +++ b/chromium/components/arc/mojom/auth.mojom @@ -352,10 +352,10 @@ interface AuthHost { // Next Method ID: 6 interface AuthInstance { // DEPRECATED: Please use Init@2 instead. - InitDeprecated@0(AuthHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=12] Init@2(AuthHost host_ptr) => (); + [MinVersion=12] Init@2(pending_remote host_remote) => (); // Callback from RequestAccountInfo. This cannot be a normal callback since // the result can sometimes take a few minutes in some cases (Kiosk mode), diff --git a/chromium/components/arc/mojom/bluetooth.mojom b/chromium/components/arc/mojom/bluetooth.mojom index 495be3cb939..64e3f6d24f3 100644 --- a/chromium/components/arc/mojom/bluetooth.mojom +++ b/chromium/components/arc/mojom/bluetooth.mojom @@ -447,10 +447,10 @@ interface BluetoothHost { // Deprecated Method ID: 2, 6, 11, 12 interface BluetoothInstance { // DEPRECATED: Please use Init@18 instead. - InitDeprecated@0(BluetoothHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=7] Init@18(BluetoothHost host_ptr) => (); + [MinVersion=7] Init@18(pending_remote host_remote) => (); OnAdapterProperties@1(BluetoothStatus status, array properties); diff --git a/chromium/components/arc/mojom/boot_phase_monitor.mojom b/chromium/components/arc/mojom/boot_phase_monitor.mojom index 045270f3e1f..703df7f5dc2 100644 --- a/chromium/components/arc/mojom/boot_phase_monitor.mojom +++ b/chromium/components/arc/mojom/boot_phase_monitor.mojom @@ -15,8 +15,8 @@ interface BootPhaseMonitorHost { // Next method ID: 2 interface BootPhaseMonitorInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(BootPhaseMonitorHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@1(BootPhaseMonitorHost host_ptr) => (); + [MinVersion=1] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/camera.mojom b/chromium/components/arc/mojom/camera.mojom index 6437d710c2a..a90eb6524e7 100644 --- a/chromium/components/arc/mojom/camera.mojom +++ b/chromium/components/arc/mojom/camera.mojom @@ -96,7 +96,7 @@ interface CameraService { interface CameraHost { // Notifies Chrome that CameraService is requested and returns an interface // pointer bound to a newly created service. Used by camera HAL v1. - StartCameraService@0() => (CameraService service); + StartCameraService@0() => (pending_remote service); // Registers the camera HAL client. Used by camera HAL v3. [MinVersion=2] RegisterCameraHalClient@1( @@ -106,5 +106,5 @@ interface CameraHost { // Next method ID: 1 interface CameraInstance { // Establishes full-duplex communication with the host. - Init@0(CameraHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/cert_store.mojom b/chromium/components/arc/mojom/cert_store.mojom index 90f1d8c3ff9..24ec3fb3ed3 100644 --- a/chromium/components/arc/mojom/cert_store.mojom +++ b/chromium/components/arc/mojom/cert_store.mojom @@ -111,10 +111,10 @@ interface CertStoreHost { // Next method ID: 4 interface CertStoreInstance { // DEPRECATED: Please use Init@3 instead. - InitDeprecated@0(CertStoreHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@3(CertStoreHost host_ptr) => (); + [MinVersion=1] Init@3(pending_remote host_remote) => (); // Informs the key permissions are changed: only listed packages are allowed // to use exposed certificates. @@ -134,5 +134,5 @@ interface SmartCardManagerHost { // Next method ID: 1 interface SmartCardManagerInstance { // Establishes full-duplex communication with the host. - Init@0(SmartCardManagerHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/clipboard.mojom b/chromium/components/arc/mojom/clipboard.mojom index 11ea94683c4..452599c6f0a 100644 --- a/chromium/components/arc/mojom/clipboard.mojom +++ b/chromium/components/arc/mojom/clipboard.mojom @@ -53,10 +53,10 @@ interface ClipboardHost { // Deprecated method IDs: 1 interface ClipboardInstance { // DEPRECATED: Please use Init@3 instead. - InitDeprecated@0(ClipboardHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=2] Init@3(ClipboardHost host_ptr) => (); + [MinVersion=2] Init@3(pending_remote host_remote) => (); // Tells that the Host clipboard has been updated. [MinVersion=1] OnHostClipboardUpdated@2(); diff --git a/chromium/components/arc/mojom/crash_collector.mojom b/chromium/components/arc/mojom/crash_collector.mojom index 70611023476..b25907a42c3 100644 --- a/chromium/components/arc/mojom/crash_collector.mojom +++ b/chromium/components/arc/mojom/crash_collector.mojom @@ -24,8 +24,8 @@ interface CrashCollectorHost { // Next Method ID: 2 interface CrashCollectorInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(CrashCollectorHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=2] Init@1(CrashCollectorHost host_ptr) => (); + [MinVersion=2] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/disk_quota.mojom b/chromium/components/arc/mojom/disk_quota.mojom index 006537a6c95..68b4418ee06 100644 --- a/chromium/components/arc/mojom/disk_quota.mojom +++ b/chromium/components/arc/mojom/disk_quota.mojom @@ -18,5 +18,6 @@ interface DiskQuotaHost { // Next Method ID: 1 interface DiskQuotaInstance { - Init@0(DiskQuotaHost host_ptr) => (); + // Establishes full-duplex communication with the host. + Init@0(pending_remote host_remote) => (); }; \ No newline at end of file diff --git a/chromium/components/arc/mojom/enterprise_reporting.mojom b/chromium/components/arc/mojom/enterprise_reporting.mojom index 3130ee9fc54..a302a5d21bc 100644 --- a/chromium/components/arc/mojom/enterprise_reporting.mojom +++ b/chromium/components/arc/mojom/enterprise_reporting.mojom @@ -28,10 +28,11 @@ interface EnterpriseReportingHost { // Next method ID: 3 interface EnterpriseReportingInstance { // DEPRECATED: Please use Init@2 instead. - InitDeprecated@0(EnterpriseReportingHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=2] Init@2(EnterpriseReportingHost host_ptr) => (); + [MinVersion=2] Init@2( + pending_remote host_remote) => (); // Requests that a JSON status blob be generated and passed to the // host. diff --git a/chromium/components/arc/mojom/file_system.mojom b/chromium/components/arc/mojom/file_system.mojom index 4c3176e4a45..7a74c610d13 100644 --- a/chromium/components/arc/mojom/file_system.mojom +++ b/chromium/components/arc/mojom/file_system.mojom @@ -382,10 +382,10 @@ interface FileSystemInstance { (Document? document); // DEPRECATED: Please use Init@10 instead. - [MinVersion=3] InitDeprecated@5(FileSystemHost host_ptr); + [MinVersion=3] InitDeprecated@5(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=7] Init@10(FileSystemHost host_ptr) => (); + [MinVersion=7] Init@10(pending_remote host_remote) => (); // Asks the ContentResolver to get a FD to read the file specified by the // URL. diff --git a/chromium/components/arc/mojom/ime.mojom b/chromium/components/arc/mojom/ime.mojom index bae292e5e65..f65ee6679e0 100644 --- a/chromium/components/arc/mojom/ime.mojom +++ b/chromium/components/arc/mojom/ime.mojom @@ -43,6 +43,19 @@ struct CompositionSegment { bool emphasized; }; +// Represents the information of a key event. +struct KeyEventData { + // Whether the event is a press event or a release event. + bool pressed; + // The key touched in the event represented in |ui::KeyboardCode|. + int32 key_code; + // The flags for modifiers state. + bool is_shift_down; + bool is_control_down; + bool is_alt_down; + bool is_capslock_on; +}; + // Next method ID: 6 interface ImeHost { // Notifies Chrome that the text input focus is changed. @@ -97,10 +110,10 @@ interface ImeHost { // Next method ID: 8 interface ImeInstance { // DEPRECATED: Please use Init@6 instead. - InitDeprecated@0(ImeHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=6] Init@6(ImeHost host_ptr) => (); + [MinVersion=6] Init@6(pending_remote host_remote) => (); // Sets composition text and attributes requested by the host IME. SetCompositionText@1(string text, array segments); diff --git a/chromium/components/arc/mojom/ime.typemap b/chromium/components/arc/mojom/ime.typemap index d82e98af726..6d6c89dc7a9 100644 --- a/chromium/components/arc/mojom/ime.typemap +++ b/chromium/components/arc/mojom/ime.typemap @@ -1,4 +1,10 @@ mojom = "//components/arc/mojom/ime.mojom" -public_headers = [ "//ui/base/ime/text_input_type.h" ] +public_headers = [ + "//ui/base/ime/text_input_type.h", + "//ui/events/event.h", +] traits_headers = [ "//components/arc/mojom/ime_mojom_traits.h" ] -type_mappings = [ "arc.mojom.TextInputType=::ui::TextInputType" ] +type_mappings = [ + "arc.mojom.TextInputType=::ui::TextInputType", + "arc.mojom.KeyEventData=::std::unique_ptr<::ui::KeyEvent>[move_only]", +] diff --git a/chromium/components/arc/mojom/ime_mojom_traits.cc b/chromium/components/arc/mojom/ime_mojom_traits.cc new file mode 100644 index 00000000000..1687c4635c4 --- /dev/null +++ b/chromium/components/arc/mojom/ime_mojom_traits.cc @@ -0,0 +1,42 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/arc/mojom/ime_mojom_traits.h" + +#include "ui/events/keycodes/keyboard_code_conversion.h" + +namespace mojo { +using KeyEventUniquePtr = std::unique_ptr; + +bool StructTraits::Read( + arc::mojom::KeyEventDataDataView data, + KeyEventUniquePtr* out) { + const ui::EventType type = + data.pressed() ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED; + // TODO(yhanada): Currently we have no way to know the correct keyboard layout + // here, so assuming US layout. Find a way to get the more precise DomCode. + const ui::DomCode dom_code = ui::UsLayoutKeyboardCodeToDomCode( + static_cast(data.key_code())); + + int flags = 0; + if (data.is_shift_down()) + flags |= ui::EF_SHIFT_DOWN; + if (data.is_control_down()) + flags |= ui::EF_CONTROL_DOWN; + if (data.is_alt_down()) + flags |= ui::EF_ALT_DOWN; + if (data.is_capslock_on()) + flags |= ui::EF_CAPS_LOCK_ON; + + ui::KeyboardCode key_code; + ui::DomKey dom_key; + if (!DomCodeToUsLayoutDomKey(dom_code, flags, &dom_key, &key_code)) + return false; + + *out = std::make_unique(type, key_code, dom_code, flags, + dom_key, base::TimeTicks::Now()); + return true; +} + +} // namespace mojo diff --git a/chromium/components/arc/mojom/ime_mojom_traits.h b/chromium/components/arc/mojom/ime_mojom_traits.h index 5dc61426d55..3941f6161ec 100644 --- a/chromium/components/arc/mojom/ime_mojom_traits.h +++ b/chromium/components/arc/mojom/ime_mojom_traits.h @@ -5,8 +5,9 @@ #ifndef COMPONENTS_ARC_MOJOM_IME_MOJOM_TRAITS_H_ #define COMPONENTS_ARC_MOJOM_IME_MOJOM_TRAITS_H_ -#include "components/arc/mojom/ime.mojom-shared.h" +#include "components/arc/mojom/ime.mojom.h" #include "ui/base/ime/text_input_type.h" +#include "ui/events/event.h" namespace mojo { @@ -107,6 +108,32 @@ struct EnumTraits { } }; +using KeyEventUniquePtr = std::unique_ptr; +template <> +struct StructTraits { + static bool pressed(const KeyEventUniquePtr& key_event) { + return key_event->type() == ui::ET_KEY_PRESSED; + } + static int32_t key_code(const KeyEventUniquePtr& key_event) { + return key_event->key_code(); + } + static bool is_shift_down(const KeyEventUniquePtr& key_event) { + return key_event->IsShiftDown(); + } + static bool is_control_down(const KeyEventUniquePtr& key_event) { + return key_event->IsControlDown(); + } + static bool is_alt_down(const KeyEventUniquePtr& key_event) { + return key_event->IsAltDown(); + } + static bool is_capslock_on(const KeyEventUniquePtr& key_event) { + return key_event->IsCapsLockOn(); + } + + static bool Read(arc::mojom::KeyEventDataDataView data, + KeyEventUniquePtr* out); +}; + } // namespace mojo #endif // COMPONENTS_ARC_MOJOM_IME_MOJOM_TRAITS_H_ diff --git a/chromium/components/arc/mojom/ime_mojom_traits_unittest.cc b/chromium/components/arc/mojom/ime_mojom_traits_unittest.cc new file mode 100644 index 00000000000..7dfece3f529 --- /dev/null +++ b/chromium/components/arc/mojom/ime_mojom_traits_unittest.cc @@ -0,0 +1,45 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/arc/mojom/ime_mojom_traits.h" + +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/dom/dom_code.h" + +namespace mojo { + +namespace { + +void ExpectKeyEventsEqual(const ui::KeyEvent& expected, + const ui::KeyEvent& actual) { + EXPECT_EQ(expected.type(), actual.type()); + EXPECT_EQ(expected.key_code(), actual.key_code()); + EXPECT_EQ(expected.code(), actual.code()); + EXPECT_EQ(expected.IsShiftDown(), actual.IsShiftDown()); + EXPECT_EQ(expected.IsAltDown(), actual.IsAltDown()); + EXPECT_EQ(expected.IsControlDown(), actual.IsControlDown()); + EXPECT_EQ(expected.IsCapsLockOn(), actual.IsCapsLockOn()); +} + +} // namespace + +TEST(KeyEventStructTraitsTest, Convert) { + const ui::KeyEvent kTestData[] = { + {ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, ui::EF_CONTROL_DOWN}, + {ui::ET_KEY_PRESSED, ui::VKEY_B, ui::DomCode::US_B, ui::EF_ALT_DOWN}, + {ui::ET_KEY_RELEASED, ui::VKEY_B, ui::DomCode::US_B, ui::EF_SHIFT_DOWN}, + {ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, ui::EF_CAPS_LOCK_ON}, + }; + for (size_t idx = 0; idx < base::size(kTestData); ++idx) { + auto copy = std::make_unique(kTestData[idx]); + std::unique_ptr output; + mojo::test::SerializeAndDeserialize(©, + &output); + ExpectKeyEventsEqual(*copy, *output); + } +} + +} // namespace mojo diff --git a/chromium/components/arc/mojom/input_method_manager.mojom b/chromium/components/arc/mojom/input_method_manager.mojom index c8e3dcde029..273eac40c5f 100644 --- a/chromium/components/arc/mojom/input_method_manager.mojom +++ b/chromium/components/arc/mojom/input_method_manager.mojom @@ -59,20 +59,6 @@ struct TextInputState { [MinVersion=6] Range? composition_text_range; }; -// Represents the information of a key event. -[MinVersion=7] -struct KeyEventData { - // Whether the event is a press event or a release event. - bool pressed; - // The key touched in the event represented in |ui::KeyboardCode|. - int32 key_code; - // The flags for modifiers state. - bool is_shift_down; - bool is_control_down; - bool is_alt_down; - bool is_capslock_on; -}; - // This interface provides methods to control a text field. // It is generated for each focused text field and passed to Android. // This interface will be closed when the focus moves to another text field. @@ -135,7 +121,7 @@ interface InputMethodManagerHost { // Next method ID: 7 interface InputMethodManagerInstance { // Establishes full-duplex communication with the host. - Init@0(InputMethodManagerHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); // Enables/Disables an IME in Android. Calling this method will add/remove // the specified IME to/from ENABLED_INPUT_METHODS settings. diff --git a/chromium/components/arc/mojom/intent_helper.mojom b/chromium/components/arc/mojom/intent_helper.mojom index 3be1655ab7b..c37690fce69 100644 --- a/chromium/components/arc/mojom/intent_helper.mojom +++ b/chromium/components/arc/mojom/intent_helper.mojom @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Next MinVersion: 37 +// Next MinVersion: 39 module arc.mojom; @@ -82,6 +82,7 @@ struct IntentFilter { [MinVersion=10] array? data_paths; [MinVersion=10] array? deprecated_data_scheme_specific_parts; [MinVersion=21] string? package_name; // Package which registered the filter. + [MinVersion=38] array? mime_types; // Intent filer mime types. }; // Describes a package that can handle an intent. @@ -147,7 +148,7 @@ enum ChromePage { ABOUTBLANK, ABOUTDOWNLOADS, ABOUTHISTORY, - CROSTINIDISKRESIZE, + DEPRECATED_CROSTINIDISKRESIZE, ACCESSIBILITY, ACCOUNTMANAGER, ANDROIDAPPSDETAILS, @@ -314,7 +315,7 @@ interface IntentHelperHost { }; // Sends intents to ARC on behalf of Chrome. -// Next method ID: 19 +// Next method ID: 20 interface IntentHelperInstance { // Sets the given package as a preferred package. The next time an ACTION_VIEW // intent is sent with a URL that requires disambiguation, instead of opening @@ -354,10 +355,10 @@ interface IntentHelperInstance { ActionType action_type); // DEPRECATED: Please use Init@13 instead. - InitDeprecated@0(IntentHelperHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=19] Init@13(IntentHelperHost host_ptr) => (); + [MinVersion=19] Init@13(pending_remote host_remote) => (); // DEPRECATED. Use FileSystemInstance.OpenFileToRead() instead. [MinVersion=15] OpenFileToReadDeprecated@12(string url) => (handle? fd); @@ -413,4 +414,8 @@ interface IntentHelperInstance { CameraIntentAction action, array data) => (bool is_success); + + // Request ARC to send the domain verification status update for all packages + // to Chrome OS. + [MinVersion=37] RequestDomainVerificationStatusUpdate@19(); }; diff --git a/chromium/components/arc/mojom/keymaster.mojom b/chromium/components/arc/mojom/keymaster.mojom index 24475b72636..6aa80cf3284 100644 --- a/chromium/components/arc/mojom/keymaster.mojom +++ b/chromium/components/arc/mojom/keymaster.mojom @@ -12,13 +12,14 @@ module arc.mojom; // Host is implemented in Chrome. Listens until server and instance come online // and forwards a server handle to the instance. interface KeymasterHost { - GetServer@0() => (KeymasterServer server_ptr); + GetServer@0() => (pending_remote? server_remote); }; // Instance is implemented in ARC. Retrieves a server pointer from the host and // uses it to fulfill Android Keymaster operations. interface KeymasterInstance { - Init@0(KeymasterHost host_ptr) => (); + // Establishes full-duplex communication with the host. + Init@0(pending_remote host_remote) => (); }; // Server is implemented in arc-keymasterd in Chrome OS. This interface is the diff --git a/chromium/components/arc/mojom/kiosk.mojom b/chromium/components/arc/mojom/kiosk.mojom index 39beb508f79..852991b17a7 100644 --- a/chromium/components/arc/mojom/kiosk.mojom +++ b/chromium/components/arc/mojom/kiosk.mojom @@ -23,8 +23,8 @@ interface KioskHost { // Next method ID: 2 interface KioskInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(KioskHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@1(KioskHost host_ptr) => (); + [MinVersion=1] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/metrics.mojom b/chromium/components/arc/mojom/metrics.mojom index c5befd6601b..b076bb6031e 100644 --- a/chromium/components/arc/mojom/metrics.mojom +++ b/chromium/components/arc/mojom/metrics.mojom @@ -108,8 +108,8 @@ interface MetricsHost { // Next method ID: 2 interface MetricsInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(MetricsHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=2] Init@1(MetricsHost host_ptr) => (); + [MinVersion=2] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/midis.mojom b/chromium/components/arc/mojom/midis.mojom index 4a8e1735e20..18d9e40376e 100644 --- a/chromium/components/arc/mojom/midis.mojom +++ b/chromium/components/arc/mojom/midis.mojom @@ -66,7 +66,8 @@ interface MidisServer { // by the client). // Next Method ID: 1 interface MidisHost { - Connect@0(MidisServer& server, MidisClient client); + Connect@0( + pending_receiver server, pending_remote client); }; // MidisInstance is implemented in the ARC MIDI JNI code that @@ -74,8 +75,8 @@ interface MidisHost { // Next Method ID: 2 interface MidisInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(MidisHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@1(MidisHost host_ptr) => (); + [MinVersion=1] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/net.mojom b/chromium/components/arc/mojom/net.mojom index 81791af10f1..8faca92ca21 100644 --- a/chromium/components/arc/mojom/net.mojom +++ b/chromium/components/arc/mojom/net.mojom @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Next MinVersion: 14 +// Next MinVersion: 15 // This file defines the mojo interface between the ARC networking stack and // Chrome OS. There are three different groups of interactions: @@ -92,7 +92,8 @@ enum IPAddressType { IPV6, }; -// Layer 3 and proxy configuration information for an IP network. +// Deprecated. Individual fields added to NetworkConfiguration in version 13 of +// this file should be used instead. struct IPConfiguration { // Literal representation of the IP address of the ARC gateway. @@ -126,7 +127,8 @@ enum SecurityType { WPA_EAP, }; -// Tethering state of a |NetworkConfiguration| as a client. +// Deprecated enum. |is_metered| in NetworkConfiguration should be +// used instead. [Extensible] enum TetheringClientState { // Tethering state is detected and confirmed. @@ -189,8 +191,9 @@ struct NetworkConfiguration { // A string token that uniquely identifies this network service. string guid; - // IP configuration for the network service inside ARC. - array? ip_configs; + // Deprecated. Individual fields added to NetworkConfiguration in version 13 + // of this file should be used instead. + array? deprecated_ip_configs; // Deprecated field unused from ARC P and later. string? deprecated_mac_address; @@ -201,9 +204,8 @@ struct NetworkConfiguration { // Additional WiFi properties for WiFi network services. WiFi? wifi; - // Indicates if the physical network is known to have upstream Internet - // access through tethering on a metered network. - [MinVersion=8] TetheringClientState tethering_client_state; + // Deprecated field. Uses |is_metered| instead. + [MinVersion=8] TetheringClientState deprecated_tethering_client_state; // The name of the network interface on the host. [MinVersion=10] string? network_interface; @@ -266,6 +268,10 @@ struct NetworkConfiguration { // to ARC and associated with the network service. This can be different // from the name of the real physical interface managed by shill. [MinVersion=13] string? arc_network_interface; + + // True if the network has been autodetected by the platform as a metered + // network or if the user explicitly marked the network as metered in the UI. + [MinVersion=14] bool is_metered; }; // Describes a Wifi network configuration that ARC has requested the host to @@ -399,10 +405,10 @@ interface NetHost { // ID 2 is missing as it belonged to deprecated method. interface NetInstance { // DEPRECATED: Please use Init@6 instead. - InitDeprecated@0(NetHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=8] Init@6(NetHost host_ptr) => (); + [MinVersion=8] Init@6(pending_remote host_remote) => (); // Notifies the instance of a WiFI AP scan being completed. [MinVersion=1] ScanCompleted@1(); diff --git a/chromium/components/arc/mojom/notifications.mojom b/chromium/components/arc/mojom/notifications.mojom index 96cee17676a..db460d4f7c0 100644 --- a/chromium/components/arc/mojom/notifications.mojom +++ b/chromium/components/arc/mojom/notifications.mojom @@ -243,10 +243,10 @@ interface NotificationsHost { // TODO(lhchavez): Migrate all request/response messages to Mojo. interface NotificationsInstance { // DEPRECATED: Please use Init@5 instead. - InitDeprecated@0(NotificationsHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=14] Init@5(NotificationsHost host_ptr) => (); + [MinVersion=14] Init@5(pending_remote host_remote) => (); // Sends an event from Chrome notification UI to Android. // |event| is a type of occured event. diff --git a/chromium/components/arc/mojom/obb_mounter.mojom b/chromium/components/arc/mojom/obb_mounter.mojom index d3659753320..403522cba02 100644 --- a/chromium/components/arc/mojom/obb_mounter.mojom +++ b/chromium/components/arc/mojom/obb_mounter.mojom @@ -21,8 +21,8 @@ interface ObbMounterHost { // Next Method ID: 2 interface ObbMounterInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(ObbMounterHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@1(ObbMounterHost host_ptr) => (); + [MinVersion=1] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/oemcrypto.mojom b/chromium/components/arc/mojom/oemcrypto.mojom index 2c4b74737da..c7d050def44 100644 --- a/chromium/components/arc/mojom/oemcrypto.mojom +++ b/chromium/components/arc/mojom/oemcrypto.mojom @@ -342,7 +342,7 @@ interface OemCryptoService { // that runs in Chrome OS. // Next Method ID: 1 interface OemCryptoHost { - Connect@0(OemCryptoService& oemcryptor); + Connect@0(pending_receiver oemcryptor); }; // OemCryptoInstance is implemented in the liboemcrypto.so library that runs in @@ -350,8 +350,8 @@ interface OemCryptoHost { // Next Method ID: 2 interface OemCryptoInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(OemCryptoHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@1(OemCryptoHost host_ptr) => (); + [MinVersion=1] Init@1(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/oemcrypto_daemon.mojom b/chromium/components/arc/mojom/oemcrypto_daemon.mojom deleted file mode 100644 index 2988433412c..00000000000 --- a/chromium/components/arc/mojom/oemcrypto_daemon.mojom +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// The original version of this file lives in the Chromium repository at: -// src/components/arc/mojom/oemcrypto_daemon.mojom - -// This file defines the mojo interface used between Chrome and the Chrome OS -// daemon for establishing the connection from Android to the Chrome OS -// daemon. This is used so Chrome can proxy the OemCryptoService implementation -// over to the daemon and then also hand the daemon a Mojo connection to the -// GPU process for dealing with secure buffers. - -module arc_oemcrypto.mojom; - -import "components/arc/mojom/oemcrypto.mojom"; -import "components/arc/mojom/protected_buffer_manager.mojom"; - -// OemCryptoHostDaemon is implemented by the OemCrypto daemon running in -// Chrome OS and has Connect called from the Browser process in Chrome. -// Next Method ID: 1 -interface OemCryptoHostDaemon { - Connect@0( - arc.mojom.OemCryptoService& oemcryptor, - pending_remote protected_buffer_manager); -}; diff --git a/chromium/components/arc/mojom/pip.mojom b/chromium/components/arc/mojom/pip.mojom index ce620e0f570..8d38010bc40 100644 --- a/chromium/components/arc/mojom/pip.mojom +++ b/chromium/components/arc/mojom/pip.mojom @@ -28,7 +28,7 @@ interface PipHost { // Next Method ID: 3 interface PipInstance { // Establishes full-duplex communication with the host. - Init@0(PipHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); // Instruct Android to close the current Android PIP window, if it exists. // This is used if the user initiates a Chrome side PIP window, since we diff --git a/chromium/components/arc/mojom/policy.mojom b/chromium/components/arc/mojom/policy.mojom index 71489020452..820e8a435be 100644 --- a/chromium/components/arc/mojom/policy.mojom +++ b/chromium/components/arc/mojom/policy.mojom @@ -98,10 +98,10 @@ interface PolicyHost { // Next Method ID: 4 interface PolicyInstance { // DEPRECATED: Please use Init@2 instead. - InitDeprecated@0(PolicyHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=2] Init@2(PolicyHost host_ptr) => (); + [MinVersion=2] Init@2(pending_remote host_remote) => (); // Indicates some policies have changed OnPolicyUpdated@1(); diff --git a/chromium/components/arc/mojom/power.mojom b/chromium/components/arc/mojom/power.mojom index b3304bf856c..ac17a824e8c 100644 --- a/chromium/components/arc/mojom/power.mojom +++ b/chromium/components/arc/mojom/power.mojom @@ -36,10 +36,10 @@ interface PowerHost { // Next method ID: 7 interface PowerInstance { // DEPRECATED: Please use Init@5 instead. - InitDeprecated@0(PowerHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=4] Init@5(PowerHost host_ptr) => (); + [MinVersion=4] Init@5(pending_remote host_remote) => (); // Alerts the instance to a change in interactive state. [MinVersion=1] SetInteractive@1(bool enabled); diff --git a/chromium/components/arc/mojom/print_spooler.mojom b/chromium/components/arc/mojom/print_spooler.mojom index c84183d2970..b0322e7e299 100644 --- a/chromium/components/arc/mojom/print_spooler.mojom +++ b/chromium/components/arc/mojom/print_spooler.mojom @@ -42,16 +42,17 @@ interface PrintSpoolerHost { // The |top_margin| is the height of the space at the top of the window. // The returned |host| will be null if errors occur while saving the print // document or locating the Android surface. - [MinVersion=1] StartPrintInCustomTab@0(handle scoped_handle, - int32 task_id, - int32 surface_id, - int32 top_margin, - PrintSessionInstance instance) + [MinVersion=1] StartPrintInCustomTab@0( + handle scoped_handle, + int32 task_id, + int32 surface_id, + int32 top_margin, + pending_remote instance) => (PrintSessionHost? host); }; // Next method ID: 1 interface PrintSpoolerInstance { // Establishes full-duplex communication with the host. - [MinVersion=0] Init@0(PrintSpoolerHost host_ptr) => (); + [MinVersion=0] Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/screen_capture.mojom b/chromium/components/arc/mojom/screen_capture.mojom index 960012ca016..ca72e9164f1 100644 --- a/chromium/components/arc/mojom/screen_capture.mojom +++ b/chromium/components/arc/mojom/screen_capture.mojom @@ -42,9 +42,9 @@ interface ScreenCaptureHost { // size should have the width/height of the buffers used // returns null interface in the case the permission was not granted, a valid // interface pointer otherwise - OpenSession@1(ScreenCaptureSessionNotifier notifier, - string package_name, Size size) => - (ScreenCaptureSession? session); + OpenSession@1(pending_remote notifier, + string package_name, Size size) + => (pending_remote? session); }; // Implemented by Chrome for handling a screen capture session. @@ -61,7 +61,7 @@ interface ScreenCaptureSession { // Implemented by Android. interface ScreenCaptureInstance { // Establishes full-duplex communication with the host. - Init@0(ScreenCaptureHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; // Implemented by Android as a callback mechanism. diff --git a/chromium/components/arc/mojom/timer.mojom b/chromium/components/arc/mojom/timer.mojom index 4d8760e3096..b79b3f4e521 100644 --- a/chromium/components/arc/mojom/timer.mojom +++ b/chromium/components/arc/mojom/timer.mojom @@ -50,5 +50,5 @@ interface TimerHost { // Next method ID: 1 interface TimerInstance { // Establishes full-duplex communication with the host. - Init@0(TimerHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/tts.mojom b/chromium/components/arc/mojom/tts.mojom index b013a1b2c21..540ac41f8c0 100644 --- a/chromium/components/arc/mojom/tts.mojom +++ b/chromium/components/arc/mojom/tts.mojom @@ -33,10 +33,10 @@ interface TtsHost { // Next Method ID: 4 interface TtsInstance { // DEPRECATED: Please use Init@3 instead. - InitDeprecated@0(TtsHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=1] Init@3(TtsHost host_ptr) => (); + [MinVersion=1] Init@3(pending_remote host_remote) => (); // Sends an utterance to Android for synthesis. Speak@1(TtsUtterance utterance); diff --git a/chromium/components/arc/mojom/usb_host.mojom b/chromium/components/arc/mojom/usb_host.mojom index ff41f57cbaa..e31181baf9b 100644 --- a/chromium/components/arc/mojom/usb_host.mojom +++ b/chromium/components/arc/mojom/usb_host.mojom @@ -49,7 +49,7 @@ interface UsbHostHost { // Next method ID: 3 interface UsbHostInstance { // Establishes full-duplex communication with the host. - Init@0(UsbHostHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); // Notifies the instance of a new USB device. // Only packages in |event_receiver_packages| will receive broadcast. diff --git a/chromium/components/arc/mojom/video.mojom b/chromium/components/arc/mojom/video.mojom index 22f2558e8c9..f887d6690b0 100644 --- a/chromium/components/arc/mojom/video.mojom +++ b/chromium/components/arc/mojom/video.mojom @@ -24,22 +24,25 @@ interface VideoHost { // Next method ID: 2 interface VideoInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(VideoHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=5] Init@1(VideoHost host_ptr) => (); + [MinVersion=5] Init@1(pending_remote host_remote) => (); }; // Deprecated method IDs: 0 // Next method ID: 4 interface VideoAcceleratorFactory { [MinVersion=1] - CreateEncodeAccelerator@1(VideoEncodeAccelerator& video_encoder); + CreateEncodeAccelerator@1( + pending_receiver video_encoder); [MinVersion=6] - CreateDecodeAccelerator@2(VideoDecodeAccelerator& video_decoder); + CreateDecodeAccelerator@2( + pending_receiver video_decoder); [Minversion=7] CreateProtectedBufferAllocator@3( - VideoProtectedBufferAllocator& video_protected_buffer_allocator); + pending_receiver + video_protected_buffer_allocator); }; diff --git a/chromium/components/arc/mojom/video_decode_accelerator.mojom b/chromium/components/arc/mojom/video_decode_accelerator.mojom index 56bf60dd8c3..d859c7f1eac 100644 --- a/chromium/components/arc/mojom/video_decode_accelerator.mojom +++ b/chromium/components/arc/mojom/video_decode_accelerator.mojom @@ -95,7 +95,7 @@ interface VideoDecodeAccelerator { // The caller needs to wait for the initialization result (returned by // callback) before calling any other methods. Initialize@0(VideoDecodeAcceleratorConfig config, - VideoDecodeClient client) => (Result result); + pending_remote client) => (Result result); // Decodes the content in the shared memory of the bitstream buffer. The // callee needs to map the the shared memory to read the content and is diff --git a/chromium/components/arc/mojom/video_encode_accelerator.mojom b/chromium/components/arc/mojom/video_encode_accelerator.mojom index fc493685fcc..40231b20899 100644 --- a/chromium/components/arc/mojom/video_encode_accelerator.mojom +++ b/chromium/components/arc/mojom/video_encode_accelerator.mojom @@ -84,7 +84,7 @@ interface VideoEncodeAccelerator { [MinVersion=4] Initialize@9(VideoEncodeAcceleratorConfig config, - VideoEncodeClient client) => (Result result); + pending_remote client) => (Result result); // Initializes the video encoder with specific configuration. Called once per // encoder construction. @@ -97,7 +97,8 @@ interface VideoEncodeAccelerator { // invoke any other methods before the callback. [MinVersion=1] InitializeDeprecated@7(VideoEncodeAcceleratorConfig config, - VideoEncodeClient client) => (bool success); + pending_remote client) => + (bool success); // Encodes the given frame. // Parameters: diff --git a/chromium/components/arc/mojom/voice_interaction_arc_home.mojom b/chromium/components/arc/mojom/voice_interaction_arc_home.mojom index f91b1be6e4c..ba0e4a57adc 100644 --- a/chromium/components/arc/mojom/voice_interaction_arc_home.mojom +++ b/chromium/components/arc/mojom/voice_interaction_arc_home.mojom @@ -63,8 +63,9 @@ interface VoiceInteractionArcHomeHost { // Next method ID: 2 interface VoiceInteractionArcHomeInstance { // DEPRECATED: Please use Init@1 instead. - InitDeprecated@0(VoiceInteractionArcHomeHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=3] Init@1(VoiceInteractionArcHomeHost host_ptr) => (); + [MinVersion=3] Init@1( + pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/voice_interaction_framework.mojom b/chromium/components/arc/mojom/voice_interaction_framework.mojom index 2873ad90dfa..3b1b055cbb3 100644 --- a/chromium/components/arc/mojom/voice_interaction_framework.mojom +++ b/chromium/components/arc/mojom/voice_interaction_framework.mojom @@ -50,10 +50,11 @@ struct VoiceInteractionStatus { // Deprecated method ID: 4 interface VoiceInteractionFrameworkInstance { // DEPRECATED: Please use Init@11 instead. - InitDeprecated@0(VoiceInteractionFrameworkHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=13] Init@11(VoiceInteractionFrameworkHost host_ptr) => (); + [MinVersion=13] Init@11( + pending_remote host_remote) => (); // Starts the voice interaction session in container. // |homescreen_is_active| is true if the session was invoked when homescreen is active. diff --git a/chromium/components/arc/mojom/volume_mounter.mojom b/chromium/components/arc/mojom/volume_mounter.mojom index c8059971fa9..d919be789af 100644 --- a/chromium/components/arc/mojom/volume_mounter.mojom +++ b/chromium/components/arc/mojom/volume_mounter.mojom @@ -55,7 +55,7 @@ interface VolumeMounterHost { // Next Method ID: 2 interface VolumeMounterInstance { // Establishes full-duplex communication with the host. - [MinVersion=1] Init@0(VolumeMounterHost host_ptr) => (); + [MinVersion=1] Init@0(pending_remote host_remote) => (); // Triggers a mount event in Android. OnMountEvent@1(MountPointInfo mount_point_info); diff --git a/chromium/components/arc/mojom/wake_lock.mojom b/chromium/components/arc/mojom/wake_lock.mojom index 44217547422..b1f689189b0 100644 --- a/chromium/components/arc/mojom/wake_lock.mojom +++ b/chromium/components/arc/mojom/wake_lock.mojom @@ -25,5 +25,5 @@ interface WakeLockHost { // Next method ID: 1 interface WakeLockInstance { // Establishes full-duplex communication with the host. - Init@0(WakeLockHost host_ptr) => (); + Init@0(pending_remote host_remote) => (); }; diff --git a/chromium/components/arc/mojom/wallpaper.mojom b/chromium/components/arc/mojom/wallpaper.mojom index 3048e4560a7..cab9703c639 100644 --- a/chromium/components/arc/mojom/wallpaper.mojom +++ b/chromium/components/arc/mojom/wallpaper.mojom @@ -24,10 +24,10 @@ interface WallpaperHost { // Next method ID: 4 interface WallpaperInstance { // DEPRECATED: Please use Init@3 instead. - InitDeprecated@0(WallpaperHost host_ptr); + InitDeprecated@0(pending_remote host_remote); // Establishes full-duplex communication with the host. - [MinVersion=3] Init@3(WallpaperHost host_ptr) => (); + [MinVersion=3] Init@3(pending_remote host_remote) => (); // Notifies ArcWallpaperManagerService that wallpaper is changed. [MinVersion=1] OnWallpaperChanged@1([MinVersion=2] int32 wallpaper_id); diff --git a/chromium/components/arc/net/OWNERS b/chromium/components/arc/net/OWNERS new file mode 100644 index 00000000000..522f9d41ab5 --- /dev/null +++ b/chromium/components/arc/net/OWNERS @@ -0,0 +1,3 @@ +hugobenichi@google.com + +# COMPONENT: Platform>Apps>ARC diff --git a/chromium/components/arc/net/arc_net_host_impl.cc b/chromium/components/arc/net/arc_net_host_impl.cc index 6b5aa63e8f5..be411029899 100644 --- a/chromium/components/arc/net/arc_net_host_impl.cc +++ b/chromium/components/arc/net/arc_net_host_impl.cc @@ -13,8 +13,6 @@ #include "base/memory/singleton.h" #include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" #include "chromeos/login/login_state/login_state.h" #include "chromeos/network/device_state.h" #include "chromeos/network/managed_network_configuration_handler.h" @@ -24,7 +22,6 @@ #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_type_pattern.h" -#include "chromeos/network/network_util.h" #include "chromeos/network/onc/onc_utils.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h" #include "components/arc/arc_prefs.h" @@ -36,10 +33,6 @@ namespace { constexpr int kGetNetworksListLimit = 100; -// Delay in millisecond before asking for a network property update when no IP -// configuration can be retrieved for a network. -constexpr base::TimeDelta kNetworkPropertyUpdateDelay = - base::TimeDelta::FromMilliseconds(5000); chromeos::NetworkStateHandler* GetStateHandler() { return chromeos::NetworkHandler::Get()->network_state_handler(); @@ -70,171 +63,25 @@ bool IsDeviceOwner() { user_manager::UserManager::Get()->GetOwnerAccountId(); } -std::string GetStringFromONCDictionary(const base::Value* dict, - const char* key, - bool required) { - DCHECK(dict->is_dict()); - const base::Value* string_value = - dict->FindKeyOfType(key, base::Value::Type::STRING); - if (!string_value) { - LOG_IF(ERROR, required) << "Required property " << key << " not found."; - return std::string(); - } - std::string result = string_value->GetString(); - LOG_IF(ERROR, required && result.empty()) - << "Required property " << key << " is empty."; - return result; -} - -arc::mojom::SecurityType TranslateONCWifiSecurityType( - const base::DictionaryValue* dict) { - std::string type = GetStringFromONCDictionary(dict, onc::wifi::kSecurity, - true /* required */); - if (type == onc::wifi::kWEP_PSK) +arc::mojom::SecurityType TranslateWiFiSecurity(const std::string& type) { + if (type == shill::kSecurityNone) + return arc::mojom::SecurityType::NONE; + if (type == shill::kSecurityWep) return arc::mojom::SecurityType::WEP_PSK; - if (type == onc::wifi::kWEP_8021X) - return arc::mojom::SecurityType::WEP_8021X; - if (type == onc::wifi::kWPA_PSK) + if (type == shill::kSecurityPsk) + return arc::mojom::SecurityType::WPA_PSK; + if (type == shill::kSecurityWpa) return arc::mojom::SecurityType::WPA_PSK; - if (type == onc::wifi::kWPA_EAP) + if (type == shill::kSecurity8021x) + return arc::mojom::SecurityType::WPA_EAP; + // Robust Security Network does not appear to be defined in Android. + // Approximate it with WPA_EAP + if (type == shill::kSecurityRsn) return arc::mojom::SecurityType::WPA_EAP; + LOG(WARNING) << "Unknown WiFi security type " << type; return arc::mojom::SecurityType::NONE; } -arc::mojom::TetheringClientState TranslateTetheringState( - const std::string& tethering_state) { - if (tethering_state == onc::tethering_state::kTetheringConfirmedState) - return arc::mojom::TetheringClientState::CONFIRMED; - else if (tethering_state == onc::tethering_state::kTetheringNotDetectedState) - return arc::mojom::TetheringClientState::NOT_DETECTED; - else if (tethering_state == onc::tethering_state::kTetheringSuspectedState) - return arc::mojom::TetheringClientState::SUSPECTED; - NOTREACHED() << "Invalid tethering state: " << tethering_state; - return arc::mojom::TetheringClientState::NOT_DETECTED; -} - -arc::mojom::WiFiPtr TranslateONCWifi(const base::DictionaryValue* dict) { - arc::mojom::WiFiPtr wifi = arc::mojom::WiFi::New(); - - // Optional; defaults to 0. - dict->GetInteger(onc::wifi::kFrequency, &wifi->frequency); - - wifi->bssid = - GetStringFromONCDictionary(dict, onc::wifi::kBSSID, false /* required */); - wifi->hex_ssid = GetStringFromONCDictionary(dict, onc::wifi::kHexSSID, - true /* required */); - - // Optional; defaults to false. - dict->GetBoolean(onc::wifi::kHiddenSSID, &wifi->hidden_ssid); - - wifi->security = TranslateONCWifiSecurityType(dict); - - // Optional; defaults to 0. - dict->GetInteger(onc::wifi::kSignalStrength, &wifi->signal_strength); - - return wifi; -} - -// Extracts WiFi's tethering client state from a dictionary of WiFi properties. -arc::mojom::TetheringClientState GetWifiTetheringClientState( - const base::DictionaryValue* dict) { - std::string tethering_state; - dict->GetString(onc::wifi::kTetheringState, &tethering_state); - return TranslateTetheringState(tethering_state); -} - -arc::mojom::IPConfigurationPtr TranslateONCIPConfig( - const base::Value* ip_dict) { - DCHECK(ip_dict->is_dict()); - - arc::mojom::IPConfigurationPtr configuration = - arc::mojom::IPConfiguration::New(); - - const base::Value* ip_address = ip_dict->FindKeyOfType( - onc::ipconfig::kIPAddress, base::Value::Type::STRING); - if (ip_address && !ip_address->GetString().empty()) { - configuration->ip_address = ip_address->GetString(); - const base::Value* routing_prefix = ip_dict->FindKeyOfType( - onc::ipconfig::kRoutingPrefix, base::Value::Type::INTEGER); - if (routing_prefix) - configuration->routing_prefix = routing_prefix->GetInt(); - else - LOG(ERROR) << "Required property RoutingPrefix not found."; - configuration->gateway = GetStringFromONCDictionary( - ip_dict, onc::ipconfig::kGateway, true /* required */); - } - - const base::Value* name_servers = ip_dict->FindKeyOfType( - onc::ipconfig::kNameServers, base::Value::Type::LIST); - if (name_servers) { - for (const auto& entry : name_servers->GetList()) - configuration->name_servers.push_back(entry.GetString()); - } - - const base::Value* type = - ip_dict->FindKeyOfType(onc::ipconfig::kType, base::Value::Type::STRING); - configuration->type = type && type->GetString() == onc::ipconfig::kIPv6 - ? arc::mojom::IPAddressType::IPV6 - : arc::mojom::IPAddressType::IPV4; - - configuration->web_proxy_auto_discovery_url = GetStringFromONCDictionary( - ip_dict, onc::ipconfig::kWebProxyAutoDiscoveryUrl, false /* required */); - - return configuration; -} - -// Returns true if the IP configuration is valid enough for ARC. Empty IP -// config objects can be generated when IPv4 DHCP or IPv6 autoconf has not -// completed yet. -bool IsValidIPConfiguration(const arc::mojom::IPConfiguration& ip_config) { - return !ip_config.ip_address.empty() && !ip_config.gateway.empty(); -} - -// Returns an IPConfiguration vector from the IPConfigs ONC property, which may -// include multiple IP configurations (e.g. IPv4 and IPv6). -std::vector IPConfigurationsFromONCIPConfigs( - const base::Value* dict) { - const base::Value* ip_config_list = - dict->FindKey(onc::network_config::kIPConfigs); - if (!ip_config_list || !ip_config_list->is_list()) - return {}; - std::vector result; - for (const auto& entry : ip_config_list->GetList()) { - arc::mojom::IPConfigurationPtr config = TranslateONCIPConfig(&entry); - if (config && IsValidIPConfiguration(*config)) - result.push_back(std::move(config)); - } - return result; -} - -// Returns an IPConfiguration vector from ONC property |property|, which will -// include a single IP configuration. -std::vector IPConfigurationsFromONCProperty( - const base::Value* dict, - const char* property_key) { - const base::Value* ip_dict = dict->FindKey(property_key); - if (!ip_dict) - return {}; - arc::mojom::IPConfigurationPtr config = TranslateONCIPConfig(ip_dict); - if (!config || !IsValidIPConfiguration(*config)) - return {}; - std::vector result; - result.push_back(std::move(config)); - return result; -} - -arc::mojom::ConnectionStateType TranslateONCConnectionState( - const base::DictionaryValue* dict) { - std::string connection_state = GetStringFromONCDictionary( - dict, onc::network_config::kConnectionState, false /* required */); - - if (connection_state == onc::connection_state::kConnected) - return arc::mojom::ConnectionStateType::CONNECTED; - if (connection_state == onc::connection_state::kConnecting) - return arc::mojom::ConnectionStateType::CONNECTING; - return arc::mojom::ConnectionStateType::NOT_CONNECTED; -} - // Translates a shill connection state into a mojo ConnectionStateType. // This is effectively the inverse function of shill.Service::GetStateString // defined in platform2/shill/service.cc, with in addition some of shill's @@ -273,54 +120,47 @@ bool IsActiveNetworkState(const chromeos::NetworkState* network) { state == shill::kStatePortalSuspected; } -void TranslateONCNetworkTypeDetails(const base::DictionaryValue* dict, - arc::mojom::NetworkConfiguration* mojo) { - std::string type = GetStringFromONCDictionary( - dict, onc::network_config::kType, true /* required */); - // This property will be updated as required by the relevant network types - // below. - mojo->tethering_client_state = arc::mojom::TetheringClientState::NOT_DETECTED; - if (type == onc::network_type::kCellular) { - mojo->type = arc::mojom::NetworkType::CELLULAR; - } else if (type == onc::network_type::kEthernet) { - mojo->type = arc::mojom::NetworkType::ETHERNET; - } else if (type == onc::network_type::kVPN) { - mojo->type = arc::mojom::NetworkType::VPN; - } else if (type == onc::network_type::kWiFi) { - mojo->type = arc::mojom::NetworkType::WIFI; - const base::DictionaryValue* wifi_dict = nullptr; - dict->GetDictionary(onc::network_config::kWiFi, &wifi_dict); - DCHECK(wifi_dict); - mojo->wifi = TranslateONCWifi(wifi_dict); - mojo->tethering_client_state = GetWifiTetheringClientState(wifi_dict); - } else { - NOTREACHED() << "Unknown network type: " << type; - } -} - -// Parse a shill IPConfig dictionary and appends the resulting mojo -// IPConfiguration object to the given |ip_configs| vector. -void AddIpConfiguration(std::vector& ip_configs, +arc::mojom::NetworkType TranslateNetworkType(const std::string& type) { + if (type == shill::kTypeWifi) + return arc::mojom::NetworkType::WIFI; + if (type == shill::kTypeVPN) + return arc::mojom::NetworkType::VPN; + if (type == shill::kTypeEthernet) + return arc::mojom::NetworkType::ETHERNET; + if (type == shill::kTypeEthernetEap) + return arc::mojom::NetworkType::ETHERNET; + if (type == shill::kTypeCellular) + return arc::mojom::NetworkType::CELLULAR; + NOTREACHED() << "Unknown network type: " << type; + return arc::mojom::NetworkType::ETHERNET; +} + +// Parses a shill IPConfig dictionary and adds the relevant fields to +// the given |network| NetworkConfiguration object. +void AddIpConfiguration(arc::mojom::NetworkConfiguration* network, const base::Value* shill_ipconfig) { if (!shill_ipconfig || !shill_ipconfig->is_dict()) return; - auto ip_config = arc::mojom::IPConfiguration::New(); - if (const auto* address_property = - shill_ipconfig->FindStringPath(shill::kAddressProperty)) - ip_config->ip_address = *address_property; - - if (const auto* gateway_property = - shill_ipconfig->FindStringPath(shill::kGatewayProperty)) - ip_config->gateway = *gateway_property; - - ip_config->routing_prefix = + // Only set the IP address and gateway if both are defined and non empty. + const auto* address = shill_ipconfig->FindStringPath(shill::kAddressProperty); + const auto* gateway = shill_ipconfig->FindStringPath(shill::kGatewayProperty); + const int prefixlen = shill_ipconfig->FindIntPath(shill::kPrefixlenProperty).value_or(0); + if (address && !address->empty() && gateway && !gateway->empty()) { + if (prefixlen < 64) { + network->host_ipv4_prefix_length = prefixlen; + network->host_ipv4_address = *address; + network->host_ipv4_gateway = *gateway; + } else { + network->host_ipv6_prefix_length = prefixlen; + network->host_ipv6_global_addresses->push_back(*address); + network->host_ipv6_gateway = *gateway; + } + } - ip_config->type = (ip_config->routing_prefix < 64) - ? arc::mojom::IPAddressType::IPV4 - : arc::mojom::IPAddressType::IPV6; - + // If the user has overridden DNS with the "Google nameservers" UI options, + // the kStaticIPConfigProperty object will be empty except for DNS addresses. if (const auto* dns_list = shill_ipconfig->FindListKey(shill::kNameServersProperty)) { for (const auto& dns_value : dns_list->GetList()) { @@ -333,80 +173,80 @@ void AddIpConfiguration(std::vector& ip_configs, if (dns == "0.0.0.0") continue; - ip_config->name_servers.push_back(dns); + network->host_dns_addresses->push_back(dns); } } - if (IsValidIPConfiguration(*ip_config)) - ip_configs.push_back(std::move(ip_config)); -} - -// Add shill's Device properties to the given mojo NetworkConfiguration objects. -// This adds the network interface and current IP configurations. -void AddDeviceProperties(arc::mojom::NetworkConfiguration* network, - const std::string& device_path) { - const auto* device = GetStateHandler()->GetDeviceState(device_path); - if (!device) - return; - - network->network_interface = device->interface(); - - std::vector ip_configs; - for (const auto& kv : device->ip_configs()) - AddIpConfiguration(ip_configs, kv.second.get()); + if (const auto* domains = + shill_ipconfig->FindKey(shill::kSearchDomainsProperty)) { + if (domains->is_list()) { + for (const auto& domain : domains->GetList()) + network->host_search_domains->push_back(domain.GetString()); + } + } - // If the DeviceState had any IP configuration, always use them and ignore - // any other IP configuration previously obtained through NetworkState. - if (!ip_configs.empty()) - network->ip_configs = std::move(ip_configs); + const int mtu = shill_ipconfig->FindIntPath(shill::kMtuProperty).value_or(0); + if (mtu > 0) + network->host_mtu = mtu; } -arc::mojom::NetworkConfigurationPtr TranslateONCConfiguration( +arc::mojom::NetworkConfigurationPtr TranslateNetworkProperties( const chromeos::NetworkState* network_state, - const base::Value* shill_dict, - const base::DictionaryValue* onc_dict) { + const base::Value* shill_dict) { auto mojo = arc::mojom::NetworkConfiguration::New(); - - mojo->connection_state = TranslateONCConnectionState(onc_dict); - - mojo->guid = GetStringFromONCDictionary(onc_dict, onc::network_config::kGUID, - true /* required */); - - // crbug.com/761708 - VPNs do not currently have an IPConfigs array, - // so in order to fetch the parameters (particularly the DNS server list), - // fall back to StaticIPConfig or SavedIPConfig. - // TODO(b/145960788) Remove IP configuration retrieval from ONC properties. - std::vector ip_configs = - IPConfigurationsFromONCIPConfigs(onc_dict); - if (ip_configs.empty()) { - ip_configs = IPConfigurationsFromONCProperty( - onc_dict, onc::network_config::kStaticIPConfig); + // Initialize optional array fields to avoid null guards both here and in ARC. + mojo->host_ipv6_global_addresses = std::vector(); + mojo->host_search_domains = std::vector(); + mojo->host_dns_addresses = std::vector(); + mojo->connection_state = + TranslateConnectionState(network_state->connection_state()); + mojo->guid = network_state->guid(); + if (mojo->guid.empty()) + LOG(ERROR) << "Missing GUID property for network " << network_state->path(); + mojo->type = TranslateNetworkType(network_state->type()); + mojo->is_metered = + shill_dict && + shill_dict->FindBoolPath(shill::kMeteredProperty).value_or(false); + + // IP configuration data is added from the properties of the underlying shill + // Device and shill Service attached to the Device. Device properties are + // preferred because Service properties cannot have both IPv4 and IPv6 + // configurations at the same time for dual stack networks. It is necessary to + // fallback on Service properties for networks without a shill Device exposed + // over DBus (builtin OpenVPN, builtin L2TP client, Chrome extension VPNs), + // particularly to obtain the DNS server list (b/155129178). + // A connecting or newly connected network may not immediately have any + // usable IP config object if IPv4 dhcp or IPv6 autoconf have not completed + // yet. This case is covered by requesting shill properties asynchronously + // when chromeos::NetworkStateHandlerObserver::NetworkPropertiesUpdated is + // called. + + // Add shill's Device properties to the given mojo NetworkConfiguration + // objects. This adds the network interface and current IP configurations. + if (const auto* device = + GetStateHandler()->GetDeviceState(network_state->device_path())) { + mojo->network_interface = device->interface(); + for (const auto& kv : device->ip_configs()) + AddIpConfiguration(mojo.get(), kv.second.get()); } - if (ip_configs.empty()) { - ip_configs = IPConfigurationsFromONCProperty( - onc_dict, onc::network_config::kSavedIPConfig); - } - // b/155129178 Also use cached shill properties if available. + if (shill_dict) { for (const auto* property : - {shill::kIPConfigProperty, shill::kStaticIPConfigProperty, - shill::kSavedIPConfigProperty}) { - if (!ip_configs.empty()) - break; - - AddIpConfiguration(ip_configs, shill_dict->FindKey(property)); + {shill::kStaticIPConfigProperty, shill::kSavedIPConfigProperty}) { + AddIpConfiguration(mojo.get(), shill_dict->FindKey(property)); } } - mojo->ip_configs = std::move(ip_configs); - mojo->guid = GetStringFromONCDictionary(onc_dict, onc::network_config::kGUID, - true /* required */); - TranslateONCNetworkTypeDetails(onc_dict, mojo.get()); - - if (network_state) { - mojo->connection_state = - TranslateConnectionState(network_state->connection_state()); - AddDeviceProperties(mojo.get(), network_state->device_path()); + if (mojo->type == arc::mojom::NetworkType::WIFI) { + mojo->wifi = arc::mojom::WiFi::New(); + mojo->wifi->bssid = network_state->bssid(); + mojo->wifi->hex_ssid = network_state->GetHexSsid(); + mojo->wifi->security = + TranslateWiFiSecurity(network_state->security_class()); + mojo->wifi->frequency = network_state->frequency(); + mojo->wifi->hidden_ssid = shill_dict && + shill_dict->FindBoolPath(shill::kWifiHiddenSsid).value_or(false); + mojo->wifi->signal_strength = network_state->signal_strength(); } return mojo; @@ -440,25 +280,22 @@ std::vector TranslateNetworkStates( std::vector networks; for (const chromeos::NetworkState* state : network_states) { const std::string& network_path = state->path(); - if (network_path == arc_vpn_path) { - // Never tell Android about its own VPN. + // Never tell Android about its own VPN. + if (network_path == arc_vpn_path) continue; - } + // For tethered networks, the underlying WiFi networks are not part of // active networks. Replace any such tethered network with its underlying // backing network, because ARC cannot match its datapath with the tethered // network configuration. state = GetShillBackedNetwork(state); - if (!state) { + if (!state) continue; - } const auto it = shill_network_properties.find(network_path); const auto* shill_dict = (it != shill_network_properties.end()) ? &it->second : nullptr; - const auto onc_dict = - chromeos::network_util::TranslateNetworkStateToONC(state); - auto network = TranslateONCConfiguration(state, shill_dict, onc_dict.get()); + auto network = TranslateNetworkProperties(state, shill_dict); network->is_default_network = state == GetStateHandler()->DefaultNetwork(); network->service_name = network_path; networks.push_back(std::move(network)); @@ -475,7 +312,6 @@ void ForgetNetworkFailureCallback( base::OnceCallback callback, const std::string& error_name, std::unique_ptr error_data) { - VLOG(1) << "ForgetNetworkFailureCallback: " << error_name; std::move(callback).Run(arc::mojom::NetworkResult::FAILURE); } @@ -488,7 +324,6 @@ void StartConnectFailureCallback( base::OnceCallback callback, const std::string& error_name, std::unique_ptr error_data) { - VLOG(1) << "StartConnectFailureCallback: " << error_name; std::move(callback).Run(arc::mojom::NetworkResult::FAILURE); } @@ -501,17 +336,15 @@ void StartDisconnectFailureCallback( base::OnceCallback callback, const std::string& error_name, std::unique_ptr error_data) { - VLOG(1) << "StartDisconnectFailureCallback: " << error_name; std::move(callback).Run(arc::mojom::NetworkResult::FAILURE); } -void ArcVpnSuccessCallback() { - DVLOG(1) << "ArcVpnSuccessCallback"; -} +void ArcVpnSuccessCallback() {} -void ArcVpnErrorCallback(const std::string& error_name, +void ArcVpnErrorCallback(const std::string& operation, + const std::string& error_name, std::unique_ptr error_data) { - LOG(ERROR) << "ArcVpnErrorCallback: " << error_name; + LOG(ERROR) << "ArcVpnErrorCallback: " << operation << ": " << error_name; } } // namespace @@ -588,10 +421,9 @@ void ArcNetHostImpl::OnConnectionReady() { GetShillBackedNetwork(GetStateHandler()->DefaultNetwork()); if (default_network && default_network->type() == shill::kTypeVPN && default_network->GetVpnProviderType() == shill::kProviderArcVpn) { - VLOG(0) << "Disconnecting stale ARC VPN " << default_network->path(); GetNetworkConnectionHandler()->DisconnectNetwork( default_network->path(), base::Bind(&ArcVpnSuccessCallback), - base::Bind(&ArcVpnErrorCallback)); + base::Bind(&ArcVpnErrorCallback, "disconnecting stale ARC VPN")); } } @@ -638,8 +470,6 @@ void ArcNetHostImpl::CreateNetworkSuccessCallback( base::OnceCallback callback, const std::string& service_path, const std::string& guid) { - VLOG(1) << "CreateNetworkSuccessCallback"; - cached_guid_ = guid; cached_service_path_ = service_path; @@ -650,13 +480,14 @@ void ArcNetHostImpl::CreateNetworkFailureCallback( base::OnceCallback callback, const std::string& error_name, std::unique_ptr error_data) { - VLOG(1) << "CreateNetworkFailureCallback: " << error_name; + LOG(ERROR) << "CreateNetworkFailureCallback: " << error_name; std::move(callback).Run(std::string()); } void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg, CreateNetworkCallback callback) { if (!IsDeviceOwner()) { + LOG(ERROR) << "Only device owner can create WiFi networks"; std::move(callback).Run(std::string()); return; } @@ -665,12 +496,16 @@ void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg, std::unique_ptr wifi_dict(new base::DictionaryValue); if (!cfg->hexssid.has_value() || !cfg->details) { + LOG(ERROR) + << "Cannot create WiFi network without hex ssid or WiFi properties"; std::move(callback).Run(std::string()); return; } + mojom::ConfiguredNetworkDetailsPtr details = std::move(cfg->details->get_configured()); if (!details) { + LOG(ERROR) << "Cannot create WiFi network without WiFi properties"; std::move(callback).Run(std::string()); return; } @@ -707,7 +542,7 @@ void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg, bool ArcNetHostImpl::GetNetworkPathFromGuid(const std::string& guid, std::string* path) { - const chromeos::NetworkState* network = + const auto* network = GetShillBackedNetwork(GetStateHandler()->GetNetworkStateFromGuid(guid)); if (network) { *path = network->path(); @@ -718,18 +553,21 @@ bool ArcNetHostImpl::GetNetworkPathFromGuid(const std::string& guid, *path = cached_service_path_; return true; } + return false; } void ArcNetHostImpl::ForgetNetwork(const std::string& guid, ForgetNetworkCallback callback) { if (!IsDeviceOwner()) { + LOG(ERROR) << "Only device owner can remove WiFi networks"; std::move(callback).Run(mojom::NetworkResult::FAILURE); return; } std::string path; if (!GetNetworkPathFromGuid(guid, &path)) { + LOG(ERROR) << "Could not retrieve Service path from GUID " << guid; std::move(callback).Run(mojom::NetworkResult::FAILURE); return; } @@ -748,6 +586,7 @@ void ArcNetHostImpl::StartConnect(const std::string& guid, StartConnectCallback callback) { std::string path; if (!GetNetworkPathFromGuid(guid, &path)) { + LOG(ERROR) << "Could not retrieve Service path from GUID " << guid; std::move(callback).Run(mojom::NetworkResult::FAILURE); return; } @@ -766,6 +605,7 @@ void ArcNetHostImpl::StartDisconnect(const std::string& guid, StartDisconnectCallback callback) { std::string path; if (!GetNetworkPathFromGuid(guid, &path)) { + LOG(ERROR) << "Could not retrieve Service path from GUID " << guid; std::move(callback).Run(mojom::NetworkResult::FAILURE); return; } @@ -788,17 +628,17 @@ void ArcNetHostImpl::GetWifiEnabledState(GetWifiEnabledStateCallback callback) { void ArcNetHostImpl::SetWifiEnabledState(bool is_enabled, SetWifiEnabledStateCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - chromeos::NetworkStateHandler::TechnologyState state = - GetStateHandler()->GetTechnologyState( - chromeos::NetworkTypePattern::WiFi()); + auto state = GetStateHandler()->GetTechnologyState( + chromeos::NetworkTypePattern::WiFi()); // WiFi can't be enabled or disabled in these states. if ((state == chromeos::NetworkStateHandler::TECHNOLOGY_PROHIBITED) || (state == chromeos::NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) || (state == chromeos::NetworkStateHandler::TECHNOLOGY_UNAVAILABLE)) { - VLOG(1) << "SetWifiEnabledState failed due to WiFi state: " << state; + LOG(ERROR) << "SetWifiEnabledState failed due to WiFi state: " << state; std::move(callback).Run(false); return; } + GetStateHandler()->SetTechnologyEnabled( chromeos::NetworkTypePattern::WiFi(), is_enabled, chromeos::network_handler::ErrorCallback()); @@ -836,43 +676,39 @@ std::string ArcNetHostImpl::LookupArcVpnServicePath() { false /* visible_only */, kGetNetworksListLimit, &state_list); for (const chromeos::NetworkState* state : state_list) { - const chromeos::NetworkState* shill_backed_network = - GetShillBackedNetwork(state); + const auto* shill_backed_network = GetShillBackedNetwork(state); if (!shill_backed_network) continue; - if (shill_backed_network->GetVpnProviderType() == shill::kProviderArcVpn) { + + if (shill_backed_network->GetVpnProviderType() == shill::kProviderArcVpn) return shill_backed_network->path(); - } } return std::string(); } void ArcNetHostImpl::ConnectArcVpn(const std::string& service_path, const std::string& /* guid */) { - DVLOG(1) << "ConnectArcVpn " << service_path; arc_vpn_service_path_ = service_path; GetNetworkConnectionHandler()->ConnectToNetwork( service_path, base::Bind(&ArcVpnSuccessCallback), - base::Bind(&ArcVpnErrorCallback), false /* check_error_state */, + base::Bind(&ArcVpnErrorCallback, "connecting ARC VPN"), + false /* check_error_state */, chromeos::ConnectCallbackMode::ON_COMPLETED); } std::unique_ptr ArcNetHostImpl::TranslateStringListToValue( const std::vector& string_list) { - std::unique_ptr result = - std::make_unique(base::Value::Type::LIST); - for (const auto& item : string_list) { + auto result = std::make_unique(base::Value::Type::LIST); + for (const auto& item : string_list) result->Append(item); - } return result; } std::unique_ptr ArcNetHostImpl::TranslateVpnConfigurationToOnc( const mojom::AndroidVpnConfiguration& cfg) { - std::unique_ptr top_dict = - std::make_unique(); + auto top_dict = std::make_unique(); // Name, Type top_dict->SetKey( @@ -930,31 +766,26 @@ ArcNetHostImpl::TranslateVpnConfigurationToOnc( void ArcNetHostImpl::AndroidVpnConnected( mojom::AndroidVpnConfigurationPtr cfg) { - std::unique_ptr properties = - TranslateVpnConfigurationToOnc(*cfg); + auto properties = TranslateVpnConfigurationToOnc(*cfg); std::string service_path = LookupArcVpnServicePath(); if (!service_path.empty()) { - VLOG(1) << "AndroidVpnConnected: reusing " << service_path; GetManagedConfigurationHandler()->SetProperties( service_path, *properties, base::Bind(&ArcNetHostImpl::ConnectArcVpn, weak_factory_.GetWeakPtr(), service_path, std::string()), - base::Bind(&ArcVpnErrorCallback)); + base::Bind(&ArcVpnErrorCallback, + "reconnecting ARC VPN " + service_path)); return; } - VLOG(1) << "AndroidVpnConnected: creating new ARC VPN"; std::string user_id_hash = chromeos::LoginState::Get()->primary_user_hash(); GetManagedConfigurationHandler()->CreateConfiguration( user_id_hash, *properties, base::Bind(&ArcNetHostImpl::ConnectArcVpn, weak_factory_.GetWeakPtr()), - base::Bind(&ArcVpnErrorCallback)); + base::Bind(&ArcVpnErrorCallback, "connecting new ARC VPN")); } void ArcNetHostImpl::AndroidVpnStateChanged(mojom::ConnectionStateType state) { - VLOG(1) << "AndroidVpnStateChanged: state=" << state - << " service=" << arc_vpn_service_path_; - if (state != arc::mojom::ConnectionStateType::NOT_CONNECTED || arc_vpn_service_path_.empty()) { return; @@ -968,7 +799,7 @@ void ArcNetHostImpl::AndroidVpnStateChanged(mojom::ConnectionStateType state) { GetNetworkConnectionHandler()->DisconnectNetwork( service_path, base::Bind(&ArcVpnSuccessCallback), - base::Bind(&ArcVpnErrorCallback)); + base::Bind(&ArcVpnErrorCallback, "disconnecting ARC VPN")); } void ArcNetHostImpl::SetAlwaysOnVpn(const std::string& vpn_package, @@ -984,10 +815,9 @@ void ArcNetHostImpl::DisconnectArcVpn() { auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->net(), DisconnectAndroidVpn); - if (!net_instance) { - LOG(ERROR) << "User requested VPN disconnection but API is unavailable"; + if (!net_instance) return; - } + net_instance->DisconnectAndroidVpn(); } @@ -999,27 +829,23 @@ void ArcNetHostImpl::DisconnectRequested(const std::string& service_path) { // This code path is taken when a user clicks the blue Disconnect button // in Chrome OS. Chrome is about to send the Disconnect call to shill, // so update our local state and tell Android to disconnect the VPN. - VLOG(1) << "DisconnectRequested " << service_path; DisconnectArcVpn(); } void ArcNetHostImpl::NetworkConnectionStateChanged( const chromeos::NetworkState* network) { - const chromeos::NetworkState* shill_backed_network = - GetShillBackedNetwork(network); + const auto* shill_backed_network = GetShillBackedNetwork(network); if (!shill_backed_network) return; if (arc_vpn_service_path_ != shill_backed_network->path() || - shill_backed_network->IsConnectingOrConnected()) { + shill_backed_network->IsConnectingOrConnected()) return; - } // This code path is taken when shill disconnects the Android VPN // service. This can happen if a user tries to connect to a Chrome OS // VPN, and shill's VPNProvider::DisconnectAll() forcibly disconnects // all other VPN services to avoid a conflict. - VLOG(1) << "NetworkConnectionStateChanged " << shill_backed_network->path(); DisconnectArcVpn(); } @@ -1064,43 +890,8 @@ void ArcNetHostImpl::UpdateActiveNetworks() { if (!net_instance) return; - const auto active_networks = GetActiveNetworks(); - auto network_configurations = TranslateNetworkStates( - arc_vpn_service_path_, active_networks, shill_network_properties_); - - // A newly connected network may not immediately have any usable IP config - // object if IPv4 dhcp or IPv6 autoconf have not completed yet. Schedule - // with a few seconds delay a forced property update for that service to - // ensure the IP configuration is sent to ARC. Ensure that at most one such - // request is scheduled for a given service. - for (const auto& network : network_configurations) { - if (!network->ip_configs->empty()) - continue; - - if (!network->service_name) - continue; - - const std::string& path = network->service_name.value(); - if (pending_service_property_requests_.insert(path).second) { - LOG(WARNING) << "No IP configuration for " << path; - // TODO(hugobenichi): add exponential backoff for the case when IP - // configuration stays unavailable. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::BindOnce(&ArcNetHostImpl::RequestUpdateForNetwork, - weak_factory_.GetWeakPtr(), path), - kNetworkPropertyUpdateDelay); - } - } - - net_instance->ActiveNetworksChanged(std::move(network_configurations)); -} - -void ArcNetHostImpl::RequestUpdateForNetwork(const std::string& service_path) { - // TODO(hugobenichi): skip the request if the IP configuration for this - // service has been received since then and ARC has been notified about it. - pending_service_property_requests_.erase(service_path); - GetStateHandler()->RequestUpdateForNetwork(service_path); + net_instance->ActiveNetworksChanged(TranslateNetworkStates( + arc_vpn_service_path_, GetActiveNetworks(), shill_network_properties_)); } void ArcNetHostImpl::NetworkListChanged() { @@ -1109,7 +900,14 @@ void ArcNetHostImpl::NetworkListChanged() { return !IsActiveNetworkState( GetStateHandler()->GetNetworkState(entry.first)); }); - for (const auto* network : GetActiveNetworks()) + const auto active_networks = GetActiveNetworks(); + // If there is no active networks, send an explicit ActiveNetworksChanged + // event to ARC and skip updating Shill properties. + if (active_networks.empty()) { + UpdateActiveNetworks(); + return; + } + for (const auto* network : active_networks) NetworkPropertiesUpdated(network); } diff --git a/chromium/components/arc/net/arc_net_host_impl.h b/chromium/components/arc/net/arc_net_host_impl.h index 1dcde71376c..5a072af14b5 100644 --- a/chromium/components/arc/net/arc_net_host_impl.h +++ b/chromium/components/arc/net/arc_net_host_impl.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -150,8 +149,6 @@ class ArcNetHostImpl : public KeyedService, const std::string& error_name, std::unique_ptr error_data); - // Request properties of the Service corresponding to |service_path|. - void RequestUpdateForNetwork(const std::string& service_path); // Callback for chromeos::NetworkHandler::GetShillProperties void ReceiveShillProperties(const std::string& service_path, const base::DictionaryValue& shill_properties); @@ -161,9 +158,6 @@ class ArcNetHostImpl : public KeyedService, // True if the chrome::NetworkStateHandler is currently being observed for // state changes. bool observing_network_state_ = false; - // Contains all service paths for which a property update request is - // currently scheduled. - std::set pending_service_property_requests_; // Cached shill properties for all active networks, keyed by Service path. std::map shill_network_properties_; diff --git a/chromium/components/arc/power/arc_power_bridge.cc b/chromium/components/arc/power/arc_power_bridge.cc index 89d3c85c16a..0ab1ebed483 100644 --- a/chromium/components/arc/power/arc_power_bridge.cc +++ b/chromium/components/arc/power/arc_power_bridge.cc @@ -151,7 +151,7 @@ void ArcPowerBridge::FlushWakeLocksForTesting() { } void ArcPowerBridge::OnConnectionReady() { - // TODO(mash): Support this functionality without ash::Shell access in Chrome. + // ash::Shell may not exist in tests. if (ash::Shell::HasInstance()) ash::Shell::Get()->display_configurator()->AddObserver(this); chromeos::PowerManagerClient::Get()->AddObserver(this); @@ -161,7 +161,7 @@ void ArcPowerBridge::OnConnectionReady() { } void ArcPowerBridge::OnConnectionClosed() { - // TODO(mash): Support this functionality without ash::Shell access in Chrome. + // ash::Shell may not exist in tests. if (ash::Shell::HasInstance()) ash::Shell::Get()->display_configurator()->RemoveObserver(this); chromeos::PowerManagerClient::Get()->RemoveObserver(this); diff --git a/chromium/components/arc/rotation_lock/arc_rotation_lock_bridge.cc b/chromium/components/arc/rotation_lock/arc_rotation_lock_bridge.cc index e3b073b1f82..9a7cc716baf 100644 --- a/chromium/components/arc/rotation_lock/arc_rotation_lock_bridge.cc +++ b/chromium/components/arc/rotation_lock/arc_rotation_lock_bridge.cc @@ -47,7 +47,7 @@ ArcRotationLockBridge::ArcRotationLockBridge(content::BrowserContext* context, ArcBridgeService* bridge_service) : arc_bridge_service_(bridge_service) { arc_bridge_service_->rotation_lock()->AddObserver(this); - // TODO(mash): Support this functionality without ash::Shell access in Chrome. + // ash::Shell may not exist in tests. if (ash::Shell::HasInstance()) { ash::Shell::Get()->screen_orientation_controller()->AddObserver(this); ash::Shell::Get()->tablet_mode_controller()->AddObserver(this); @@ -56,8 +56,7 @@ ArcRotationLockBridge::ArcRotationLockBridge(content::BrowserContext* context, ArcRotationLockBridge::~ArcRotationLockBridge() { arc_bridge_service_->rotation_lock()->RemoveObserver(this); - // TODO(mus): mus needs proper shutdown process. - // TODO(mash): Support this functionality without ash::Shell access in Chrome. + // ash::Shell may not exist in tests. if (ash::Shell::HasInstance()) { ash::Shell::Get()->screen_orientation_controller()->RemoveObserver(this); ash::Shell::Get()->tablet_mode_controller()->RemoveObserver(this); @@ -77,7 +76,7 @@ void ArcRotationLockBridge::OnTabletPhysicalStateChanged() { } void ArcRotationLockBridge::SendRotationLockState() { - // TODO(mash): Support this functionality without ash::Shell access in Chrome. + // ash::Shell may not exist in tests. if (!ash::Shell::HasInstance()) return; diff --git a/chromium/components/arc/session/arc_bridge_host_impl.cc b/chromium/components/arc/session/arc_bridge_host_impl.cc index f6ee475621d..4bd630b65f2 100644 --- a/chromium/components/arc/session/arc_bridge_host_impl.cc +++ b/chromium/components/arc/session/arc_bridge_host_impl.cc @@ -7,7 +7,8 @@ #include #include -#include "ash/public/cpp/arc_notifications_host_initializer.h" +#include "ash/public/cpp/external_arc/message_center/arc_notification_manager.h" +#include "ash/public/cpp/message_center/arc_notifications_host_initializer.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "components/arc/mojom/accessibility_helper.mojom.h" @@ -80,268 +81,296 @@ ArcBridgeHostImpl::~ArcBridgeHostImpl() { } void ArcBridgeHostImpl::OnAccessibilityHelperInstanceReady( - mojom::AccessibilityHelperInstancePtr accessibility_helper_ptr) { + mojo::PendingRemote + accessibility_helper_remote) { OnInstanceReady(arc_bridge_service_->accessibility_helper(), - std::move(accessibility_helper_ptr)); + std::move(accessibility_helper_remote)); } -void ArcBridgeHostImpl::OnAppInstanceReady(mojom::AppInstancePtr app_ptr) { - OnInstanceReady(arc_bridge_service_->app(), std::move(app_ptr)); +void ArcBridgeHostImpl::OnAppInstanceReady( + mojo::PendingRemote app_remote) { + OnInstanceReady(arc_bridge_service_->app(), std::move(app_remote)); } void ArcBridgeHostImpl::OnAppPermissionsInstanceReady( - mojom::AppPermissionsInstancePtr app_permissions_ptr) { + mojo::PendingRemote app_permissions_remote) { OnInstanceReady(arc_bridge_service_->app_permissions(), - std::move(app_permissions_ptr)); + std::move(app_permissions_remote)); } void ArcBridgeHostImpl::OnAppfuseInstanceReady( - mojom::AppfuseInstancePtr appfuse_ptr) { - OnInstanceReady(arc_bridge_service_->appfuse(), std::move(appfuse_ptr)); + mojo::PendingRemote appfuse_remote) { + OnInstanceReady(arc_bridge_service_->appfuse(), std::move(appfuse_remote)); } void ArcBridgeHostImpl::OnAudioInstanceReady( - mojom::AudioInstancePtr audio_ptr) { - OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_ptr)); + mojo::PendingRemote audio_remote) { + OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_remote)); } -void ArcBridgeHostImpl::OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) { - OnInstanceReady(arc_bridge_service_->auth(), std::move(auth_ptr)); +void ArcBridgeHostImpl::OnAuthInstanceReady( + mojo::PendingRemote auth_remote) { + OnInstanceReady(arc_bridge_service_->auth(), std::move(auth_remote)); } void ArcBridgeHostImpl::OnBackupSettingsInstanceReady( - mojom::BackupSettingsInstancePtr backup_settings_ptr) { + mojo::PendingRemote backup_settings_remote) { OnInstanceReady(arc_bridge_service_->backup_settings(), - std::move(backup_settings_ptr)); + std::move(backup_settings_remote)); } void ArcBridgeHostImpl::OnBluetoothInstanceReady( - mojom::BluetoothInstancePtr bluetooth_ptr) { - OnInstanceReady(arc_bridge_service_->bluetooth(), std::move(bluetooth_ptr)); + mojo::PendingRemote bluetooth_remote) { + OnInstanceReady(arc_bridge_service_->bluetooth(), + std::move(bluetooth_remote)); } void ArcBridgeHostImpl::OnBootPhaseMonitorInstanceReady( - mojom::BootPhaseMonitorInstancePtr boot_phase_monitor_ptr) { + mojo::PendingRemote + boot_phase_monitor_remote) { OnInstanceReady(arc_bridge_service_->boot_phase_monitor(), - std::move(boot_phase_monitor_ptr)); + std::move(boot_phase_monitor_remote)); } void ArcBridgeHostImpl::OnCameraInstanceReady( - mojom::CameraInstancePtr camera_ptr) { - OnInstanceReady(arc_bridge_service_->camera(), std::move(camera_ptr)); + mojo::PendingRemote camera_remote) { + OnInstanceReady(arc_bridge_service_->camera(), std::move(camera_remote)); } void ArcBridgeHostImpl::OnCastReceiverInstanceReady( - mojom::CastReceiverInstancePtr cast_receiver_ptr) { + mojo::PendingRemote cast_receiver_remote) { OnInstanceReady(arc_bridge_service_->cast_receiver(), - std::move(cast_receiver_ptr)); + std::move(cast_receiver_remote)); } void ArcBridgeHostImpl::OnCertStoreInstanceReady( - mojom::CertStoreInstancePtr instance_ptr) { - OnInstanceReady(arc_bridge_service_->cert_store(), std::move(instance_ptr)); + mojo::PendingRemote instance_remote) { + OnInstanceReady(arc_bridge_service_->cert_store(), + std::move(instance_remote)); } void ArcBridgeHostImpl::OnClipboardInstanceReady( - mojom::ClipboardInstancePtr clipboard_ptr) { - OnInstanceReady(arc_bridge_service_->clipboard(), std::move(clipboard_ptr)); + mojo::PendingRemote clipboard_remote) { + OnInstanceReady(arc_bridge_service_->clipboard(), + std::move(clipboard_remote)); } void ArcBridgeHostImpl::OnCrashCollectorInstanceReady( - mojom::CrashCollectorInstancePtr crash_collector_ptr) { + mojo::PendingRemote crash_collector_remote) { OnInstanceReady(arc_bridge_service_->crash_collector(), - std::move(crash_collector_ptr)); + std::move(crash_collector_remote)); } void ArcBridgeHostImpl::OnDiskQuotaInstanceReady( - mojom::DiskQuotaInstancePtr disk_quota_ptr) { - OnInstanceReady(arc_bridge_service_->disk_quota(), std::move(disk_quota_ptr)); + mojo::PendingRemote disk_quota_remote) { + OnInstanceReady(arc_bridge_service_->disk_quota(), + std::move(disk_quota_remote)); } void ArcBridgeHostImpl::OnEnterpriseReportingInstanceReady( - mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) { + mojo::PendingRemote + enterprise_reporting_remote) { OnInstanceReady(arc_bridge_service_->enterprise_reporting(), - std::move(enterprise_reporting_ptr)); + std::move(enterprise_reporting_remote)); } void ArcBridgeHostImpl::OnFileSystemInstanceReady( - mojom::FileSystemInstancePtr file_system_ptr) { + mojo::PendingRemote file_system_remote) { OnInstanceReady(arc_bridge_service_->file_system(), - std::move(file_system_ptr)); + std::move(file_system_remote)); } -void ArcBridgeHostImpl::OnImeInstanceReady(mojom::ImeInstancePtr ime_ptr) { - OnInstanceReady(arc_bridge_service_->ime(), std::move(ime_ptr)); +void ArcBridgeHostImpl::OnImeInstanceReady( + mojo::PendingRemote ime_remote) { + OnInstanceReady(arc_bridge_service_->ime(), std::move(ime_remote)); } void ArcBridgeHostImpl::OnInputMethodManagerInstanceReady( - mojom::InputMethodManagerInstancePtr input_method_manager_ptr) { + mojo::PendingRemote + input_method_manager_remote) { OnInstanceReady(arc_bridge_service_->input_method_manager(), - std::move(input_method_manager_ptr)); + std::move(input_method_manager_remote)); } void ArcBridgeHostImpl::OnIntentHelperInstanceReady( - mojom::IntentHelperInstancePtr intent_helper_ptr) { + mojo::PendingRemote intent_helper_remote) { OnInstanceReady(arc_bridge_service_->intent_helper(), - std::move(intent_helper_ptr)); + std::move(intent_helper_remote)); } void ArcBridgeHostImpl::OnKeymasterInstanceReady( - mojom::KeymasterInstancePtr keymaster_ptr) { - OnInstanceReady(arc_bridge_service_->keymaster(), std::move(keymaster_ptr)); + mojo::PendingRemote keymaster_remote) { + OnInstanceReady(arc_bridge_service_->keymaster(), + std::move(keymaster_remote)); } void ArcBridgeHostImpl::OnKioskInstanceReady( - mojom::KioskInstancePtr kiosk_ptr) { - OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr)); + mojo::PendingRemote kiosk_remote) { + OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_remote)); } void ArcBridgeHostImpl::OnLockScreenInstanceReady( - mojom::LockScreenInstancePtr lock_screen_ptr) { + mojo::PendingRemote lock_screen_remote) { OnInstanceReady(arc_bridge_service_->lock_screen(), - std::move(lock_screen_ptr)); + std::move(lock_screen_remote)); } void ArcBridgeHostImpl::OnMediaSessionInstanceReady( - mojom::MediaSessionInstancePtr media_session_ptr) { + mojo::PendingRemote media_session_remote) { OnInstanceReady(arc_bridge_service_->media_session(), - std::move(media_session_ptr)); + std::move(media_session_remote)); } void ArcBridgeHostImpl::OnMetricsInstanceReady( - mojom::MetricsInstancePtr metrics_ptr) { - OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_ptr)); + mojo::PendingRemote metrics_remote) { + OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_remote)); } void ArcBridgeHostImpl::OnMidisInstanceReady( - mojom::MidisInstancePtr midis_ptr) { - OnInstanceReady(arc_bridge_service_->midis(), std::move(midis_ptr)); + mojo::PendingRemote midis_remote) { + OnInstanceReady(arc_bridge_service_->midis(), std::move(midis_remote)); } -void ArcBridgeHostImpl::OnNetInstanceReady(mojom::NetInstancePtr net_ptr) { - OnInstanceReady(arc_bridge_service_->net(), std::move(net_ptr)); +void ArcBridgeHostImpl::OnNetInstanceReady( + mojo::PendingRemote net_remote) { + OnInstanceReady(arc_bridge_service_->net(), std::move(net_remote)); } void ArcBridgeHostImpl::OnNotificationsInstanceReady( - mojom::NotificationsInstancePtr notifications_ptr) { - // Forward notification instance to ash. - ash::ArcNotificationsHostInitializer::Get()->SetArcNotificationsInstance( - notifications_ptr.PassInterface()); + mojo::PendingRemote notifications_remote) { + auto* host_initializer = ash::ArcNotificationsHostInitializer::Get(); + auto* manager = host_initializer->GetArcNotificationManagerInstance(); + if (manager) { + static_cast(manager)->SetInstance( + std::move(notifications_remote)); + return; + } + // Forward notification instance to ash by injecting ArcNotificationManager. + auto new_manager = std::make_unique(); + new_manager->SetInstance(std::move(notifications_remote)); + ash::ArcNotificationsHostInitializer::Get() + ->SetArcNotificationManagerInstance(std::move(new_manager)); } void ArcBridgeHostImpl::OnObbMounterInstanceReady( - mojom::ObbMounterInstancePtr obb_mounter_ptr) { + mojo::PendingRemote obb_mounter_remote) { OnInstanceReady(arc_bridge_service_->obb_mounter(), - std::move(obb_mounter_ptr)); + std::move(obb_mounter_remote)); } void ArcBridgeHostImpl::OnOemCryptoInstanceReady( - mojom::OemCryptoInstancePtr oemcrypto_ptr) { - OnInstanceReady(arc_bridge_service_->oemcrypto(), std::move(oemcrypto_ptr)); + mojo::PendingRemote oemcrypto_remote) { + OnInstanceReady(arc_bridge_service_->oemcrypto(), + std::move(oemcrypto_remote)); } -void ArcBridgeHostImpl::OnPipInstanceReady(mojom::PipInstancePtr pip_ptr) { - OnInstanceReady(arc_bridge_service_->pip(), std::move(pip_ptr)); +void ArcBridgeHostImpl::OnPipInstanceReady( + mojo::PendingRemote pip_remote) { + OnInstanceReady(arc_bridge_service_->pip(), std::move(pip_remote)); } void ArcBridgeHostImpl::OnPolicyInstanceReady( - mojom::PolicyInstancePtr policy_ptr) { - OnInstanceReady(arc_bridge_service_->policy(), std::move(policy_ptr)); + mojo::PendingRemote policy_remote) { + OnInstanceReady(arc_bridge_service_->policy(), std::move(policy_remote)); } void ArcBridgeHostImpl::OnPowerInstanceReady( - mojom::PowerInstancePtr power_ptr) { - OnInstanceReady(arc_bridge_service_->power(), std::move(power_ptr)); + mojo::PendingRemote power_remote) { + OnInstanceReady(arc_bridge_service_->power(), std::move(power_remote)); } void ArcBridgeHostImpl::OnPrintSpoolerInstanceReady( - mojom::PrintSpoolerInstancePtr print_spooler_ptr) { + mojo::PendingRemote print_spooler_remote) { OnInstanceReady(arc_bridge_service_->print_spooler(), - std::move(print_spooler_ptr)); + std::move(print_spooler_remote)); } void ArcBridgeHostImpl::OnProcessInstanceReady( - mojom::ProcessInstancePtr process_ptr) { - OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr)); + mojo::PendingRemote process_remote) { + OnInstanceReady(arc_bridge_service_->process(), std::move(process_remote)); } void ArcBridgeHostImpl::OnPropertyInstanceReady( - mojom::PropertyInstancePtr property_ptr) { - OnInstanceReady(arc_bridge_service_->property(), std::move(property_ptr)); + mojo::PendingRemote property_remote) { + OnInstanceReady(arc_bridge_service_->property(), std::move(property_remote)); } void ArcBridgeHostImpl::OnRotationLockInstanceReady( - mojom::RotationLockInstancePtr rotation_lock_ptr) { + mojo::PendingRemote rotation_lock_remote) { OnInstanceReady(arc_bridge_service_->rotation_lock(), - std::move(rotation_lock_ptr)); + std::move(rotation_lock_remote)); } void ArcBridgeHostImpl::OnScreenCaptureInstanceReady( - mojom::ScreenCaptureInstancePtr screen_capture_ptr) { + mojo::PendingRemote screen_capture_remote) { OnInstanceReady(arc_bridge_service_->screen_capture(), - std::move(screen_capture_ptr)); + std::move(screen_capture_remote)); } void ArcBridgeHostImpl::OnSmartCardManagerInstanceReady( - mojom::SmartCardManagerInstancePtr smart_card_manager_ptr) { + mojo::PendingRemote + smart_card_manager_remote) { OnInstanceReady(arc_bridge_service_->smart_card_manager(), - std::move(smart_card_manager_ptr)); + std::move(smart_card_manager_remote)); } void ArcBridgeHostImpl::OnStorageManagerInstanceReady( - mojom::StorageManagerInstancePtr storage_manager_ptr) { + mojo::PendingRemote storage_manager_remote) { OnInstanceReady(arc_bridge_service_->storage_manager(), - std::move(storage_manager_ptr)); + std::move(storage_manager_remote)); } void ArcBridgeHostImpl::OnTimerInstanceReady( - mojom::TimerInstancePtr timer_ptr) { - OnInstanceReady(arc_bridge_service_->timer(), std::move(timer_ptr)); + mojo::PendingRemote timer_remote) { + OnInstanceReady(arc_bridge_service_->timer(), std::move(timer_remote)); } void ArcBridgeHostImpl::OnTracingInstanceReady( - mojom::TracingInstancePtr tracing_ptr) { - OnInstanceReady(arc_bridge_service_->tracing(), std::move(tracing_ptr)); + mojo::PendingRemote tracing_remote) { + OnInstanceReady(arc_bridge_service_->tracing(), std::move(tracing_remote)); } -void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) { - OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr)); +void ArcBridgeHostImpl::OnTtsInstanceReady( + mojo::PendingRemote tts_remote) { + OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_remote)); } void ArcBridgeHostImpl::OnUsbHostInstanceReady( - mojom::UsbHostInstancePtr usb_host_ptr) { - OnInstanceReady(arc_bridge_service_->usb_host(), std::move(usb_host_ptr)); + mojo::PendingRemote usb_host_remote) { + OnInstanceReady(arc_bridge_service_->usb_host(), std::move(usb_host_remote)); } void ArcBridgeHostImpl::OnVideoInstanceReady( - mojom::VideoInstancePtr video_ptr) { - OnInstanceReady(arc_bridge_service_->video(), std::move(video_ptr)); + mojo::PendingRemote video_remote) { + OnInstanceReady(arc_bridge_service_->video(), std::move(video_remote)); } void ArcBridgeHostImpl::OnVoiceInteractionArcHomeInstanceReady( - mojom::VoiceInteractionArcHomeInstancePtr home_ptr) { + mojo::PendingRemote home_remote) { NOTREACHED(); } void ArcBridgeHostImpl::OnVoiceInteractionFrameworkInstanceReady( - mojom::VoiceInteractionFrameworkInstancePtr framework_ptr) { + mojo::PendingRemote + framework_remote) { NOTREACHED(); } void ArcBridgeHostImpl::OnVolumeMounterInstanceReady( - mojom::VolumeMounterInstancePtr volume_mounter_ptr) { + mojo::PendingRemote volume_mounter_remote) { OnInstanceReady(arc_bridge_service_->volume_mounter(), - std::move(volume_mounter_ptr)); + std::move(volume_mounter_remote)); } void ArcBridgeHostImpl::OnWakeLockInstanceReady( - mojom::WakeLockInstancePtr wakelock_ptr) { - OnInstanceReady(arc_bridge_service_->wake_lock(), std::move(wakelock_ptr)); + mojo::PendingRemote wakelock_remote) { + OnInstanceReady(arc_bridge_service_->wake_lock(), std::move(wakelock_remote)); } void ArcBridgeHostImpl::OnWallpaperInstanceReady( - mojom::WallpaperInstancePtr wallpaper_ptr) { - OnInstanceReady(arc_bridge_service_->wallpaper(), std::move(wallpaper_ptr)); + mojo::PendingRemote wallpaper_remote) { + OnInstanceReady(arc_bridge_service_->wallpaper(), + std::move(wallpaper_remote)); } void ArcBridgeHostImpl::OnClosed() { @@ -360,16 +389,16 @@ void ArcBridgeHostImpl::OnClosed() { template void ArcBridgeHostImpl::OnInstanceReady( ConnectionHolder* holder, - mojo::InterfacePtr ptr) { + mojo::PendingRemote remote) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(receiver_.is_bound()); - DCHECK(ptr.is_bound()); + DCHECK(remote.is_valid()); // Track |channel|'s lifetime via |mojo_channels_| so that it will be // closed on ArcBridgeHost/Instance closing or the ArcBridgeHostImpl's // destruction. auto* channel = - new MojoChannel(holder, ptr.PassInterface()); + new MojoChannel(holder, std::move(remote)); mojo_channels_.emplace_back(channel); // Since |channel| is managed by |mojo_channels_|, its lifetime is shorter diff --git a/chromium/components/arc/session/arc_bridge_host_impl.h b/chromium/components/arc/session/arc_bridge_host_impl.h index 4e4020846b5..8f9b1fa796d 100644 --- a/chromium/components/arc/session/arc_bridge_host_impl.h +++ b/chromium/components/arc/session/arc_bridge_host_impl.h @@ -40,86 +40,124 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost { // ArcBridgeHost overrides. void OnAccessibilityHelperInstanceReady( - mojom::AccessibilityHelperInstancePtr accessibility_helper_ptr) override; - void OnAppInstanceReady(mojom::AppInstancePtr app_ptr) override; + mojo::PendingRemote + accessibility_helper_remote) override; + void OnAppInstanceReady( + mojo::PendingRemote app_ptr) override; void OnAppPermissionsInstanceReady( - mojom::AppPermissionsInstancePtr app_permissions_ptr) override; - void OnAppfuseInstanceReady(mojom::AppfuseInstancePtr appfuse_ptr) override; - void OnAudioInstanceReady(mojom::AudioInstancePtr audio_ptr) override; - void OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) override; + mojo::PendingRemote app_permissions_remote) + override; + void OnAppfuseInstanceReady( + mojo::PendingRemote appfuse_remote) override; + void OnAudioInstanceReady( + mojo::PendingRemote audio_remote) override; + void OnAuthInstanceReady( + mojo::PendingRemote auth_remote) override; void OnBackupSettingsInstanceReady( - mojom::BackupSettingsInstancePtr backup_settings_ptr) override; + mojo::PendingRemote backup_settings_remote) + override; void OnBluetoothInstanceReady( - mojom::BluetoothInstancePtr bluetooth_ptr) override; + mojo::PendingRemote bluetooth_remote) override; void OnBootPhaseMonitorInstanceReady( - mojom::BootPhaseMonitorInstancePtr boot_phase_monitor_ptr) override; - void OnCameraInstanceReady(mojom::CameraInstancePtr camera_ptr) override; + mojo::PendingRemote + boot_phase_monitor_remote) override; + void OnCameraInstanceReady( + mojo::PendingRemote camera_remote) override; void OnCastReceiverInstanceReady( - mojom::CastReceiverInstancePtr cast_receiver_ptr) override; + mojo::PendingRemote cast_receiver_remote) + override; void OnCertStoreInstanceReady( - mojom::CertStoreInstancePtr instance_ptr) override; + mojo::PendingRemote instance_remote) override; void OnClipboardInstanceReady( - mojom::ClipboardInstancePtr clipboard_ptr) override; + mojo::PendingRemote clipboard_remote) override; void OnCrashCollectorInstanceReady( - mojom::CrashCollectorInstancePtr crash_collector_ptr) override; + mojo::PendingRemote crash_collector_remote) + override; void OnDiskQuotaInstanceReady( - mojom::DiskQuotaInstancePtr disk_quota_ptr) override; + mojo::PendingRemote disk_quota_remote) override; void OnEnterpriseReportingInstanceReady( - mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) override; - void OnFileSystemInstanceReady( - mojom::FileSystemInstancePtr file_system_ptr) override; - void OnImeInstanceReady(mojom::ImeInstancePtr ime_ptr) override; + mojo::PendingRemote + enterprise_reporting_remote) override; + void OnFileSystemInstanceReady(mojo::PendingRemote + file_system_remote) override; + void OnImeInstanceReady( + mojo::PendingRemote ime_remote) override; void OnInputMethodManagerInstanceReady( - mojom::InputMethodManagerInstancePtr input_method_manager_ptr) override; + mojo::PendingRemote + input_method_manager_remote) override; void OnIntentHelperInstanceReady( - mojom::IntentHelperInstancePtr intent_helper_ptr) override; + mojo::PendingRemote intent_helper_remote) + override; void OnKeymasterInstanceReady( - mojom::KeymasterInstancePtr keymaster_ptr) override; - void OnKioskInstanceReady(mojom::KioskInstancePtr kiosk_ptr) override; - void OnLockScreenInstanceReady( - mojom::LockScreenInstancePtr lock_screen_ptr) override; + mojo::PendingRemote keymaster_remote) override; + void OnKioskInstanceReady( + mojo::PendingRemote kiosk_remote) override; + void OnLockScreenInstanceReady(mojo::PendingRemote + lock_screen_remote) override; void OnMediaSessionInstanceReady( - mojom::MediaSessionInstancePtr media_session_ptr) override; - void OnMetricsInstanceReady(mojom::MetricsInstancePtr metrics_ptr) override; - void OnMidisInstanceReady(mojom::MidisInstancePtr midis_ptr) override; - void OnNetInstanceReady(mojom::NetInstancePtr net_ptr) override; + mojo::PendingRemote media_session_remote) + override; + void OnMetricsInstanceReady( + mojo::PendingRemote metrics_remote) override; + void OnMidisInstanceReady( + mojo::PendingRemote midis_remote) override; + void OnNetInstanceReady( + mojo::PendingRemote net_remote) override; void OnNotificationsInstanceReady( - mojom::NotificationsInstancePtr notifications_ptr) override; - void OnObbMounterInstanceReady( - mojom::ObbMounterInstancePtr obb_mounter_ptr) override; + mojo::PendingRemote notifications_remote) + override; + void OnObbMounterInstanceReady(mojo::PendingRemote + obb_mounter_remote) override; void OnOemCryptoInstanceReady( - mojom::OemCryptoInstancePtr oemcrypto_ptr) override; - void OnPipInstanceReady(mojom::PipInstancePtr policy_ptr) override; - void OnPolicyInstanceReady(mojom::PolicyInstancePtr policy_ptr) override; - void OnPowerInstanceReady(mojom::PowerInstancePtr power_ptr) override; + mojo::PendingRemote oemcrypto_remote) override; + void OnPipInstanceReady( + mojo::PendingRemote policy_remote) override; + void OnPolicyInstanceReady( + mojo::PendingRemote policy_remote) override; + void OnPowerInstanceReady( + mojo::PendingRemote power_remote) override; void OnPrintSpoolerInstanceReady( - mojom::PrintSpoolerInstancePtr print_spooler_ptr) override; - void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override; + mojo::PendingRemote print_spooler_remote) + override; + void OnProcessInstanceReady( + mojo::PendingRemote process_remote) override; void OnPropertyInstanceReady( - mojom::PropertyInstancePtr property_ptr) override; + mojo::PendingRemote property_remote) override; void OnRotationLockInstanceReady( - mojom::RotationLockInstancePtr rotation_lock_ptr) override; + mojo::PendingRemote rotation_lock_remote) + override; void OnScreenCaptureInstanceReady( - mojom::ScreenCaptureInstancePtr screen_capture_ptr) override; + mojo::PendingRemote screen_capture_remote) + override; void OnSmartCardManagerInstanceReady( - mojom::SmartCardManagerInstancePtr smart_card_manager_ptr) override; + mojo::PendingRemote + smart_card_manager_remote) override; void OnStorageManagerInstanceReady( - mojom::StorageManagerInstancePtr storage_manager_ptr) override; - void OnTimerInstanceReady(mojom::TimerInstancePtr timer_ptr) override; - void OnTracingInstanceReady(mojom::TracingInstancePtr trace_ptr) override; - void OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) override; - void OnUsbHostInstanceReady(mojom::UsbHostInstancePtr usb_host_ptr) override; - void OnVideoInstanceReady(mojom::VideoInstancePtr video_ptr) override; + mojo::PendingRemote storage_manager_remote) + override; + void OnTimerInstanceReady( + mojo::PendingRemote timer_remote) override; + void OnTracingInstanceReady( + mojo::PendingRemote trace_remote) override; + void OnTtsInstanceReady( + mojo::PendingRemote tts_remote) override; + void OnUsbHostInstanceReady( + mojo::PendingRemote usb_host_remote) override; + void OnVideoInstanceReady( + mojo::PendingRemote video_remote) override; void OnVoiceInteractionArcHomeInstanceReady( - mojom::VoiceInteractionArcHomeInstancePtr home_ptr) override; + mojo::PendingRemote home_remote) + override; void OnVoiceInteractionFrameworkInstanceReady( - mojom::VoiceInteractionFrameworkInstancePtr framework_ptr) override; + mojo::PendingRemote + framework_remote) override; void OnVolumeMounterInstanceReady( - mojom::VolumeMounterInstancePtr volume_mounter_ptr) override; + mojo::PendingRemote volume_mounter_remote) + override; void OnWakeLockInstanceReady( - mojom::WakeLockInstancePtr wake_lock_ptr) override; + mojo::PendingRemote wake_lock_remote) override; void OnWallpaperInstanceReady( - mojom::WallpaperInstancePtr wallpaper_ptr) override; + mojo::PendingRemote wallpaper_remote) override; private: // Called when the bridge channel is closed. This typically only happens when @@ -130,7 +168,7 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost { // |T| is a ARC Mojo Instance type. template void OnInstanceReady(ConnectionHolder* holder, - mojo::InterfacePtr ptr); + mojo::PendingRemote remote); // Called if one of the established channels is closed. void OnChannelClosed(MojoChannelBase* channel); diff --git a/chromium/components/arc/session/arc_property_util.cc b/chromium/components/arc/session/arc_property_util.cc index 9b6498c51fe..0a4ca1a4c66 100644 --- a/chromium/components/arc/session/arc_property_util.cc +++ b/chromium/components/arc/session/arc_property_util.cc @@ -216,7 +216,8 @@ bool ExpandPropertyContents(const std::string& content, bool ExpandPropertyFile(const base::FilePath& input, const base::FilePath& output, - CrosConfig* config) { + CrosConfig* config, + bool append) { std::string content; std::string expanded; if (!base::ReadFileToString(input, &content)) { @@ -225,10 +226,17 @@ bool ExpandPropertyFile(const base::FilePath& input, } if (!ExpandPropertyContents(content, config, &expanded)) return false; - if (base::WriteFile(output, expanded.data(), expanded.size()) != - static_cast(expanded.size())) { - PLOG(ERROR) << "Failed to write to " << output; - return false; + if (append && base::PathExists(output)) { + if (!base::AppendToFile(output, expanded.data(), expanded.size())) { + PLOG(ERROR) << "Failed to append to " << output; + return false; + } + } else { + if (base::WriteFile(output, expanded.data(), expanded.size()) != + static_cast(expanded.size())) { + PLOG(ERROR) << "Failed to write to " << output; + return false; + } } return true; } @@ -287,22 +295,35 @@ bool TruncateAndroidPropertyForTesting(const std::string& line, bool ExpandPropertyFileForTesting(const base::FilePath& input, const base::FilePath& output, CrosConfig* config) { - return ExpandPropertyFile(input, output, config); + return ExpandPropertyFile(input, output, config, /*append=*/false); } bool ExpandPropertyFiles(const base::FilePath& source_path, - const base::FilePath& dest_path) { + const base::FilePath& dest_path, + bool single_file) { CrosConfig config; - for (const char* file : {"default.prop", "build.prop", "vendor_build.prop"}) { - if (!ExpandPropertyFile(source_path.Append(file), dest_path.Append(file), - &config)) { + if (single_file) + base::DeleteFile(dest_path, /*recursive=*/false); + + // default.prop may not exist. Silently skip it if not found. + for (const auto& pair : {std::pair{"default.prop", true}, + {"build.prop", false}, + {"vendor_build.prop", false}}) { + const char* file = pair.first; + const bool is_optional = pair.second; + + if (is_optional && !base::PathExists(source_path.Append(file))) + continue; + + if (!ExpandPropertyFile(source_path.Append(file), + single_file ? dest_path : dest_path.Append(file), + &config, + /*append=*/single_file)) { LOG(ERROR) << "Failed to expand " << source_path.Append(file); return false; } } - // Use the same permissions as stock Android for /vendor/build.prop. - return base::SetPosixFilePermissions(dest_path.Append("vendor_build.prop"), - 0600); + return true; } } // namespace arc diff --git a/chromium/components/arc/session/arc_property_util.h b/chromium/components/arc/session/arc_property_util.h index ce780cfc787..4748e563f94 100644 --- a/chromium/components/arc/session/arc_property_util.h +++ b/chromium/components/arc/session/arc_property_util.h @@ -57,10 +57,13 @@ bool ExpandPropertyFileForTesting(const base::FilePath& input, const base::FilePath& output, CrosConfig* config); -// Calls ExpandPropertyFile for {build,default}.prop files in |source_path|. -// Expanded files are written in |dest_path|. Returns true on success. +// Calls ExpandPropertyFile for {build,default,vendor_build}.prop files in +// |source_path|. Expanded files are written in |dest_path|. Returns true on +// success. When |single_file| is true, only one file (|dest_path| itself) is +// written. All expanded properties are included in the single file. bool ExpandPropertyFiles(const base::FilePath& source_path, - const base::FilePath& dest_path); + const base::FilePath& dest_path, + bool single_file); } // namespace arc diff --git a/chromium/components/arc/session/arc_property_util_unittest.cc b/chromium/components/arc/session/arc_property_util_unittest.cc index 29e8a5e0e13..1583bc1a5da 100644 --- a/chromium/components/arc/session/arc_property_util_unittest.cc +++ b/chromium/components/arc/session/arc_property_util_unittest.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/strings/stringprintf.h" #include "chromeos/constants/chromeos_switches.h" #include "components/arc/test/fake_cros_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -286,36 +287,37 @@ TEST_F(ArcPropertyUtilTest, ExpandPropertyFile_CannotWrite) { path, base::FilePath("/nonexistent2"), config())); } -TEST_F(ArcPropertyUtilTest, ExpandPropertyFiles_NoSource) { +TEST_F(ArcPropertyUtilTest, ExpandPropertyFiles) { // Both source and dest are not found. EXPECT_FALSE(ExpandPropertyFiles(base::FilePath("/nonexistent1"), - base::FilePath("/nonexistent2"))); + base::FilePath("/nonexistent2"), + /*single_file=*/false)); // Both source and dest exist, but the source directory is empty. base::FilePath source_dir; ASSERT_TRUE(base::CreateTemporaryDirInDir(GetTempDir(), "test", &source_dir)); base::FilePath dest_dir; ASSERT_TRUE(base::CreateTemporaryDirInDir(GetTempDir(), "test", &dest_dir)); - EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir)); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, false)); // Add default.prop to the source, but not build.prop. base::FilePath default_prop = source_dir.Append("default.prop"); constexpr const char kDefaultProp[] = "ro.foo=bar\n"; base::WriteFile(default_prop, kDefaultProp, strlen(kDefaultProp)); - EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir)); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, false)); // Add build.prop too. The call should not succeed still. base::FilePath build_prop = source_dir.Append("build.prop"); constexpr const char kBuildProp[] = "ro.baz=boo\n"; base::WriteFile(build_prop, kBuildProp, strlen(kBuildProp)); - EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir)); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, false)); // Add vendor_build.prop too. Then the call should succeed. base::FilePath vendor_build_prop = source_dir.Append("vendor_build.prop"); constexpr const char kVendorBuildProp[] = "ro.a=b\n"; base::WriteFile(vendor_build_prop, kVendorBuildProp, strlen(kVendorBuildProp)); - EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir)); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir, false)); // Verify all dest files are there. EXPECT_TRUE(base::PathExists(dest_dir.Append("default.prop"))); @@ -334,8 +336,100 @@ TEST_F(ArcPropertyUtilTest, ExpandPropertyFiles_NoSource) { base::ReadFileToString(dest_dir.Append("vendor_build.prop"), &content)); EXPECT_EQ(std::string(kVendorBuildProp) + "\n", content); + // Expand it again, verify the previous result is cleared. + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir, false)); + EXPECT_TRUE( + base::ReadFileToString(dest_dir.Append("default.prop"), &content)); + EXPECT_EQ(std::string(kDefaultProp) + "\n", content); + + // If default.prop does not exist in the source path, it should still process + // the other files, while also ensuring that default.prop is removed from the + // destination path. + base::DeleteFile(dest_dir.Append("default.prop"), /*recursive=*/false); + + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir, false)); + + EXPECT_TRUE(base::ReadFileToString(dest_dir.Append("build.prop"), &content)); + EXPECT_EQ(std::string(kBuildProp) + "\n", content); + EXPECT_TRUE( + base::ReadFileToString(dest_dir.Append("vendor_build.prop"), &content)); + EXPECT_EQ(std::string(kVendorBuildProp) + "\n", content); + + // Finally, test the case where source is valid but the dest is not. + EXPECT_FALSE( + ExpandPropertyFiles(source_dir, base::FilePath("/nonexistent"), false)); +} + +// Do the same as the previous test, but with |single_file| == true. +TEST_F(ArcPropertyUtilTest, ExpandPropertyFiles_SingleFile) { + // Both source and dest are not found. + EXPECT_FALSE(ExpandPropertyFiles(base::FilePath("/nonexistent1"), + base::FilePath("/nonexistent2"), + /*single_file=*/true)); + + // Both source and dest exist, but the source directory is empty. + base::FilePath source_dir; + ASSERT_TRUE(base::CreateTemporaryDirInDir(GetTempDir(), "test", &source_dir)); + base::FilePath dest_prop_file; + ASSERT_TRUE( + base::CreateTemporaryDirInDir(GetTempDir(), "test", &dest_prop_file)); + dest_prop_file = dest_prop_file.Append("combined.prop"); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + + // Add default.prop to the source, but not build.prop. + base::FilePath default_prop = source_dir.Append("default.prop"); + constexpr const char kDefaultProp[] = "ro.foo=bar\n"; + base::WriteFile(default_prop, kDefaultProp, strlen(kDefaultProp)); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + + // Add build.prop too. The call should not succeed still. + base::FilePath build_prop = source_dir.Append("build.prop"); + constexpr const char kBuildProp[] = "ro.baz=boo\n"; + base::WriteFile(build_prop, kBuildProp, strlen(kBuildProp)); + EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + + // Add vendor_build.prop too. Then the call should succeed. + base::FilePath vendor_build_prop = source_dir.Append("vendor_build.prop"); + constexpr const char kVendorBuildProp[] = "ro.a=b\n"; + base::WriteFile(vendor_build_prop, kVendorBuildProp, + strlen(kVendorBuildProp)); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + + // Verify only one dest file exists. + EXPECT_FALSE( + base::PathExists(dest_prop_file.DirName().Append("default.prop"))); + EXPECT_FALSE(base::PathExists(dest_prop_file.DirName().Append("build.prop"))); + EXPECT_FALSE( + base::PathExists(dest_prop_file.DirName().Append("vendor_build.prop"))); + EXPECT_TRUE(base::PathExists(dest_prop_file)); + + // Verify the content. + // Note: ExpandPropertyFileForTesting() adds a trailing LF. + std::string content; + EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); + EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp, + kVendorBuildProp), + content); + + // Expand it again, verify the previous result is cleared. + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); + EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp, + kVendorBuildProp), + content); + + // If default.prop does not exist in the source path, it should still process + // the other files. + base::DeleteFile(source_dir.Append("default.prop"), + /*recursive=*/false); + EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true)); + EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content)); + EXPECT_EQ(base::StringPrintf("%s\n%s\n", kBuildProp, kVendorBuildProp), + content); + // Finally, test the case where source is valid but the dest is not. - EXPECT_FALSE(ExpandPropertyFiles(source_dir, base::FilePath("/nonexistent"))); + EXPECT_FALSE( + ExpandPropertyFiles(source_dir, base::FilePath("/nonexistent"), true)); } } // namespace diff --git a/chromium/components/arc/session/arc_session_impl.cc b/chromium/components/arc/session/arc_session_impl.cc index 3657d1c29a6..cecccda4961 100644 --- a/chromium/components/arc/session/arc_session_impl.cc +++ b/chromium/components/arc/session/arc_session_impl.cc @@ -375,7 +375,7 @@ void ArcSessionImpl::DoStartMiniInstance(size_t num_cores_disabled) { base::FeatureList::IsEnabled(arc::kNativeBridgeToggleFeature); params.arc_file_picker_experiment = base::FeatureList::IsEnabled(arc::kFilePickerExperimentFeature); - // Enable Custom Tabs only on Dev and Cannary, and only when Mash is enabled. + // Enable Custom Tabs only on Dev and Canary. const bool is_custom_tab_enabled = base::FeatureList::IsEnabled(arc::kCustomTabsExperimentFeature) && delegate_->GetChannel() != version_info::Channel::STABLE && diff --git a/chromium/components/arc/session/arc_vm_client_adapter.cc b/chromium/components/arc/session/arc_vm_client_adapter.cc index aed435c112f..c9ffbb891b0 100644 --- a/chromium/components/arc/session/arc_vm_client_adapter.cc +++ b/chromium/components/arc/session/arc_vm_client_adapter.cc @@ -114,11 +114,12 @@ std::string MonotonicTimestamp() { ArcBinaryTranslationType IdentifyBinaryTranslationType( const StartParams& start_params) { const auto* command_line = base::CommandLine::ForCurrentProcess(); - bool is_houdini_available = + const bool is_houdini_available = command_line->HasSwitch(chromeos::switches::kEnableHoudini) || command_line->HasSwitch(chromeos::switches::kEnableHoudini64); - bool is_ndk_translation_available = - command_line->HasSwitch(chromeos::switches::kEnableNdkTranslation); + const bool is_ndk_translation_available = + command_line->HasSwitch(chromeos::switches::kEnableNdkTranslation) || + command_line->HasSwitch(chromeos::switches::kEnableNdkTranslation64); if (!is_houdini_available && !is_ndk_translation_available) return ArcBinaryTranslationType::NONE; @@ -219,14 +220,6 @@ vm_tools::concierge::StartArcVmRequest CreateStartArcVmRequest( } request.add_params("init=/init"); - // TIP: When you want to see all dmesg logs from the Android system processes - // such as init, uncomment the following line. By default, the guest kernel - // rate-limits the logging and you might not be able to see all LOGs from - // them. The logs could be silently dropped. This is useful when modifying - // init.bertha.rc, for example. - // - // request.add_params("printk.devkmsg=on"); - for (auto& entry : kernel_cmdline) request.add_params(std::move(entry)); @@ -234,27 +227,21 @@ vm_tools::concierge::StartArcVmRequest CreateStartArcVmRequest( vm->set_kernel(file_system_status.guest_kernel_path().value()); - // Add / as /dev/vda. + // Add rootfs as /dev/vda. vm->set_rootfs(file_system_status.system_image_path().value()); request.set_rootfs_writable(file_system_status.is_host_rootfs_writable() && file_system_status.is_system_image_ext_format()); - // Add /vendor as /dev/vdb. + // Add /vendor as /dev/block/vdb. The device name has to be consistent with + // the one in GenerateFirstStageFstab() in ../arc_util.cc. vm_tools::concierge::DiskImage* disk_image = request.add_disks(); disk_image->set_path(file_system_status.vendor_image_path().value()); disk_image->set_image_type(vm_tools::concierge::DISK_IMAGE_AUTO); disk_image->set_writable(false); disk_image->set_do_mount(true); - // Add /vendor as /dev/vdc. - // TODO(yusukes): Remove /dev/vdc once Android side stops using it. - disk_image = request.add_disks(); - disk_image->set_path(file_system_status.vendor_image_path().value()); - disk_image->set_image_type(vm_tools::concierge::DISK_IMAGE_AUTO); - disk_image->set_writable(false); - disk_image->set_do_mount(true); - - // Add /run/imageloader/.../android_demo_apps.squash as /dev/vdd if needed. + // Add /run/imageloader/.../android_demo_apps.squash as /dev/block/vdc if + // needed. // TODO(b/144542975): Do this on upgrade instead. if (!demo_session_apps_path.empty()) { disk_image = request.add_disks(); @@ -482,7 +469,7 @@ class ArcVmClientAdapter : public ArcClientAdapter, OnArcInstanceStopped(); } - void ConciergeServiceRestarted() override {} + void ConciergeServiceStarted() override {} private: void OnArcBugReportBackedUp(bool result) { @@ -537,8 +524,6 @@ class ArcVmClientAdapter : public ArcClientAdapter, VLOG(1) << "OnArcVmServerProxyJobStopped: job " << (result ? "stopped" : "not running?"); - should_notify_observers_ = true; - // Make sure to stop arc-keymasterd if it's already started. Always move // |callback| as is and ignore |result|. chromeos::UpstartClient::Get()->StopJob( @@ -616,6 +601,8 @@ class ArcVmClientAdapter : public ArcClientAdapter, return; } std::move(callback).Run(true); + // StartMiniArc() successful. Update the member variable here. + should_notify_observers_ = true; } void OnConciergeStarted(UpgradeParams params, diff --git a/chromium/components/arc/session/arc_vm_client_adapter_unittest.cc b/chromium/components/arc/session/arc_vm_client_adapter_unittest.cc index 2328423e97b..067862033b5 100644 --- a/chromium/components/arc/session/arc_vm_client_adapter_unittest.cc +++ b/chromium/components/arc/session/arc_vm_client_adapter_unittest.cc @@ -58,7 +58,7 @@ constexpr int64_t kCid = 123; StartParams GetPopulatedStartParams() { StartParams params; - params.native_bridge_experiment = true; + params.native_bridge_experiment = false; params.lcd_density = 240; params.arc_file_picker_experiment = true; params.play_store_auto_update = @@ -483,6 +483,10 @@ TEST_F(ArcVmClientAdapterTest, StartMiniArc) { StartMiniArc(); // Confirm that no VM is started. ARCVM doesn't support mini ARC yet. EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called()); + + // TODO(wvk): Once mini VM is supported, call StopArcInstance() and + // SendVmStoppedSignal() here, then verify arc_instance_stopped_called() + // becomes true. See StopArcInstance test for more details. } // Tests that StartMiniArc() still succeeds even when Upstart fails to stop @@ -494,6 +498,10 @@ TEST_F(ArcVmClientAdapterTest, StartMiniArc_StopArcVmServerProxyJobFail) { StartMiniArc(); // Confirm that no VM is started. ARCVM doesn't support mini ARC yet. EXPECT_FALSE(GetTestConciergeClient()->start_arc_vm_called()); + + // TODO(wvk): Once mini VM is supported, call StopArcInstance() here, + // then verify arc_instance_stopped_called() never becomes true. Same + // for other StartMiniArc_...Fail tests. } // Tests that StartMiniArc() fails if Upstart fails to start arc-keymasterd. @@ -517,7 +525,7 @@ TEST_F(ArcVmClientAdapterTest, StartMiniArc_StopArcKeymasterJobFail) { } // Tests that StartMiniArc() fails when Upstart fails to start the job. -TEST_F(ArcVmClientAdapterTest, StartMiniArc_Fail) { +TEST_F(ArcVmClientAdapterTest, StartMiniArc_StartArcVmPerBoardFeaturesJobFail) { // Inject failure to FakeUpstartClient. InjectUpstartStartJobFailure(kArcVmPerBoardFeaturesJobName); @@ -1011,11 +1019,11 @@ TEST_F(ArcVmClientAdapterTest, VmStartedSignal) { run_loop()->RunUntilIdle(); } -// Tests that ConciergeServiceRestarted() doesn't crash. -TEST_F(ArcVmClientAdapterTest, TestConciergeServiceRestarted) { +// Tests that ConciergeServiceStarted() doesn't crash. +TEST_F(ArcVmClientAdapterTest, TestConciergeServiceStarted) { StartMiniArc(); for (auto& observer : GetTestConciergeClient()->observer_list()) - observer.ConciergeServiceRestarted(); + observer.ConciergeServiceStarted(); } // Tests that the kernel parameter does not include "rw" by default. @@ -1090,8 +1098,8 @@ TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNone) { "androidboot.native_bridge=0")); } -// Tests that the binary translation type is set to Houdini when only Houdini -// library is enabled by USE flags. +// Tests that the binary translation type is set to Houdini when only 32-bit +// Houdini library is enabled by USE flags. TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeHoudini) { base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--enable-houdini"}); @@ -1104,8 +1112,8 @@ TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeHoudini) { "androidboot.native_bridge=libhoudini.so")); } -// Tests that the binary translation type is set to Houdini when only Houdini -// 64-bit library is enabled by USE flags. +// Tests that the binary translation type is set to Houdini when only 64-bit +// Houdini library is enabled by USE flags. TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeHoudini64) { base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--enable-houdini64"}); @@ -1119,7 +1127,7 @@ TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeHoudini64) { } // Tests that the binary translation type is set to NDK translation when only -// NDK translation library is enabled by USE flags. +// 32-bit NDK translation library is enabled by USE flags. TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNdkTranslation) { base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--enable-ndk-translation"}); @@ -1132,13 +1140,28 @@ TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNdkTranslation) { "androidboot.native_bridge=libndk_translation.so")); } +// Tests that the binary translation type is set to NDK translation when only +// 64-bit NDK translation library is enabled by USE flags. +TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNdkTranslation64) { + base::CommandLine::ForCurrentProcess()->InitFromArgv( + {"", "--enable-ndk-translation64"}); + StartParams start_params(GetPopulatedStartParams()); + SetValidUserInfo(); + StartMiniArcWithParams(true, std::move(start_params)); + UpgradeArc(true); + EXPECT_TRUE( + base::Contains(GetTestConciergeClient()->start_arc_vm_request().params(), + "androidboot.native_bridge=libndk_translation.so")); +} + // Tests that the binary translation type is set to NDK translation when both // Houdini and NDK translation libraries are enabled by USE flags, and the -// parameter start_params.native_bridge_experiment is set. +// parameter start_params.native_bridge_experiment is set to true. TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNativeBridgeExperiment) { base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--enable-houdini", "--enable-ndk-translation"}); StartParams start_params(GetPopulatedStartParams()); + start_params.native_bridge_experiment = true; SetValidUserInfo(); StartMiniArcWithParams(true, std::move(start_params)); UpgradeArc(true); @@ -1149,7 +1172,7 @@ TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNativeBridgeExperiment) { // Tests that the binary translation type is set to Houdini when both Houdini // and NDK translation libraries are enabled by USE flags, and the parameter -// start_params.native_bridge_experiment is not set. +// start_params.native_bridge_experiment is set to false. TEST_F(ArcVmClientAdapterTest, BintaryTranslationTypeNoNativeBridgeExperiment) { base::CommandLine::ForCurrentProcess()->InitFromArgv( {"", "--enable-houdini", "--enable-ndk-translation"}); diff --git a/chromium/components/arc/session/connection_holder.h b/chromium/components/arc/session/connection_holder.h index 0d35f5ff224..6dcfb061f3f 100644 --- a/chromium/components/arc/session/connection_holder.h +++ b/chromium/components/arc/session/connection_holder.h @@ -11,6 +11,7 @@ #include #include "base/bind.h" +#include "base/logging.h" #include "base/macros.h" #include "base/observer_list.h" #include "base/threading/thread_checker.h" @@ -133,8 +134,8 @@ class ConnectionHolderImpl { // When both the instance and host are ready, start connection. // TODO(crbug.com/750563): Fix the race issue. auto receiver = std::make_unique>(host_); - mojo::InterfacePtr host_proxy; - receiver->Bind(mojo::MakeRequest(&host_proxy)); + mojo::PendingRemote host_proxy; + receiver->Bind(host_proxy.InitWithNewPipeAndPassReceiver()); instance_->Init( std::move(host_proxy), base::BindOnce(&ConnectionHolderImpl::OnConnectionReady, diff --git a/chromium/components/arc/session/file_system_status.cc b/chromium/components/arc/session/file_system_status.cc index 3d0eb8f00f4..d4cccb9fcba 100644 --- a/chromium/components/arc/session/file_system_status.cc +++ b/chromium/components/arc/session/file_system_status.cc @@ -22,7 +22,7 @@ namespace { constexpr const char kArcVmConfigJsonPath[] = "/usr/share/arcvm/config.json"; constexpr const char kBuiltinPath[] = "/opt/google/vms/android"; -constexpr const char kFstab[] = "fstab"; +constexpr const char kFstabPath[] = "/run/arcvm/host_generated/fstab"; constexpr const char kKernel[] = "vmlinux"; constexpr const char kRootFs[] = "system.raw.img"; constexpr const char kVendorImage[] = "vendor.raw.img"; @@ -41,7 +41,7 @@ FileSystemStatus::FileSystemStatus() system_image_path_(base::FilePath(kBuiltinPath).Append(kRootFs)), vendor_image_path_(base::FilePath(kBuiltinPath).Append(kVendorImage)), guest_kernel_path_(base::FilePath(kBuiltinPath).Append(kKernel)), - fstab_path_(base::FilePath(kBuiltinPath).Append(kFstab)), + fstab_path_(kFstabPath), is_system_image_ext_format_(IsSystemImageExtFormat(system_image_path_)) {} // static diff --git a/chromium/components/arc/usb/usb_host_bridge.h b/chromium/components/arc/usb/usb_host_bridge.h index 8082b97470e..f292a593a8c 100644 --- a/chromium/components/arc/usb/usb_host_bridge.h +++ b/chromium/components/arc/usb/usb_host_bridge.h @@ -108,7 +108,6 @@ class ArcUsbHostBridge : public KeyedService, SEQUENCE_CHECKER(sequence_); ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. - mojom::UsbHostHostPtr usb_host_ptr_; // Connection to the DeviceService for usb manager. mojo::Remote usb_manager_; diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc index 9b4a79d431c..b38784c8f60 100644 --- a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc +++ b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc @@ -282,13 +282,13 @@ void GpuArcVideoDecodeAccelerator::ExecuteRequest( void GpuArcVideoDecodeAccelerator::Initialize( mojom::VideoDecodeAcceleratorConfigPtr config, - mojom::VideoDecodeClientPtr client, + mojo::PendingRemote client, InitializeCallback callback) { VLOGF(2) << "profile = " << config->profile << ", secure_mode = " << config->secure_mode; DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); CHECK(!client_); - client_ = std::move(client); + client_.Bind(std::move(client)); auto result = InitializeTask(std::move(config)); diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h index 5de76657dd5..f3d2afa9e48 100644 --- a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h +++ b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h @@ -16,6 +16,8 @@ #include "components/arc/mojom/video_decode_accelerator.mojom.h" #include "gpu/config/gpu_preferences.h" #include "media/video/video_decode_accelerator.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" namespace arc { @@ -61,7 +63,7 @@ class GpuArcVideoDecodeAccelerator // mojom::VideoDecodeAccelerator implementation. void Initialize(mojom::VideoDecodeAcceleratorConfigPtr config, - mojom::VideoDecodeClientPtr client, + mojo::PendingRemote client, InitializeCallback callback) override; void Decode(mojom::BitstreamBufferPtr bitstream_buffer) override; void AssignPictureBuffers(uint32_t count) override; @@ -72,7 +74,6 @@ class GpuArcVideoDecodeAccelerator void ReusePictureBuffer(int32_t picture_buffer_id) override; void Flush(FlushCallback callback) override; void Reset(ResetCallback callback) override; - private: using PendingCallback = base::OnceCallback; @@ -140,7 +141,7 @@ class GpuArcVideoDecodeAccelerator gpu::GpuPreferences gpu_preferences_; std::unique_ptr vda_; - mojom::VideoDecodeClientPtr client_; + mojo::Remote client_; gfx::Size coded_size_; gfx::Size pending_coded_size_; diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc index 48b07c1ad4c..4db437aeeff 100644 --- a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc +++ b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc @@ -114,7 +114,7 @@ void GpuArcVideoEncodeAccelerator::GetSupportedProfiles( void GpuArcVideoEncodeAccelerator::Initialize( const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client, + mojo::PendingRemote client, InitializeCallback callback) { auto result = InitializeTask(config, std::move(client)); std::move(callback).Run(result); @@ -122,7 +122,7 @@ void GpuArcVideoEncodeAccelerator::Initialize( void GpuArcVideoEncodeAccelerator::InitializeDeprecated( const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client, + mojo::PendingRemote client, InitializeDeprecatedCallback callback) { auto result = InitializeTask(config, std::move(client)); std::move(callback).Run(result == @@ -132,7 +132,7 @@ void GpuArcVideoEncodeAccelerator::InitializeDeprecated( mojom::VideoEncodeAccelerator::Result GpuArcVideoEncodeAccelerator::InitializeTask( const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client) { + mojo::PendingRemote client) { DVLOGF(2) << config.AsHumanReadableString(); if (!config.storage_type.has_value()) { DLOG(ERROR) << "storage type must be specified"; @@ -147,7 +147,7 @@ GpuArcVideoEncodeAccelerator::InitializeTask( DLOG(ERROR) << "Failed to create a VideoEncodeAccelerator."; return mojom::VideoEncodeAccelerator::Result::kPlatformFailureError; } - client_ = std::move(client); + client_.Bind(std::move(client)); return mojom::VideoEncodeAccelerator::Result::kSuccess; } diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h index 63508c124a8..8531b7ee13d 100644 --- a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h +++ b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h @@ -16,6 +16,8 @@ #include "gpu/config/gpu_preferences.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "media/video/video_encode_accelerator.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" namespace arc { @@ -33,7 +35,6 @@ class GpuArcVideoEncodeAccelerator using VideoPixelFormat = media::VideoPixelFormat; using VideoCodecProfile = media::VideoCodecProfile; using Error = media::VideoEncodeAccelerator::Error; - using VideoEncodeClientPtr = ::arc::mojom::VideoEncodeClientPtr; // VideoEncodeAccelerator::Client implementation. void RequireBitstreamBuffers(unsigned int input_count, @@ -48,14 +49,15 @@ class GpuArcVideoEncodeAccelerator void GetSupportedProfiles(GetSupportedProfilesCallback callback) override; void Initialize(const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client, + mojo::PendingRemote client, InitializeCallback callback) override; - void InitializeDeprecated(const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client, - InitializeDeprecatedCallback callback) override; + void InitializeDeprecated( + const media::VideoEncodeAccelerator::Config& config, + mojo::PendingRemote client, + InitializeDeprecatedCallback callback) override; mojom::VideoEncodeAccelerator::Result InitializeTask( const media::VideoEncodeAccelerator::Config& config, - VideoEncodeClientPtr client); + mojo::PendingRemote client); void Encode(media::VideoPixelFormat format, mojo::ScopedHandle fd, @@ -86,7 +88,7 @@ class GpuArcVideoEncodeAccelerator gpu::GpuPreferences gpu_preferences_; std::unique_ptr accelerator_; - ::arc::mojom::VideoEncodeClientPtr client_; + mojo::Remote<::arc::mojom::VideoEncodeClient> client_; gfx::Size coded_size_; gfx::Size visible_size_; VideoPixelFormat input_pixel_format_; diff --git a/chromium/components/assist_ranker/ranker_example_util.cc b/chromium/components/assist_ranker/ranker_example_util.cc index ceedd8f9b18..ea9ed5ab311 100644 --- a/chromium/components/assist_ranker/ranker_example_util.cc +++ b/chromium/components/assist_ranker/ranker_example_util.cc @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "components/assist_ranker/ranker_example_util.h" + #include -#include "components/assist_ranker/ranker_example_util.h" #include "base/bit_cast.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/metrics/metrics_hashes.h" +#include "base/notreached.h" #include "base/strings/stringprintf.h" namespace assist_ranker { diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn index f246baeb159..e29c613e982 100644 --- a/chromium/components/autofill/android/BUILD.gn +++ b/chromium/components/autofill/android/BUILD.gn @@ -41,6 +41,7 @@ android_library("autofill_java") { "//base:base_java", "//content/public/android:content_java", "//third_party/android_deps:android_support_v7_appcompat_java", + "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", "//ui/android:ui_java", ] sources = [ @@ -54,49 +55,3 @@ android_library("autofill_java") { ] srcjar_deps = [ ":autofill_core_browser_java_enums" ] } - -android_library("provider_java") { - deps = [ - "//base:base_java", - "//base:jni_java", - "//components/autofill/core/common/mojom:mojo_types_java", - "//components/version_info/android:version_constants_java", - "//content/public/android:content_java", - "//third_party/android_deps:androidx_annotation_annotation_java", - "//ui/android:ui_java", - ] - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - sources = [ - "java/src/org/chromium/components/autofill/AutofillActionModeCallback.java", - "java/src/org/chromium/components/autofill/AutofillManagerWrapper.java", - "java/src/org/chromium/components/autofill/AutofillProvider.java", - "java/src/org/chromium/components/autofill/AutofillProviderImpl.java", - "java/src/org/chromium/components/autofill/AutofillProviderUMA.java", - "java/src/org/chromium/components/autofill/FormData.java", - "java/src/org/chromium/components/autofill/FormFieldData.java", - ] -} - -generate_jni("jni_headers") { - sources = [ - "java/src/org/chromium/components/autofill/AutofillProvider.java", - "java/src/org/chromium/components/autofill/FormData.java", - "java/src/org/chromium/components/autofill/FormFieldData.java", - ] -} - -static_library("provider") { - sources = [ - "autofill_provider_android.cc", - "autofill_provider_android.h", - "form_data_android.cc", - "form_data_android.h", - "form_field_data_android.cc", - "form_field_data_android.h", - ] - deps = [ - ":jni_headers", - "//components/autofill/core/browser:browser", - "//content/public/browser", - ] -} diff --git a/chromium/components/autofill/android/OWNERS b/chromium/components/autofill/android/OWNERS index 7f956d082c5..475b1659fb0 100644 --- a/chromium/components/autofill/android/OWNERS +++ b/chromium/components/autofill/android/OWNERS @@ -1,8 +1,2 @@ file://ui/android/OWNERS -# Files related to integration with system autofill -per-file *autofill_provider*=michaelbai@chromium.org -per-file *AutofillProvider*=michaelbai@chromium.org -per-file *AutofillManagerWrapper*=michaelbai@chromium.org -per-file *form*=michaelbai@chromium.org -per-file *Form*=michaelbai@chromium.org diff --git a/chromium/components/autofill/android/autofill_provider_android.cc b/chromium/components/autofill/android/autofill_provider_android.cc deleted file mode 100644 index 76129371d95..00000000000 --- a/chromium/components/autofill/android/autofill_provider_android.cc +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/android/autofill_provider_android.h" - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "components/autofill/android/form_data_android.h" -#include "components/autofill/android/jni_headers/AutofillProvider_jni.h" -#include "components/autofill/core/browser/autofill_driver.h" -#include "components/autofill/core/browser/autofill_handler_proxy.h" -#include "components/autofill/core/common/autofill_constants.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/web_contents.h" -#include "ui/gfx/geometry/rect_f.h" - -using base::android::AttachCurrentThread; -using base::android::ConvertUTF16ToJavaString; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaRef; -using base::android::ScopedJavaLocalRef; -using content::BrowserThread; -using content::WebContents; -using gfx::RectF; - -namespace autofill { - -using mojom::SubmissionSource; - -AutofillProviderAndroid::AutofillProviderAndroid( - const JavaRef& jcaller, - content::WebContents* web_contents) - : id_(kNoQueryId), web_contents_(web_contents), check_submission_(false) { - OnJavaAutofillProviderChanged(AttachCurrentThread(), jcaller); -} - -void AutofillProviderAndroid::OnJavaAutofillProviderChanged( - JNIEnv* env, - const JavaRef& jcaller) { - // If the current Java object isn't null (e.g., because it hasn't been - // garbage-collected yet), clear its reference to this object. - ScopedJavaLocalRef obj = java_ref_.get(env); - if (!obj.is_null()) { - Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0); - } - - java_ref_ = JavaObjectWeakGlobalRef(env, jcaller); - - // If the new Java object isn't null, set its native object to |this|. - obj = java_ref_.get(env); - if (!obj.is_null()) { - Java_AutofillProvider_setNativeAutofillProvider( - env, obj, reinterpret_cast(this)); - } -} - -AutofillProviderAndroid::~AutofillProviderAndroid() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - // Remove the reference to this object on the Java side. - Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0); -} - -void AutofillProviderAndroid::OnQueryFormFieldAutofill( - AutofillHandlerProxy* handler, - int32_t id, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - bool /*unused_autoselect_first_suggestion*/) { - // The id isn't passed to Java side because Android API guarantees the - // response is always for current session, so we just use the current id - // in response, see OnAutofillAvailable. - DCHECK_CURRENTLY_ON(BrowserThread::UI); - id_ = id; - - // Focus or field value change will also trigger the query, so it should be - // ignored if the form is same. - if (ShouldStartNewSession(handler, form)) - StartNewSession(handler, form, field, bounding_box); -} - -bool AutofillProviderAndroid::ShouldStartNewSession( - AutofillHandlerProxy* handler, - const FormData& form) { - // Only start a new session when form or handler is changed, the change of - // handler indicates query from other frame and a new session is needed. - return !IsCurrentlyLinkedForm(form) || !IsCurrentlyLinkedHandler(handler); -} - -void AutofillProviderAndroid::StartNewSession(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - form_ = std::make_unique( - form, base::BindRepeating( - &AutofillDriver::TransformBoundingBoxToViewportCoordinates, - base::Unretained(handler->driver()))); - - size_t index; - if (!form_->GetFieldIndex(field, &index)) { - form_.reset(); - return; - } - - FormStructure* form_structure = nullptr; - AutofillField* autofill_field = nullptr; - if (!handler->GetCachedFormAndField(form, field, &form_structure, - &autofill_field)) { - form_structure = nullptr; - } - gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); - - ScopedJavaLocalRef form_obj = form_->GetJavaPeer(form_structure); - handler_ = handler->GetWeakPtr(); - Java_AutofillProvider_startAutofillSession( - env, obj, form_obj, index, transformed_bounding.x(), - transformed_bounding.y(), transformed_bounding.width(), - transformed_bounding.height()); -} - -void AutofillProviderAndroid::OnAutofillAvailable(JNIEnv* env, - jobject jcaller, - jobject formData) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (handler_) { - const FormData& form = form_->GetAutofillValues(); - SendFormDataToRenderer(handler_.get(), id_, form); - } -} - -void AutofillProviderAndroid::OnTextFieldDidChange( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - const base::TimeTicks timestamp) { - FireFormFieldDidChanged(handler, form, field, bounding_box); -} - -void AutofillProviderAndroid::OnTextFieldDidScroll( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - size_t index; - if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || - !form_->GetSimilarFieldIndex(field, &index)) - return; - - form_->OnFormFieldDidChange(index, field.value); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); - Java_AutofillProvider_onTextFieldDidScroll( - env, obj, index, transformed_bounding.x(), transformed_bounding.y(), - transformed_bounding.width(), transformed_bounding.height()); -} - -void AutofillProviderAndroid::OnSelectControlDidChange( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { - if (ShouldStartNewSession(handler, form)) - StartNewSession(handler, form, field, bounding_box); - FireFormFieldDidChanged(handler, form, field, bounding_box); -} - -void AutofillProviderAndroid::FireSuccessfulSubmission( - SubmissionSource source) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - Java_AutofillProvider_onFormSubmitted(env, obj, (int)source); - Reset(); -} - -void AutofillProviderAndroid::OnFormSubmitted(AutofillHandlerProxy* handler, - const FormData& form, - bool known_success, - SubmissionSource source) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form)) - return; - - if (known_success || source == SubmissionSource::FORM_SUBMISSION) { - FireSuccessfulSubmission(source); - return; - } - - check_submission_ = true; - pending_submission_source_ = source; -} - -void AutofillProviderAndroid::OnFocusNoLongerOnForm( - AutofillHandlerProxy* handler) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!IsCurrentlyLinkedHandler(handler)) - return; - - OnFocusChanged(false, 0, RectF()); -} - -void AutofillProviderAndroid::OnFocusOnFormField( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - size_t index; - if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || - !form_->GetSimilarFieldIndex(field, &index)) - return; - - // Because this will trigger a suggestion query, set request id to browser - // initiated request. - id_ = kNoQueryId; - - OnFocusChanged(true, index, ToClientAreaBound(bounding_box)); -} - -void AutofillProviderAndroid::OnFocusChanged(bool focus_on_form, - size_t index, - const gfx::RectF& bounding_box) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - Java_AutofillProvider_onFocusChanged( - env, obj, focus_on_form, index, bounding_box.x(), bounding_box.y(), - bounding_box.width(), bounding_box.height()); -} - -void AutofillProviderAndroid::FireFormFieldDidChanged( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - size_t index; - if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || - !form_->GetSimilarFieldIndex(field, &index)) - return; - - form_->OnFormFieldDidChange(index, field.value); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); - Java_AutofillProvider_onFormFieldDidChange( - env, obj, index, transformed_bounding.x(), transformed_bounding.y(), - transformed_bounding.width(), transformed_bounding.height()); -} - -void AutofillProviderAndroid::OnDidFillAutofillFormData( - AutofillHandlerProxy* handler, - const FormData& form, - base::TimeTicks timestamp) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (handler != handler_.get() || !IsCurrentlyLinkedForm(form)) - return; - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - Java_AutofillProvider_onDidFillAutofillFormData(env, obj); -} - -void AutofillProviderAndroid::OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector& forms, - const base::TimeTicks) { - handler_for_testing_ = handler->GetWeakPtr(); - if (!check_submission_) - return; - - if (handler != handler_.get()) - return; - - if (form_.get() == nullptr) - return; - - for (auto const& form : forms) { - if (form_->SimilarFormAs(form)) - return; - } - // The form_ disappeared after it was submitted, we consider the submission - // succeeded. - FireSuccessfulSubmission(pending_submission_source_); -} - -void AutofillProviderAndroid::Reset(AutofillHandlerProxy* handler) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (handler == handler_.get()) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - Java_AutofillProvider_reset(env, obj); - } -} - -bool AutofillProviderAndroid::IsCurrentlyLinkedHandler( - AutofillHandlerProxy* handler) { - return handler == handler_.get(); -} - -bool AutofillProviderAndroid::IsCurrentlyLinkedForm(const FormData& form) { - return form_ && form_->SimilarFormAs(form); -} - -gfx::RectF AutofillProviderAndroid::ToClientAreaBound( - const gfx::RectF& bounding_box) { - gfx::Rect client_area = web_contents_->GetContainerBounds(); - return bounding_box + client_area.OffsetFromOrigin(); -} - -void AutofillProviderAndroid::Reset() { - form_.reset(nullptr); - id_ = kNoQueryId; - check_submission_ = false; -} - -} // namespace autofill diff --git a/chromium/components/autofill/android/autofill_provider_android.h b/chromium/components/autofill/android/autofill_provider_android.h deleted file mode 100644 index 270a05eea00..00000000000 --- a/chromium/components/autofill/android/autofill_provider_android.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_ -#define COMPONENTS_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_ - -#include "base/android/jni_weak_ref.h" -#include "base/memory/weak_ptr.h" -#include "components/autofill/core/browser/autofill_provider.h" - -namespace content { -class WebContents; -} - -namespace autofill { - -class FormDataAndroid; - -// Android implementation of AutofillProvider, it has one instance per -// WebContents, this class is native peer of AutofillProvider.java. -class AutofillProviderAndroid : public AutofillProvider { - public: - AutofillProviderAndroid(const base::android::JavaRef& jcaller, - content::WebContents* web_contents); - // Invoked when the Java-side AutofillProvider counterpart of this object - // has been changed (either to null or to a new object). - void OnJavaAutofillProviderChanged( - JNIEnv* env, - const base::android::JavaRef& jcaller); - - ~AutofillProviderAndroid() override; - - // AutofillProvider: - void OnQueryFormFieldAutofill( - AutofillHandlerProxy* handler, - int32_t id, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - bool /*unused_autoselect_first_suggestion*/) override; - void OnTextFieldDidChange(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - const base::TimeTicks timestamp) override; - void OnTextFieldDidScroll(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) override; - void OnSelectControlDidChange(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) override; - void OnFormSubmitted(AutofillHandlerProxy* handler, - const FormData& form, - bool known_success, - mojom::SubmissionSource source) override; - void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override; - void OnFocusOnFormField(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) override; - void OnDidFillAutofillFormData(AutofillHandlerProxy* handler, - const FormData& form, - base::TimeTicks timestamp) override; - void OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector& forms, - const base::TimeTicks timestamp) override; - - void Reset(AutofillHandlerProxy* handler) override; - - // Methods called by Java. - void OnAutofillAvailable(JNIEnv* env, jobject jcaller, jobject form_data); - - private: - void FireSuccessfulSubmission(mojom::SubmissionSource source); - void OnFocusChanged(bool focus_on_form, - size_t index, - const gfx::RectF& bounding_box); - void FireFormFieldDidChanged(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box); - - bool IsCurrentlyLinkedHandler(AutofillHandlerProxy* handler); - - bool IsCurrentlyLinkedForm(const FormData& form); - - gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box); - - bool ShouldStartNewSession(AutofillHandlerProxy* handler, - const FormData& form); - - void StartNewSession(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box); - - void Reset(); - - int32_t id_; - std::unique_ptr form_; - base::WeakPtr handler_; - JavaObjectWeakGlobalRef java_ref_; - content::WebContents* web_contents_; - bool check_submission_; - // Valid only if check_submission_ is true. - mojom::SubmissionSource pending_submission_source_; - - base::WeakPtr handler_for_testing_; - - DISALLOW_COPY_AND_ASSIGN(AutofillProviderAndroid); -}; -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_ANDROID_AUTOFILL_PROVIDER_ANDROID_H_ diff --git a/chromium/components/autofill/android/form_data_android.cc b/chromium/components/autofill/android/form_data_android.cc deleted file mode 100644 index c91a0c62472..00000000000 --- a/chromium/components/autofill/android/form_data_android.cc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/android/form_data_android.h" - -#include "base/android/jni_string.h" -#include "components/autofill/android/form_field_data_android.h" -#include "components/autofill/android/jni_headers/FormData_jni.h" -#include "components/autofill/core/browser/form_structure.h" - -using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF16; -using base::android::ConvertUTF16ToJavaString; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaParamRef; -using base::android::ScopedJavaGlobalRef; -using base::android::ScopedJavaLocalRef; - -namespace autofill { - -FormDataAndroid::FormDataAndroid(const FormData& form, - const TransformCallback& callback) - : form_(form), index_(0) { - for (FormFieldData& field : form_.fields) - field.bounds = callback.Run(field.bounds); -} - -FormDataAndroid::~FormDataAndroid() = default; - -ScopedJavaLocalRef FormDataAndroid::GetJavaPeer( - const FormStructure* form_structure) { - // |form_structure| is ephemeral and shouldn't be used outside this call - // stack. - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) { - for (size_t i = 0; i < form_.fields.size(); ++i) { - fields_.push_back(std::unique_ptr( - new FormFieldDataAndroid(&form_.fields[i]))); - } - if (form_structure) - ApplyHeuristicFieldType(*form_structure); - ScopedJavaLocalRef jname = - ConvertUTF16ToJavaString(env, form_.name); - ScopedJavaLocalRef jhost = - ConvertUTF8ToJavaString(env, form_.url.GetOrigin().spec()); - obj = Java_FormData_createFormData(env, reinterpret_cast(this), - jname, jhost, form_.fields.size()); - java_ref_ = JavaObjectWeakGlobalRef(env, obj); - } - return obj; -} - -const FormData& FormDataAndroid::GetAutofillValues() { - for (std::unique_ptr& field : fields_) - field->GetValue(); - return form_; -} - -ScopedJavaLocalRef FormDataAndroid::GetNextFormFieldData(JNIEnv* env) { - DCHECK(index_ <= fields_.size()); - if (index_ == fields_.size()) - return ScopedJavaLocalRef(); - return fields_[index_++]->GetJavaPeer(); -} - -void FormDataAndroid::OnFormFieldDidChange(size_t index, - const base::string16& value) { - form_.fields[index].value = value; - fields_[index]->OnFormFieldDidChange(value); -} - -bool FormDataAndroid::GetFieldIndex(const FormFieldData& field, size_t* index) { - for (size_t i = 0; i < form_.fields.size(); ++i) { - if (form_.fields[i].SameFieldAs(field)) { - *index = i; - return true; - } - } - return false; -} - -bool FormDataAndroid::GetSimilarFieldIndex(const FormFieldData& field, - size_t* index) { - for (size_t i = 0; i < form_.fields.size(); ++i) { - if (form_.fields[i].SimilarFieldAs(field)) { - *index = i; - return true; - } - } - return false; -} - -bool FormDataAndroid::SimilarFormAs(const FormData& form) { - return form_.SimilarFormAs(form); -} - -void FormDataAndroid::ApplyHeuristicFieldType( - const FormStructure& form_structure) { - DCHECK(form_structure.field_count() == fields_.size()); - auto form_field_data_android = fields_.begin(); - for (const auto& autofill_field : form_structure) { - DCHECK(form_field_data_android->get()->SimilarFieldAs(*autofill_field)); - form_field_data_android->get()->set_heuristic_type( - AutofillType(autofill_field->heuristic_type())); - if (++form_field_data_android == fields_.end()) - break; - } -} - -} // namespace autofill diff --git a/chromium/components/autofill/android/form_data_android.h b/chromium/components/autofill/android/form_data_android.h deleted file mode 100644 index a2b33c8d917..00000000000 --- a/chromium/components/autofill/android/form_data_android.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_ -#define COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_ - -#include "base/android/jni_weak_ref.h" -#include "base/android/scoped_java_ref.h" -#include "components/autofill/core/common/form_data.h" - -namespace autofill { - -class FormFieldDataAndroid; -class FormStructure; - -// This class is native peer of FormData.java, to make autofill::FormData -// available in Java. -class FormDataAndroid { - public: - // The callback func to transform FormFieldData's bounds to viewport's - // coordinates, it is only used in FormDataAndroid constructor and transforms - // bounds in place to avoids an extra copy of FormData. - using TransformCallback = - base::RepeatingCallback; - - FormDataAndroid(const FormData& form, const TransformCallback& callback); - virtual ~FormDataAndroid(); - - base::android::ScopedJavaLocalRef GetJavaPeer( - const FormStructure* form_structure); - - // Get autofill values from Java side and return FormData. - const FormData& GetAutofillValues(); - - base::android::ScopedJavaLocalRef GetNextFormFieldData(JNIEnv* env); - - // Get index of given field, return True and index of focus field if found. - bool GetFieldIndex(const FormFieldData& field, size_t* index); - - // Get index of given field, return True and index of focus field if - // similar field is found. This method compares less attributes than - // GetFieldIndex() does, and should be used when field could be changed - // dynamically, but the changed has no impact on autofill purpose, e.g. css - // style change, see FormFieldData::SimilarFieldAs() for details. - bool GetSimilarFieldIndex(const FormFieldData& field, size_t* index); - - // Return true if this form is similar to the given form. - bool SimilarFormAs(const FormData& form); - - // Invoked when form field which specified by |index| is charged to new - // |value|. - void OnFormFieldDidChange(size_t index, const base::string16& value); - - void ApplyHeuristicFieldType(const FormStructure& form); - - const FormData& form_for_testing() { return form_; } - - private: - // Same as the form passed in from constructor, but FormFieldData's bounds is - // transformed to viewport coordinates. - FormData form_; - std::vector> fields_; - JavaObjectWeakGlobalRef java_ref_; - // keep track of index when popping up fields to Java. - size_t index_; - - DISALLOW_COPY_AND_ASSIGN(FormDataAndroid); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_ANDROID_FORM_DATA_ANDROID_H_ diff --git a/chromium/components/autofill/android/form_field_data_android.cc b/chromium/components/autofill/android/form_field_data_android.cc deleted file mode 100644 index da134267220..00000000000 --- a/chromium/components/autofill/android/form_field_data_android.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/android/form_field_data_android.h" - -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "components/autofill/android/jni_headers/FormFieldData_jni.h" -#include "components/autofill/core/common/autofill_util.h" - -using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF16; -using base::android::ConvertUTF16ToJavaString; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaParamRef; -using base::android::JavaRef; -using base::android::ScopedJavaGlobalRef; -using base::android::ScopedJavaLocalRef; -using base::android::ToJavaArrayOfStrings; - -namespace autofill { - -FormFieldDataAndroid::FormFieldDataAndroid(FormFieldData* field) - : heuristic_type_(AutofillType(UNKNOWN_TYPE)), field_ptr_(field) {} - -ScopedJavaLocalRef FormFieldDataAndroid::GetJavaPeer() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) { - ScopedJavaLocalRef jname = - ConvertUTF16ToJavaString(env, field_ptr_->name); - ScopedJavaLocalRef jlabel = - ConvertUTF16ToJavaString(env, field_ptr_->label); - ScopedJavaLocalRef jvalue = - ConvertUTF16ToJavaString(env, field_ptr_->value); - ScopedJavaLocalRef jautocomplete_attr = - ConvertUTF8ToJavaString(env, field_ptr_->autocomplete_attribute); - ScopedJavaLocalRef jplaceholder = - ConvertUTF16ToJavaString(env, field_ptr_->placeholder); - ScopedJavaLocalRef jid = - ConvertUTF16ToJavaString(env, field_ptr_->id_attribute); - ScopedJavaLocalRef jtype = - ConvertUTF8ToJavaString(env, field_ptr_->form_control_type); - ScopedJavaLocalRef joption_values = - ToJavaArrayOfStrings(env, field_ptr_->option_values); - ScopedJavaLocalRef joption_contents = - ToJavaArrayOfStrings(env, field_ptr_->option_contents); - ScopedJavaLocalRef jheuristic_type; - if (!heuristic_type_.IsUnknown()) - jheuristic_type = - ConvertUTF8ToJavaString(env, heuristic_type_.ToString()); - - obj = Java_FormFieldData_createFormFieldData( - env, jname, jlabel, jvalue, jautocomplete_attr, - field_ptr_->should_autocomplete, jplaceholder, jtype, jid, - joption_values, joption_contents, IsCheckable(field_ptr_->check_status), - IsChecked(field_ptr_->check_status), field_ptr_->max_length, - jheuristic_type, field_ptr_->bounds.x(), field_ptr_->bounds.y(), - field_ptr_->bounds.right(), field_ptr_->bounds.bottom()); - java_ref_ = JavaObjectWeakGlobalRef(env, obj); - } - return obj; -} - -void FormFieldDataAndroid::GetValue() { - JNIEnv* env = AttachCurrentThread(); - - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - if (IsCheckable(field_ptr_->check_status)) { - bool checked = Java_FormFieldData_isChecked(env, obj); - SetCheckStatus(field_ptr_, true, checked); - } else { - ScopedJavaLocalRef jvalue = Java_FormFieldData_getValue(env, obj); - if (jvalue.is_null()) - return; - field_ptr_->value = ConvertJavaStringToUTF16(env, jvalue); - } - field_ptr_->is_autofilled = true; -} - -void FormFieldDataAndroid::OnFormFieldDidChange(const base::string16& value) { - field_ptr_->value = value; - field_ptr_->is_autofilled = false; - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return; - - Java_FormFieldData_updateValue(env, obj, - ConvertUTF16ToJavaString(env, value)); -} - -bool FormFieldDataAndroid::SimilarFieldAs(const FormFieldData& field) const { - return field_ptr_->SimilarFieldAs(field); -} - -} // namespace autofill diff --git a/chromium/components/autofill/android/form_field_data_android.h b/chromium/components/autofill/android/form_field_data_android.h deleted file mode 100644 index e882bb49704..00000000000 --- a/chromium/components/autofill/android/form_field_data_android.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_ -#define COMPONENTS_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_ - -#include "base/android/jni_weak_ref.h" -#include "base/android/scoped_java_ref.h" -#include "components/autofill/core/browser/autofill_type.h" -#include "components/autofill/core/common/form_field_data.h" - -namespace autofill { - -// This class is native peer of FormFieldData.java, makes -// autofill::FormFieldData available in Java. -class FormFieldDataAndroid { - public: - FormFieldDataAndroid(FormFieldData* field); - virtual ~FormFieldDataAndroid() {} - - base::android::ScopedJavaLocalRef GetJavaPeer(); - void GetValue(); - void OnFormFieldDidChange(const base::string16& value); - bool SimilarFieldAs(const FormFieldData& field) const; - - void set_heuristic_type(const AutofillType& heuristic_type) { - heuristic_type_ = heuristic_type; - } - - private: - AutofillType heuristic_type_; - // Not owned. - FormFieldData* field_ptr_; - JavaObjectWeakGlobalRef java_ref_; - - DISALLOW_COPY_AND_ASSIGN(FormFieldDataAndroid); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_ANDROID_FORM_FIELD_DATA_ANDROID_H_ diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java deleted file mode 100644 index 6921fcab52c..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.content.Context; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; - -/** - * The class to implement autofill context menu. To match the Android native view behavior, the - * autofill context menu only appears when there is no text selected. - */ -public class AutofillActionModeCallback implements ActionMode.Callback { - private final Context mContext; - private final AutofillProvider mAutofillProvider; - private final int mAutofillMenuItemTitle; - private final int mAutofillMenuItem; - - public AutofillActionModeCallback(Context context, AutofillProvider autofillProvider) { - mContext = context; - mAutofillProvider = autofillProvider; - // TODO(michaelbai): Uses the resource directly after sdk roll to Android O MR1. - // crbug.com/740628 - mAutofillMenuItemTitle = - mContext.getResources().getIdentifier("autofill", "string", "android"); - mAutofillMenuItem = mContext.getResources().getIdentifier("autofill", "id", "android"); - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - return mAutofillMenuItemTitle != 0 && mAutofillMenuItem != 0; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - if (mAutofillMenuItemTitle != 0 && mAutofillProvider.shouldQueryAutofillSuggestion()) { - MenuItem item = menu.add( - Menu.NONE, mAutofillMenuItem, Menu.CATEGORY_SECONDARY, mAutofillMenuItemTitle); - item.setShowAsActionFlags( - MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - return true; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - if (item.getItemId() == mAutofillMenuItem) { - mAutofillProvider.queryAutofillSuggestion(); - mode.finish(); - return true; - } - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) {} -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java deleted file mode 100644 index 0287ac38f97..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Rect; -import android.os.Build; -import android.view.View; -import android.view.autofill.AutofillManager; -import android.view.autofill.AutofillValue; - -import androidx.annotation.VisibleForTesting; - -import org.chromium.base.Log; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * The class to call Android's AutofillManager. - */ -@TargetApi(Build.VERSION_CODES.O) -public class AutofillManagerWrapper { - // Don't change TAG, it is used for runtime log. - // NOTE: As a result of the above, the tag below still references the name of this class from - // when it was originally developed specifically for Android WebView. - public static final String TAG = "AwAutofillManager"; - - /** - * The observer of suggestion window. - */ - public static interface InputUIObserver { void onInputUIShown(); } - - private static class AutofillInputUIMonitor extends AutofillManager.AutofillCallback { - private WeakReference mManager; - - public AutofillInputUIMonitor(AutofillManagerWrapper manager) { - mManager = new WeakReference(manager); - } - - @Override - public void onAutofillEvent(View view, int virtualId, int event) { - AutofillManagerWrapper manager = mManager.get(); - if (manager == null) return; - manager.mIsAutofillInputUIShowing = (event == EVENT_INPUT_SHOWN); - if (event == EVENT_INPUT_SHOWN) manager.notifyInputUIChange(); - } - } - - private static boolean sIsLoggable; - private AutofillManager mAutofillManager; - private boolean mIsAutofillInputUIShowing; - private AutofillInputUIMonitor mMonitor; - private boolean mDestroyed; - private boolean mDisabled; - private ArrayList> mInputUIObservers; - - public AutofillManagerWrapper(Context context) { - updateLogStat(); - if (isLoggable()) log("constructor"); - mAutofillManager = context.getSystemService(AutofillManager.class); - mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled(); - if (mDisabled) { - if (isLoggable()) log("disabled"); - return; - } - - mMonitor = new AutofillInputUIMonitor(this); - mAutofillManager.registerCallback(mMonitor); - } - - public void notifyVirtualValueChanged(View parent, int childId, AutofillValue value) { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("notifyVirtualValueChanged"); - mAutofillManager.notifyValueChanged(parent, childId, value); - } - - public void commit(int submissionSource) { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("commit source:" + submissionSource); - mAutofillManager.commit(); - } - - public void cancel() { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("cancel"); - mAutofillManager.cancel(); - } - - public void notifyVirtualViewEntered(View parent, int childId, Rect absBounds) { - // Log warning only when the autofill is triggered. - if (mDisabled) { - Log.w(TAG, "Autofill is disabled: AutofillManager isn't available in given Context."); - return; - } - if (checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("notifyVirtualViewEntered"); - mAutofillManager.notifyViewEntered(parent, childId, absBounds); - } - - public void notifyVirtualViewExited(View parent, int childId) { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("notifyVirtualViewExited"); - mAutofillManager.notifyViewExited(parent, childId); - } - - public void requestAutofill(View parent, int virtualId, Rect absBounds) { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("requestAutofill"); - mAutofillManager.requestAutofill(parent, virtualId, absBounds); - } - - public boolean isAutofillInputUIShowing() { - if (mDisabled || checkAndWarnIfDestroyed()) return false; - if (isLoggable()) log("isAutofillInputUIShowing: " + mIsAutofillInputUIShowing); - return mIsAutofillInputUIShowing; - } - - public void destroy() { - if (mDisabled || checkAndWarnIfDestroyed()) return; - if (isLoggable()) log("destroy"); - try { - // The binder in the autofill service side might already be dropped, - // unregisterCallback() will cause various exceptions in this - // scenario (see crbug.com/1078337), catching RuntimeException here prevents crash. - mAutofillManager.unregisterCallback(mMonitor); - } catch (RuntimeException e) { - // We are not logging anything here since some of the exceptions are raised as 'generic' - // RuntimeException which makes it difficult to catch and ignore separately; and the - // RuntimeException seemed only happen in Android O, therefore, isn't actionable. - } finally { - mAutofillManager = null; - mDestroyed = true; - } - } - - public boolean isDisabled() { - return mDisabled; - } - - private boolean checkAndWarnIfDestroyed() { - if (mDestroyed) { - Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper", - new Throwable()); - } - return mDestroyed; - } - - public void addInputUIObserver(InputUIObserver observer) { - if (observer == null) return; - if (mInputUIObservers == null) { - mInputUIObservers = new ArrayList>(); - } - mInputUIObservers.add(new WeakReference(observer)); - } - - public void removeInputUIObserver(InputUIObserver observer) { - if (observer == null) return; - for (Iterator> i = mInputUIObservers.listIterator(); - i.hasNext();) { - WeakReference o = i.next(); - if (o.get() == null || o.get() == observer) i.remove(); - } - } - - @VisibleForTesting - public void notifyInputUIChange() { - for (Iterator> i = mInputUIObservers.listIterator(); - i.hasNext();) { - WeakReference o = i.next(); - InputUIObserver observer = o.get(); - if (observer == null) { - i.remove(); - continue; - } - observer.onInputUIShown(); - } - } - - public void notifyNewSessionStarted() { - updateLogStat(); - if (isLoggable()) log("Session starts"); - } - - /** - * Always check isLoggable() before call this method. - */ - public static void log(String log) { - // Log.i() instead of Log.d() is used here because log.d() is stripped out in release build. - Log.i(TAG, log); - } - - public static boolean isLoggable() { - return sIsLoggable; - } - - private static void updateLogStat() { - // Use 'setprop log.tag.AwAutofillManager DEBUG' to enable the log at runtime. - // NOTE: See the comment on TAG above for why this is still AwAutofillManager. - sIsLoggable = Log.isLoggable(TAG, Log.DEBUG); - } -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java deleted file mode 100644 index a7d05d359a4..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.util.SparseArray; -import android.view.ViewGroup; -import android.view.ViewStructure; -import android.view.autofill.AutofillValue; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; -import org.chromium.content_public.browser.WebContents; - -/** - * This class defines interface of AutofillProvider, it doesn't use chrome's - * autofill service or suggestion UI, instead, uses third party autofill service - * by knowing of format structure and user's input. - * - * AutofillProvider handles one autofill session at time, each call of - * queryFormFieldAutofill cancels previous session and starts a new one, the - * calling of other methods shall associate with current session. - * - */ -@JNINamespace("autofill") -public abstract class AutofillProvider { - public AutofillProvider() {} - - /** - * Invoked when container view is changed. - * - * @param containerView new container view. - */ - public abstract void onContainerViewChanged(ViewGroup containerView); - - public abstract void setWebContents(WebContents webContents); - - /** - * Invoked when autofill value is available, AutofillProvider shall fill the - * form with the provided values. - * - * @param values the array of autofill values, the key is virtual id of form - * field. - */ - public abstract void autofill(final SparseArray values); - - /** - * Invoked when autofill service needs the form structure. - * - * @param structure see View.onProvideAutofillVirtualStructure() - * @param flags see View.onProvideAutofillVirtualStructure() - */ - public abstract void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags); - - /** - * @return whether query autofill suggestion. - */ - public abstract boolean shouldQueryAutofillSuggestion(); - - public abstract void queryAutofillSuggestion(); - - /** - * Invoked when filling form is need. AutofillProvider shall ask autofill - * service for the values with which to fill the form. - * - * @param formData the form needs to fill. - * @param focus the index of focus field in formData - * @param x the boundary of focus field. - * @param y the boundary of focus field. - * @param width the boundary of focus field. - * @param height the boundary of focus field. - */ - @CalledByNative - protected abstract void startAutofillSession( - FormData formData, int focus, float x, float y, float width, float height); - - /** - * Invoked when form field's value is changed. - * - * @param index index of field in current form. - * @param x the boundary of focus field. - * @param y the boundary of focus field. - * @param width the boundary of focus field. - * @param height the boundary of focus field. - * - */ - @CalledByNative - protected abstract void onFormFieldDidChange( - int index, float x, float y, float width, float height); - - /** - * Invoked when text field is scrolled. - * - * @param index index of field in current form. - * @param x the boundary of focus field. - * @param y the boundary of focus field. - * @param width the boundary of focus field. - * @param height the boundary of focus field. - * - */ - @CalledByNative - protected abstract void onTextFieldDidScroll( - int index, float x, float y, float width, float height); - - /** - * Invoked when current form will be submitted. - * @param submissionSource the submission source, could be any member defined in - * SubmissionSource.java - */ - @CalledByNative - protected abstract void onFormSubmitted(int submissionSource); - - /** - * Invoked when focus field changed. - * - * @param focusOnForm whether focus is still on form. - * @param focusItem the index of field has focus - * @param x the boundary of focus field. - * @param y the boundary of focus field. - * @param width the boundary of focus field. - * @param height the boundary of focus field. - */ - @CalledByNative - protected abstract void onFocusChanged( - boolean focusOnForm, int focusItem, float x, float y, float width, float height); - - /** - * Send form to renderer for filling. - * - * @param nativeAutofillProvider the native autofill provider. - * @param formData the form to fill. - */ - protected void autofill(long nativeAutofillProvider, FormData formData) { - AutofillProviderJni.get().onAutofillAvailable( - nativeAutofillProvider, AutofillProvider.this, formData); - } - - /** - * Invoked when current query need to be reset. - */ - @CalledByNative - protected abstract void reset(); - - @CalledByNative - protected abstract void setNativeAutofillProvider(long nativeAutofillProvider); - - @CalledByNative - protected abstract void onDidFillAutofillFormData(); - - @NativeMethods - interface Natives { - void onAutofillAvailable( - long nativeAutofillProviderAndroid, AutofillProvider caller, FormData formData); - } -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderImpl.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderImpl.java deleted file mode 100644 index e53188067c3..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderImpl.java +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.Build; -import android.os.Bundle; -import android.util.SparseArray; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewStructure; -import android.view.autofill.AutofillValue; - -import androidx.annotation.VisibleForTesting; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.DoNotInline; -import org.chromium.base.metrics.ScopedSysTraceEvent; -import org.chromium.components.version_info.VersionConstants; -import org.chromium.content_public.browser.WebContents; -import org.chromium.ui.base.WindowAndroid; -import org.chromium.ui.display.DisplayAndroid; - -/** - * This class uses Android autofill service to fill web form. All methods are - * supposed to be called in UI thread. - * - * This class doesn't have 1:1 mapping to native AutofillProviderAndroid; the - * normal ownership model is that this object is owned by the embedder-specific - * Java WebContents wrapper (e.g., AwContents.java in //android_webview), and - * AutofillProviderAndroid is owned by the embedder-specific C++ WebContents - * wrapper (e.g., native AwContents in //android_webview). - * - * DoNotInline since it causes class verification errors, see crbug.com/991851. - */ -@DoNotInline -@TargetApi(Build.VERSION_CODES.O) -public class AutofillProviderImpl extends AutofillProvider { - private static final String TAG = "AutofillProviderImpl"; - private static class FocusField { - public final short fieldIndex; - public final Rect absBound; - - public FocusField(short fieldIndex, Rect absBound) { - this.fieldIndex = fieldIndex; - this.absBound = absBound; - } - } - /** - * The class to wrap the request to framework. - * - * Though framework guarantees always giving us the autofill value of current - * session, we still want to verify this by using unique virtual id which is - * composed of sessionId and form field index, we don't use the request id - * which comes from renderer as session id because it is not unique. - */ - private static class AutofillRequest { - private static final int INIT_ID = 1; // ID can't be 0 in Android. - private static int sSessionId = INIT_ID; - public final int sessionId; - private FormData mFormData; - private FocusField mFocusField; - - public AutofillRequest(FormData formData, FocusField focus) { - sessionId = getNextClientId(); - mFormData = formData; - mFocusField = focus; - } - - public void fillViewStructure(ViewStructure structure) { - structure.setWebDomain(mFormData.mHost); - structure.setHtmlInfo(structure.newHtmlInfoBuilder("form") - .addAttribute("name", mFormData.mName) - .build()); - int index = structure.addChildCount(mFormData.mFields.size()); - short fieldIndex = 0; - for (FormFieldData field : mFormData.mFields) { - ViewStructure child = structure.newChild(index++); - int virtualId = toVirtualId(sessionId, fieldIndex++); - child.setAutofillId(structure.getAutofillId(), virtualId); - if (field.mAutocompleteAttr != null && !field.mAutocompleteAttr.isEmpty()) { - child.setAutofillHints(field.mAutocompleteAttr.split(" +")); - } - child.setHint(field.mPlaceholder); - - RectF bounds = field.getBoundsInContainerViewCoordinates(); - // Field has no scroll. - child.setDimens((int) bounds.left, (int) bounds.top, 0 /* scrollX*/, - 0 /* scrollY */, (int) bounds.width(), (int) bounds.height()); - - ViewStructure.HtmlInfo.Builder builder = - child.newHtmlInfoBuilder("input") - .addAttribute("name", field.mName) - .addAttribute("type", field.mType) - .addAttribute("label", field.mLabel) - .addAttribute("ua-autofill-hints", field.mHeuristicType) - .addAttribute("id", field.mId); - - switch (field.getControlType()) { - case FormFieldData.ControlType.LIST: - child.setAutofillType(View.AUTOFILL_TYPE_LIST); - child.setAutofillOptions(field.mOptionContents); - int i = findIndex(field.mOptionValues, field.getValue()); - if (i != -1) { - child.setAutofillValue(AutofillValue.forList(i)); - } - break; - case FormFieldData.ControlType.TOGGLE: - child.setAutofillType(View.AUTOFILL_TYPE_TOGGLE); - child.setAutofillValue(AutofillValue.forToggle(field.isChecked())); - break; - case FormFieldData.ControlType.TEXT: - child.setAutofillType(View.AUTOFILL_TYPE_TEXT); - child.setAutofillValue(AutofillValue.forText(field.getValue())); - if (field.mMaxLength != 0) { - builder.addAttribute("maxlength", String.valueOf(field.mMaxLength)); - } - break; - default: - break; - } - child.setHtmlInfo(builder.build()); - } - } - - public boolean autofill(final SparseArray values) { - for (int i = 0; i < values.size(); ++i) { - int id = values.keyAt(i); - if (toSessionId(id) != sessionId) return false; - AutofillValue value = values.get(id); - if (value == null) continue; - short index = toIndex(id); - if (index < 0 || index >= mFormData.mFields.size()) return false; - FormFieldData field = mFormData.mFields.get(index); - if (field == null) return false; - try { - switch (field.getControlType()) { - case FormFieldData.ControlType.LIST: - int j = value.getListValue(); - if (j < 0 && j >= field.mOptionValues.length) continue; - field.setAutofillValue(field.mOptionValues[j]); - break; - case FormFieldData.ControlType.TOGGLE: - field.setChecked(value.getToggleValue()); - break; - case FormFieldData.ControlType.TEXT: - field.setAutofillValue((String) value.getTextValue()); - break; - default: - break; - } - } catch (IllegalStateException e) { - // Refer to crbug.com/1080580 . - Log.e(TAG, "The given AutofillValue wasn't expected, abort autofill.", e); - return false; - } - } - return true; - } - - public void setFocusField(FocusField focusField) { - mFocusField = focusField; - } - - public FocusField getFocusField() { - return mFocusField; - } - - public int getFieldCount() { - return mFormData.mFields.size(); - } - - public AutofillValue getFieldNewValue(int index) { - FormFieldData field = mFormData.mFields.get(index); - if (field == null) return null; - switch (field.getControlType()) { - case FormFieldData.ControlType.LIST: - int i = findIndex(field.mOptionValues, field.getValue()); - if (i == -1) return null; - return AutofillValue.forList(i); - case FormFieldData.ControlType.TOGGLE: - return AutofillValue.forToggle(field.isChecked()); - case FormFieldData.ControlType.TEXT: - return AutofillValue.forText(field.getValue()); - default: - return null; - } - } - - public int getVirtualId(short index) { - return toVirtualId(sessionId, index); - } - - public FormFieldData getField(short index) { - return mFormData.mFields.get(index); - } - - private static int findIndex(String[] values, String value) { - if (values != null && value != null) { - for (int i = 0; i < values.length; i++) { - if (value.equals(values[i])) return i; - } - } - return -1; - } - - private static int getNextClientId() { - ThreadUtils.assertOnUiThread(); - if (sSessionId == 0xffff) sSessionId = INIT_ID; - return sSessionId++; - } - - private static int toSessionId(int virtualId) { - return (virtualId & 0xffff0000) >> 16; - } - - private static short toIndex(int virtualId) { - return (short) (virtualId & 0xffff); - } - - private static int toVirtualId(int clientId, short index) { - return (clientId << 16) | index; - } - } - - private final String mProviderName; - private AutofillManagerWrapper mAutofillManager; - private ViewGroup mContainerView; - private WebContents mWebContents; - - private AutofillRequest mRequest; - private long mNativeAutofillProvider; - private AutofillProviderUMA mAutofillUMA; - private AutofillManagerWrapper.InputUIObserver mInputUIObserver; - private long mAutofillTriggeredTimeMillis; - - public AutofillProviderImpl(Context context, ViewGroup containerView, String providerName) { - this(containerView, new AutofillManagerWrapper(context), context, providerName); - } - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public AutofillProviderImpl(ViewGroup containerView, AutofillManagerWrapper manager, - Context context, String providerName) { - mProviderName = providerName; - try (ScopedSysTraceEvent e = - ScopedSysTraceEvent.scoped("AutofillProviderImpl.constructor")) { - assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - mAutofillManager = manager; - mContainerView = containerView; - mAutofillUMA = new AutofillProviderUMA(context); - mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() { - @Override - public void onInputUIShown() { - // Not need to report suggestion window displayed if there is no live autofill - // session. - if (mRequest == null) return; - mAutofillUMA.onSuggestionDisplayed( - System.currentTimeMillis() - mAutofillTriggeredTimeMillis); - } - }; - mAutofillManager.addInputUIObserver(mInputUIObserver); - } - } - - @Override - public void onContainerViewChanged(ViewGroup containerView) { - mContainerView = containerView; - } - - @Override - public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { - // This method could be called for the session started by the native - // control outside of the scope of autofill, e.g. the URL bar, in this case, we simply - // return. - if (mRequest == null) return; - - Bundle bundle = structure.getExtras(); - if (bundle != null) { - bundle.putCharSequence("VIRTUAL_STRUCTURE_PROVIDER_NAME", mProviderName); - bundle.putCharSequence( - "VIRTUAL_STRUCTURE_PROVIDER_VERSION", VersionConstants.PRODUCT_VERSION); - } - mRequest.fillViewStructure(structure); - if (AutofillManagerWrapper.isLoggable()) { - AutofillManagerWrapper.log( - "onProvideAutoFillVirtualStructure fields:" + structure.getChildCount()); - } - mAutofillUMA.onVirtualStructureProvided(); - } - - @Override - public void autofill(final SparseArray values) { - if (mNativeAutofillProvider != 0 && mRequest != null && mRequest.autofill((values))) { - autofill(mNativeAutofillProvider, mRequest.mFormData); - if (AutofillManagerWrapper.isLoggable()) { - AutofillManagerWrapper.log("autofill values:" + values.size()); - } - mAutofillUMA.onAutofill(); - } - } - - @Override - public boolean shouldQueryAutofillSuggestion() { - return mRequest != null && mRequest.getFocusField() != null - && !mAutofillManager.isAutofillInputUIShowing(); - } - - @Override - public void queryAutofillSuggestion() { - if (shouldQueryAutofillSuggestion()) { - FocusField focusField = mRequest.getFocusField(); - mAutofillManager.requestAutofill(mContainerView, - mRequest.getVirtualId(focusField.fieldIndex), focusField.absBound); - } - } - - @Override - public void startAutofillSession( - FormData formData, int focus, float x, float y, float width, float height) { - // Check focusField inside short value? - // Autofill Manager might have session that wasn't started by AutofillProviderImpl, - // we just always cancel existing session here. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - mAutofillManager.cancel(); - } - mAutofillManager.notifyNewSessionStarted(); - Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); - if (mRequest != null) notifyViewExitBeforeDestoryRequest(); - transformFormFieldToContainViewCoordinates(formData); - mRequest = new AutofillRequest(formData, new FocusField((short) focus, absBound)); - int virtualId = mRequest.getVirtualId((short) focus); - mAutofillManager.notifyVirtualViewEntered(mContainerView, virtualId, absBound); - mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled()); - mAutofillTriggeredTimeMillis = System.currentTimeMillis(); - } - - @Override - public void onFormFieldDidChange(int index, float x, float y, float width, float height) { - // Check index inside short value? - if (mRequest == null) return; - - short sIndex = (short) index; - FocusField focusField = mRequest.getFocusField(); - if (focusField == null || sIndex != focusField.fieldIndex) { - onFocusChangedImpl(true, index, x, y, width, height, true /*causedByValueChange*/); - } else { - // Currently there is no api to notify both value and position - // change, before the API is available, we still need to call - // notifyVirtualViewEntered() to tell current coordinates because - // the position could be changed. - int virtualId = mRequest.getVirtualId(sIndex); - Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); - if (!focusField.absBound.equals(absBound)) { - mAutofillManager.notifyVirtualViewExited(mContainerView, virtualId); - mAutofillManager.notifyVirtualViewEntered(mContainerView, virtualId, absBound); - // Update focus field position. - mRequest.setFocusField(new FocusField(focusField.fieldIndex, absBound)); - } - } - notifyVirtualValueChanged(index); - mAutofillUMA.onUserChangeFieldValue(mRequest.getField(sIndex).hasPreviouslyAutofilled()); - } - - @Override - public void onTextFieldDidScroll(int index, float x, float y, float width, float height) { - // crbug.com/730764 - from P and above, Android framework listens to the onScrollChanged() - // and repositions the autofill UI automatically. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) return; - if (mRequest == null) return; - - short sIndex = (short) index; - FocusField focusField = mRequest.getFocusField(); - if (focusField == null || sIndex != focusField.fieldIndex) return; - - int virtualId = mRequest.getVirtualId(sIndex); - Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); - // Notify the new position to the Android framework. Note that we do not call - // notifyVirtualViewExited() here intentionally to avoid flickering. - mAutofillManager.notifyVirtualViewEntered(mContainerView, virtualId, absBound); - - // Update focus field position. - mRequest.setFocusField(new FocusField(focusField.fieldIndex, absBound)); - } - - private void notifyVirtualValueChanged(int index) { - AutofillValue autofillValue = mRequest.getFieldNewValue(index); - if (autofillValue == null) return; - mAutofillManager.notifyVirtualValueChanged( - mContainerView, mRequest.getVirtualId((short) index), autofillValue); - } - - @Override - public void onFormSubmitted(int submissionSource) { - // The changes could be missing, like those made by Javascript, we'd better to notify - // AutofillManager current values. also see crbug.com/353001 and crbug.com/732856. - notifyFormValues(); - mAutofillManager.commit(submissionSource); - mRequest = null; - mAutofillUMA.onFormSubmitted(submissionSource); - } - - @Override - public void onFocusChanged( - boolean focusOnForm, int focusField, float x, float y, float width, float height) { - onFocusChangedImpl( - focusOnForm, focusField, x, y, width, height, false /*causedByValueChange*/); - } - - private void notifyViewExitBeforeDestoryRequest() { - if (mRequest == null) return; - FocusField focusField = mRequest.getFocusField(); - if (focusField == null) return; - mAutofillManager.notifyVirtualViewExited( - mContainerView, mRequest.getVirtualId(focusField.fieldIndex)); - mRequest.setFocusField(null); - } - - private void onFocusChangedImpl(boolean focusOnForm, int focusField, float x, float y, - float width, float height, boolean causedByValueChange) { - // Check focusField inside short value? - // FocusNoLongerOnForm is called after form submitted. - if (mRequest == null) return; - FocusField prev = mRequest.getFocusField(); - if (focusOnForm) { - Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); - if (prev != null && prev.fieldIndex == focusField && absBound.equals(prev.absBound)) { - return; - } - - // Notify focus changed. - if (prev != null) { - mAutofillManager.notifyVirtualViewExited( - mContainerView, mRequest.getVirtualId(prev.fieldIndex)); - } - - mAutofillManager.notifyVirtualViewEntered( - mContainerView, mRequest.getVirtualId((short) focusField), absBound); - - if (!causedByValueChange) { - // The focus field value might not sync with platform's - // AutofillManager, just notify it value changed. - notifyVirtualValueChanged(focusField); - mAutofillTriggeredTimeMillis = System.currentTimeMillis(); - } - mRequest.setFocusField(new FocusField((short) focusField, absBound)); - } else { - if (prev == null) return; - // Notify focus changed. - mAutofillManager.notifyVirtualViewExited( - mContainerView, mRequest.getVirtualId(prev.fieldIndex)); - mRequest.setFocusField(null); - } - } - - @Override - protected void reset() { - // We don't need to reset anything here, it should be safe to cancel - // current autofill session when new one starts in - // startAutofillSession(). - } - - @Override - protected void setNativeAutofillProvider(long nativeAutofillProvider) { - if (nativeAutofillProvider == mNativeAutofillProvider) return; - // Setting the mNativeAutofillProvider to 0 may occur as a - // result of WebView.destroy, or because a WebView has been - // gc'ed. In the former case we can go ahead and clean up the - // frameworks autofill manager, but in the latter case the - // binder connection has already been dropped in a framework - // finalizer, and so the methods we call will throw. It's not - // possible to know which case we're in, so just catch the exception - // in AutofillManagerWrapper.destroy(). - if (mNativeAutofillProvider != 0) mRequest = null; - mNativeAutofillProvider = nativeAutofillProvider; - if (nativeAutofillProvider == 0) mAutofillManager.destroy(); - } - - @Override - public void setWebContents(WebContents webContents) { - if (webContents == mWebContents) return; - if (mWebContents != null) mRequest = null; - mWebContents = webContents; - } - - @Override - protected void onDidFillAutofillFormData() { - notifyFormValues(); - } - - private void notifyFormValues() { - if (mRequest == null) return; - for (int i = 0; i < mRequest.getFieldCount(); ++i) notifyVirtualValueChanged(i); - } - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public Rect transformToWindowBounds(RectF rect) { - // Convert bounds to device pixel. - WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); - DisplayAndroid displayAndroid = windowAndroid.getDisplay(); - float dipScale = displayAndroid.getDipScale(); - RectF bounds = new RectF(rect); - Matrix matrix = new Matrix(); - matrix.setScale(dipScale, dipScale); - int[] location = new int[2]; - mContainerView.getLocationOnScreen(location); - matrix.postTranslate(location[0], location[1]); - matrix.mapRect(bounds); - return new Rect( - (int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom); - } - - /** - * Transform FormFieldData's bounds to ContainView's coordinates and update the bounds with the - * transformed one. - * - * @param formData the form need to be transformed. - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public void transformFormFieldToContainViewCoordinates(FormData formData) { - WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); - DisplayAndroid displayAndroid = windowAndroid.getDisplay(); - float dipScale = displayAndroid.getDipScale(); - Matrix matrix = new Matrix(); - matrix.setScale(dipScale, dipScale); - matrix.postTranslate(mContainerView.getScrollX(), mContainerView.getScrollY()); - - for (FormFieldData field : formData.mFields) { - RectF bounds = new RectF(); - matrix.mapRect(bounds, field.getBounds()); - field.setBoundsInContainerViewCoordinates(bounds); - } - } -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderUMA.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderUMA.java deleted file mode 100644 index 394cd849c95..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProviderUMA.java +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.content.Context; - -import org.chromium.autofill.mojom.SubmissionSource; -import org.chromium.base.ContextUtils; -import org.chromium.base.metrics.RecordHistogram; - -import java.util.concurrent.TimeUnit; - -/** - * The class for AutofillProvider-related UMA. Note that most of the concrete histogram - * names include "WebView"; when this class was originally developed it was WebView-specific, - * and when generalizing it we did not change these names to maintain continuity when - * analyzing the histograms. - */ -public class AutofillProviderUMA { - // Records whether the Autofill service is enabled or not. - public static final String UMA_AUTOFILL_ENABLED = "Autofill.WebView.Enabled"; - - // Records whether the Autofill provider is created by activity context or not. - public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT = - "Autofill.WebView.CreatedByActivityContext"; - - // Records what happened in an autofill session. - public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession"; - // The possible value of UMA_AUTOFILL_AUTOFILL_SESSION. - public static final int SESSION_UNKNOWN = 0; - public static final int NO_CALLBACK_FORM_FRAMEWORK = 1; - public static final int NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 2; - public static final int NO_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 3; - public static final int NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 4; - public static final int NO_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 5; - public static final int USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 6; - public static final int USER_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 7; - public static final int USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 8; - public static final int USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 9; - public static final int USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 10; - public static final int USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 11; - public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 12; - public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13; - public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14; - - // Records whether user changed autofilled field if user ever changed the form. The action isn't - // recorded if user didn't change form at all. - public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD = - "Autofill.WebView.UserChangedAutofilledField"; - - public static final String UMA_AUTOFILL_SUBMISSION_SOURCE = "Autofill.WebView.SubmissionSource"; - // The possible value of UMA_AUTOFILL_SUBMISSION_SOURCE. - public static final int SAME_DOCUMENT_NAVIGATION = 0; - public static final int XHR_SUCCEEDED = 1; - public static final int FRAME_DETACHED = 2; - public static final int DOM_MUTATION_AFTER_XHR = 3; - public static final int PROBABLY_FORM_SUBMITTED = 4; - public static final int FORM_SUBMISSION = 5; - public static final int SUBMISSION_SOURCE_HISTOGRAM_COUNT = 6; - - // The million seconds from user touched the field to the autofill session starting. - public static final String UMA_AUTOFILL_TRIGGERING_TIME = "Autofill.WebView.TriggeringTime"; - - // The million seconds from the autofill session starting to the suggestion being displayed. - public static final String UMA_AUTOFILL_SUGGESTION_TIME = "Autofill.WebView.SuggestionTime"; - - // The expected time range of time is from 10ms to 2 seconds, and 50 buckets is sufficient. - private static final long MIN_TIME_MILLIS = 10; - private static final long MAX_TIME_MILLIS = TimeUnit.SECONDS.toMillis(2); - private static final int NUM_OF_BUCKETS = 50; - - private static void recordTimesHistogram(String name, long durationMillis) { - RecordHistogram.recordCustomTimesHistogram( - name, durationMillis, MIN_TIME_MILLIS, MAX_TIME_MILLIS, NUM_OF_BUCKETS); - } - - private static class SessionRecorder { - public static final int EVENT_VIRTUAL_STRUCTURE_PROVIDED = 0x1 << 0; - public static final int EVENT_SUGGESTION_DISPLAYED = 0x1 << 1; - public static final int EVENT_FORM_AUTOFILLED = 0x1 << 2; - public static final int EVENT_USER_CHANGED_FIELD_VALUE = 0x1 << 3; - public static final int EVENT_FORM_SUBMITTED = 0x1 << 4; - public static final int EVENT_USER_CHANGED_AUTOFILLED_FIELD = 0x1 << 5; - - private Long mSuggestionTimeMillis; - - public void record(int event) { - // Not record any event until we get EVENT_VIRTUAL_STRUCTURE_PROVIDED which makes the - // following events meaningful. - if (event != EVENT_VIRTUAL_STRUCTURE_PROVIDED && mState == 0) return; - if (EVENT_USER_CHANGED_FIELD_VALUE == event && mUserChangedAutofilledField == null) { - mUserChangedAutofilledField = Boolean.valueOf(false); - } else if (EVENT_USER_CHANGED_AUTOFILLED_FIELD == event) { - if (mUserChangedAutofilledField == null) { - mUserChangedAutofilledField = Boolean.valueOf(true); - } - mUserChangedAutofilledField = true; - event = EVENT_USER_CHANGED_FIELD_VALUE; - } - mState |= event; - } - - public void setSuggestionTimeMillis(long suggestionTimeMillis) { - // Only record first suggestion. - if (mSuggestionTimeMillis == null) { - mSuggestionTimeMillis = Long.valueOf(suggestionTimeMillis); - } - } - - public void recordHistogram() { - RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_AUTOFILL_SESSION, - toUMAAutofillSessionValue(), AUTOFILL_SESSION_HISTOGRAM_COUNT); - // Only record if user ever changed form. - if (mUserChangedAutofilledField != null) { - RecordHistogram.recordBooleanHistogram( - UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, mUserChangedAutofilledField); - } - if (mSuggestionTimeMillis != null) { - recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis); - } - } - - private int toUMAAutofillSessionValue() { - if (mState == 0) { - return NO_CALLBACK_FORM_FRAMEWORK; - } else if (mState == EVENT_VIRTUAL_STRUCTURE_PROVIDED) { - return NO_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE)) { - return NO_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; - } else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_FORM_SUBMITTED)) { - return NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE - | EVENT_FORM_SUBMITTED)) { - return NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_FORM_AUTOFILLED)) { - return USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_FORM_AUTOFILLED | EVENT_FORM_SUBMITTED)) { - return USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE - | EVENT_FORM_SUBMITTED)) { - return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE)) { - return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; - } else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED)) { - return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_FORM_SUBMITTED)) { - return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_USER_CHANGED_FIELD_VALUE | EVENT_FORM_SUBMITTED)) { - return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; - } else if (mState - == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED - | EVENT_USER_CHANGED_FIELD_VALUE)) { - return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; - } else { - return SESSION_UNKNOWN; - } - } - - private int mState; - private Boolean mUserChangedAutofilledField; - } - - private SessionRecorder mRecorder; - private Boolean mAutofillDisabled; - - public AutofillProviderUMA(Context context) { - RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT, - ContextUtils.activityFromContext(context) != null); - } - - public void onFormSubmitted(int submissionSource) { - if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED); - recordSession(); - // We record this no matter autofill service is disabled or not. - RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE, - toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT); - } - - public void onSessionStarted(boolean autofillDisabled) { - // Record autofill status once per instance and only if user triggers the autofill. - if (mAutofillDisabled == null || mAutofillDisabled.booleanValue() != autofillDisabled) { - RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_ENABLED, !autofillDisabled); - mAutofillDisabled = Boolean.valueOf(autofillDisabled); - } - - if (mRecorder != null) recordSession(); - mRecorder = new SessionRecorder(); - } - - public void onVirtualStructureProvided() { - if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_VIRTUAL_STRUCTURE_PROVIDED); - } - - public void onSuggestionDisplayed(long suggestionTimeMillis) { - if (mRecorder != null) { - mRecorder.record(SessionRecorder.EVENT_SUGGESTION_DISPLAYED); - mRecorder.setSuggestionTimeMillis(suggestionTimeMillis); - } - } - - public void onAutofill() { - if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED); - } - - public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) { - if (mRecorder == null) return; - if (isPreviouslyAutofilled) { - mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_AUTOFILLED_FIELD); - } else { - mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_FIELD_VALUE); - } - } - - private void recordSession() { - if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) { - mRecorder.recordHistogram(); - } - mRecorder = null; - } - - private int toUMASubmissionSource(int source) { - switch (source) { - case SubmissionSource.SAME_DOCUMENT_NAVIGATION: - return SAME_DOCUMENT_NAVIGATION; - case SubmissionSource.XHR_SUCCEEDED: - return XHR_SUCCEEDED; - case SubmissionSource.FRAME_DETACHED: - return FRAME_DETACHED; - case SubmissionSource.DOM_MUTATION_AFTER_XHR: - return DOM_MUTATION_AFTER_XHR; - case SubmissionSource.PROBABLY_FORM_SUBMITTED: - return PROBABLY_FORM_SUBMITTED; - case SubmissionSource.FORM_SUBMISSION: - return FORM_SUBMISSION; - default: - return SUBMISSION_SOURCE_HISTOGRAM_COUNT; - } - } -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java deleted file mode 100644 index 8069aa887b6..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import androidx.annotation.VisibleForTesting; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; - -import java.util.ArrayList; - -/** - * The wrap class of native autofill::FormDataAndroid. - */ -@JNINamespace("autofill") -public class FormData { - public final String mName; - public final String mHost; - public final ArrayList mFields; - - @CalledByNative - private static FormData createFormData( - long nativeObj, String name, String origin, int fieldCount) { - return new FormData(nativeObj, name, origin, fieldCount); - } - - private static ArrayList popupFormFields(long nativeObj, int fieldCount) { - FormFieldData formFieldData = FormDataJni.get().getNextFormFieldData(nativeObj); - ArrayList fields = new ArrayList(fieldCount); - while (formFieldData != null) { - fields.add(formFieldData); - formFieldData = FormDataJni.get().getNextFormFieldData(nativeObj); - } - assert fields.size() == fieldCount; - return fields; - } - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public FormData(String name, String host, ArrayList fields) { - mName = name; - mHost = host; - mFields = fields; - } - - private FormData(long nativeObj, String name, String host, int fieldCount) { - this(name, host, popupFormFields(nativeObj, fieldCount)); - } - - @NativeMethods - interface Natives { - FormFieldData getNextFormFieldData(long nativeFormDataAndroid); - } -} diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java deleted file mode 100644 index 964c73206c7..00000000000 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import android.graphics.RectF; - -import androidx.annotation.IntDef; -import androidx.annotation.VisibleForTesting; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The wrap class of native autofill::FormFieldDataAndroid. - */ -@JNINamespace("autofill") -public class FormFieldData { - /** - * Define the control types supported by android.view.autofill.AutofillValue. - */ - @IntDef({ControlType.TEXT, ControlType.TOGGLE, ControlType.LIST}) - @Retention(RetentionPolicy.SOURCE) - public @interface ControlType { - int TEXT = 0; - int TOGGLE = 1; - int LIST = 2; - } - - public final String mLabel; - public final String mName; - public final String mAutocompleteAttr; - public final boolean mShouldAutocomplete; - public final String mPlaceholder; - public final String mType; - public final String mId; - public final String[] mOptionValues; - public final String[] mOptionContents; - public final @ControlType int mControlType; - public final int mMaxLength; - public final String mHeuristicType; - // The bounds in the viewport's coordinates - private final RectF mBounds; - // The bounds in the container view's coordinates. - private RectF mBoundsInContainerViewCoordinates; - - private boolean mIsChecked; - private String mValue; - // Indicates whether mValue is autofilled. - private boolean mAutofilled; - // Indicates whether this fields was autofilled, but changed by user. - private boolean mPreviouslyAutofilled; - - private FormFieldData(String name, String label, String value, String autocompleteAttr, - boolean shouldAutocomplete, String placeholder, String type, String id, - String[] optionValues, String[] optionContents, boolean isCheckField, boolean isChecked, - int maxLength, String heuristicType, float left, float top, float right, float bottom) { - mName = name; - mLabel = label; - mValue = value; - mAutocompleteAttr = autocompleteAttr; - mShouldAutocomplete = shouldAutocomplete; - mPlaceholder = placeholder; - mType = type; - mId = id; - mOptionValues = optionValues; - mOptionContents = optionContents; - mIsChecked = isChecked; - if (mOptionValues != null && mOptionValues.length != 0) { - mControlType = ControlType.LIST; - } else if (isCheckField) { - mControlType = ControlType.TOGGLE; - } else { - mControlType = ControlType.TEXT; - } - mMaxLength = maxLength; - mHeuristicType = heuristicType; - mBounds = new RectF(left, top, right, bottom); - } - - public @ControlType int getControlType() { - return mControlType; - } - - public RectF getBounds() { - return mBounds; - } - - public void setBoundsInContainerViewCoordinates(RectF bounds) { - mBoundsInContainerViewCoordinates = bounds; - } - - public RectF getBoundsInContainerViewCoordinates() { - return mBoundsInContainerViewCoordinates; - } - - /** - * @return value of field. - */ - @CalledByNative - public String getValue() { - return mValue; - } - - public void setAutofillValue(String value) { - mValue = value; - updateAutofillState(true); - } - - public void setChecked(boolean checked) { - mIsChecked = checked; - updateAutofillState(true); - } - - @CalledByNative - private void updateValue(String value) { - mValue = value; - updateAutofillState(false); - } - - @CalledByNative - public boolean isChecked() { - return mIsChecked; - } - - public boolean hasPreviouslyAutofilled() { - return mPreviouslyAutofilled; - } - - private void updateAutofillState(boolean autofilled) { - if (mAutofilled && !autofilled) mPreviouslyAutofilled = true; - mAutofilled = autofilled; - } - - @CalledByNative - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public static FormFieldData createFormFieldData(String name, String label, String value, - String autocompleteAttr, boolean shouldAutocomplete, String placeholder, String type, - String id, String[] optionValues, String[] optionContents, boolean isCheckField, - boolean isChecked, int maxLength, String heuristicType, float left, float top, - float right, float bottom) { - return new FormFieldData(name, label, value, autocompleteAttr, shouldAutocomplete, - placeholder, type, id, optionValues, optionContents, isCheckField, isChecked, - maxLength, heuristicType, left, top, right, bottom); - } -} diff --git a/chromium/components/autofill/android/junit/BUILD.gn b/chromium/components/autofill/android/junit/BUILD.gn deleted file mode 100644 index 0f58b8ac6fa..00000000000 --- a/chromium/components/autofill/android/junit/BUILD.gn +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2020 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/android/config.gni") -import("//build/config/android/rules.gni") - -java_library("components_autofill_junit_tests") { - # Platform checks are broken for Robolectric. See https://crbug.com/1071638. - bypass_platform_checks = true - testonly = true - sources = - [ "src/org/chromium/components/autofill/AutofillProviderImplTest.java" ] - deps = [ - "//base:base_java_test_support", - "//base:base_junit_test_support", - "//components/autofill/android:provider_java", - "//content/public/android:content_java", - "//ui/android:ui_java", - ] -} diff --git a/chromium/components/autofill/android/junit/OWNERS b/chromium/components/autofill/android/junit/OWNERS deleted file mode 100644 index 44a22b15980..00000000000 --- a/chromium/components/autofill/android/junit/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -michaelbai@chromium.org - diff --git a/chromium/components/autofill/android/junit/src/org/chromium/components/autofill/AutofillProviderImplTest.java b/chromium/components/autofill/android/junit/src/org/chromium/components/autofill/AutofillProviderImplTest.java deleted file mode 100644 index d8c8c2bfd3d..00000000000 --- a/chromium/components/autofill/android/junit/src/org/chromium/components/autofill/AutofillProviderImplTest.java +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.autofill; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.graphics.Rect; -import android.graphics.RectF; -import android.view.ViewGroup; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.content_public.browser.WebContents; -import org.chromium.ui.base.WindowAndroid; -import org.chromium.ui.display.DisplayAndroid; - -import java.util.ArrayList; - -/** - * The unit tests for AutofillProviderImpl. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class AutofillProviderImplTest { - private static final float EXPECTED_DIP_SCALE = 2; - private static final int SCROLL_X = 15; - private static final int SCROLL_Y = 155; - private static final int LOCATION_X = 25; - private static final int LOCATION_Y = 255; - - private Context mContext; - private WindowAndroid mWindowAndroid; - private WebContents mWebContents; - private ViewGroup mContainerView; - private AutofillProviderImpl mAutofillProvider; - private DisplayAndroid mDisplayAndroid; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = Mockito.mock(Context.class); - mWindowAndroid = Mockito.mock(WindowAndroid.class); - mDisplayAndroid = Mockito.mock(DisplayAndroid.class); - mWebContents = Mockito.mock(WebContents.class); - mContainerView = Mockito.mock(ViewGroup.class); - mAutofillProvider = - new AutofillProviderImpl(mContext, mContainerView, "AutofillProviderImplTest"); - mAutofillProvider.setWebContents(mWebContents); - - when(mWebContents.getTopLevelNativeWindow()).thenReturn(mWindowAndroid); - when(mWindowAndroid.getDisplay()).thenReturn(mDisplayAndroid); - when(mDisplayAndroid.getDipScale()).thenReturn(EXPECTED_DIP_SCALE); - when(mContainerView.getScrollX()).thenReturn(SCROLL_X); - when(mContainerView.getScrollY()).thenReturn(SCROLL_Y); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - int[] location = (int[]) args[0]; - location[0] = LOCATION_X; - location[1] = LOCATION_Y; - return null; - } - }) - .when(mContainerView) - .getLocationOnScreen(ArgumentMatchers.any()); - } - - @Test - public void testTransformFormFieldToContainViewCoordinates() { - ArrayList fields = new ArrayList(1); - fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, - null, null, null, false, false, 0, null, 10 /* left */, 20 /* top */, - 300 /* right */, 60 /*bottom*/)); - fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, - null, null, null, false, false, 0, null, 20 /* left */, 100 /* top */, - 400 /* right */, 200 /*bottom*/)); - FormData formData = new FormData(null, null, fields); - mAutofillProvider.transformFormFieldToContainViewCoordinates(formData); - RectF result = formData.mFields.get(0).getBoundsInContainerViewCoordinates(); - assertEquals(10 * EXPECTED_DIP_SCALE + SCROLL_X, result.left, 0); - assertEquals(20 * EXPECTED_DIP_SCALE + SCROLL_Y, result.top, 0); - assertEquals(300 * EXPECTED_DIP_SCALE + SCROLL_X, result.right, 0); - assertEquals(60 * EXPECTED_DIP_SCALE + SCROLL_Y, result.bottom, 0); - - result = formData.mFields.get(1).getBoundsInContainerViewCoordinates(); - assertEquals(20 * EXPECTED_DIP_SCALE + SCROLL_X, result.left, 0); - assertEquals(100 * EXPECTED_DIP_SCALE + SCROLL_Y, result.top, 0); - assertEquals(400 * EXPECTED_DIP_SCALE + SCROLL_X, result.right, 0); - assertEquals(200 * EXPECTED_DIP_SCALE + SCROLL_Y, result.bottom, 0); - } - - @Test - public void testTransformToWindowBounds() { - RectF source = new RectF(10, 20, 300, 400); - Rect result = mAutofillProvider.transformToWindowBounds(source); - assertEquals(10 * EXPECTED_DIP_SCALE + LOCATION_X, result.left, 0); - assertEquals(20 * EXPECTED_DIP_SCALE + LOCATION_Y, result.top, 0); - assertEquals(300 * EXPECTED_DIP_SCALE + LOCATION_X, result.right, 0); - assertEquals(400 * EXPECTED_DIP_SCALE + LOCATION_Y, result.bottom, 0); - } -} diff --git a/chromium/components/autofill/android/provider/BUILD.gn b/chromium/components/autofill/android/provider/BUILD.gn new file mode 100644 index 00000000000..263b88afd5e --- /dev/null +++ b/chromium/components/autofill/android/provider/BUILD.gn @@ -0,0 +1,53 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") +import("//build/config/locales.gni") + +android_library("java") { + deps = [ + "//base:base_java", + "//base:jni_java", + "//components/autofill/android:autofill_java", + "//components/autofill/core/common/mojom:mojo_types_java", + "//components/version_info/android:version_constants_java", + "//content/public/android:content_java", + "//third_party/android_deps:androidx_annotation_annotation_java", + "//ui/android:ui_java", + ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] + sources = [ + "java/src/org/chromium/components/autofill/AutofillActionModeCallback.java", + "java/src/org/chromium/components/autofill/AutofillManagerWrapper.java", + "java/src/org/chromium/components/autofill/AutofillProvider.java", + "java/src/org/chromium/components/autofill/AutofillProviderUMA.java", + "java/src/org/chromium/components/autofill/FormData.java", + "java/src/org/chromium/components/autofill/FormFieldData.java", + ] +} + +generate_jni("jni_headers") { + sources = [ + "java/src/org/chromium/components/autofill/AutofillProvider.java", + "java/src/org/chromium/components/autofill/FormData.java", + "java/src/org/chromium/components/autofill/FormFieldData.java", + ] +} + +static_library("provider") { + sources = [ + "autofill_provider_android.cc", + "autofill_provider_android.h", + "form_data_android.cc", + "form_data_android.h", + "form_field_data_android.cc", + "form_field_data_android.h", + ] + deps = [ + ":jni_headers", + "//components/autofill/core/browser:browser", + "//content/public/browser", + "//ui/android", + ] +} diff --git a/chromium/components/autofill/android/provider/OWNERS b/chromium/components/autofill/android/provider/OWNERS new file mode 100644 index 00000000000..44a22b15980 --- /dev/null +++ b/chromium/components/autofill/android/provider/OWNERS @@ -0,0 +1,2 @@ +michaelbai@chromium.org + diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.cc b/chromium/components/autofill/android/provider/autofill_provider_android.cc new file mode 100644 index 00000000000..408ff96c130 --- /dev/null +++ b/chromium/components/autofill/android/provider/autofill_provider_android.cc @@ -0,0 +1,405 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/android/provider/autofill_provider_android.h" + +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "components/autofill/android/provider/form_data_android.h" +#include "components/autofill/android/provider/jni_headers/AutofillProvider_jni.h" +#include "components/autofill/core/browser/autofill_driver.h" +#include "components/autofill/core/browser/autofill_handler_proxy.h" +#include "components/autofill/core/common/autofill_constants.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "ui/android/window_android.h" +#include "ui/gfx/geometry/rect_f.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertUTF16ToJavaString; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfStrings; +using content::BrowserThread; +using content::WebContents; +using gfx::RectF; + +namespace autofill { + +using mojom::SubmissionSource; + +AutofillProviderAndroid::AutofillProviderAndroid( + const JavaRef& jcaller, + content::WebContents* web_contents) + : id_(kNoQueryId), web_contents_(web_contents), check_submission_(false) { + OnJavaAutofillProviderChanged(AttachCurrentThread(), jcaller); +} + +void AutofillProviderAndroid::OnJavaAutofillProviderChanged( + JNIEnv* env, + const JavaRef& jcaller) { + // If the current Java object isn't null (e.g., because it hasn't been + // garbage-collected yet), clear its reference to this object. + ScopedJavaLocalRef obj = java_ref_.get(env); + if (!obj.is_null()) { + Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0); + } + + java_ref_ = JavaObjectWeakGlobalRef(env, jcaller); + + // If the new Java object isn't null, set its native object to |this|. + obj = java_ref_.get(env); + if (!obj.is_null()) { + Java_AutofillProvider_setNativeAutofillProvider( + env, obj, reinterpret_cast(this)); + } +} + +AutofillProviderAndroid::~AutofillProviderAndroid() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + // Remove the reference to this object on the Java side. + Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0); +} + +void AutofillProviderAndroid::OnQueryFormFieldAutofill( + AutofillHandlerProxy* handler, + int32_t id, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + bool /*unused_autoselect_first_suggestion*/) { + // The id isn't passed to Java side because Android API guarantees the + // response is always for current session, so we just use the current id + // in response, see OnAutofillAvailable. + DCHECK_CURRENTLY_ON(BrowserThread::UI); + id_ = id; + + // Focus or field value change will also trigger the query, so it should be + // ignored if the form is same. + if (ShouldStartNewSession(handler, form)) + StartNewSession(handler, form, field, bounding_box); + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + if (!field.datalist_values.empty()) { + ScopedJavaLocalRef jdatalist_values = + ToJavaArrayOfStrings(env, field.datalist_values); + ScopedJavaLocalRef jdatalist_labels = + ToJavaArrayOfStrings(env, field.datalist_labels); + Java_AutofillProvider_showDatalistPopup( + env, obj, jdatalist_values, jdatalist_labels, + field.text_direction == base::i18n::RIGHT_TO_LEFT); + } +} + +bool AutofillProviderAndroid::ShouldStartNewSession( + AutofillHandlerProxy* handler, + const FormData& form) { + // Only start a new session when form or handler is changed, the change of + // handler indicates query from other frame and a new session is needed. + return !IsCurrentlyLinkedForm(form) || !IsCurrentlyLinkedHandler(handler); +} + +void AutofillProviderAndroid::StartNewSession(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + form_ = std::make_unique( + form, base::BindRepeating( + &AutofillDriver::TransformBoundingBoxToViewportCoordinates, + base::Unretained(handler->driver()))); + + size_t index; + if (!form_->GetFieldIndex(field, &index)) { + form_.reset(); + return; + } + + FormStructure* form_structure = nullptr; + AutofillField* autofill_field = nullptr; + if (!handler->GetCachedFormAndField(form, field, &form_structure, + &autofill_field)) { + form_structure = nullptr; + } + gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); + + ScopedJavaLocalRef form_obj = form_->GetJavaPeer(form_structure); + handler_ = handler->GetWeakPtr(); + Java_AutofillProvider_startAutofillSession( + env, obj, form_obj, index, transformed_bounding.x(), + transformed_bounding.y(), transformed_bounding.width(), + transformed_bounding.height()); +} + +void AutofillProviderAndroid::OnAutofillAvailable(JNIEnv* env, + jobject jcaller, + jobject formData) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (handler_) { + const FormData& form = form_->GetAutofillValues(); + SendFormDataToRenderer(handler_.get(), id_, form); + } +} + +void AutofillProviderAndroid::OnAcceptDataListSuggestion(JNIEnv* env, + jobject jcaller, + jstring value) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (auto* handler = handler_.get()) { + RendererShouldAcceptDataListSuggestion( + handler, ConvertJavaStringToUTF16(env, value)); + } +} + +void AutofillProviderAndroid::SetAnchorViewRect(JNIEnv* env, + jobject jcaller, + jobject anchor_view, + jfloat x, + jfloat y, + jfloat width, + jfloat height) { + ui::ViewAndroid* view_android = web_contents_->GetNativeView(); + if (!view_android) + return; + + view_android->SetAnchorRect(ScopedJavaLocalRef(env, anchor_view), + gfx::RectF(x, y, width, height)); +} + +void AutofillProviderAndroid::OnTextFieldDidChange( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) { + FireFormFieldDidChanged(handler, form, field, bounding_box); +} + +void AutofillProviderAndroid::OnTextFieldDidScroll( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + size_t index; + if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || + !form_->GetSimilarFieldIndex(field, &index)) + return; + + form_->OnFormFieldDidChange(index, field.value); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); + Java_AutofillProvider_onTextFieldDidScroll( + env, obj, index, transformed_bounding.x(), transformed_bounding.y(), + transformed_bounding.width(), transformed_bounding.height()); +} + +void AutofillProviderAndroid::OnSelectControlDidChange( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + if (ShouldStartNewSession(handler, form)) + StartNewSession(handler, form, field, bounding_box); + FireFormFieldDidChanged(handler, form, field, bounding_box); +} + +void AutofillProviderAndroid::FireSuccessfulSubmission( + SubmissionSource source) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_onFormSubmitted(env, obj, (int)source); + Reset(); +} + +void AutofillProviderAndroid::OnFormSubmitted(AutofillHandlerProxy* handler, + const FormData& form, + bool known_success, + SubmissionSource source) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form)) + return; + + if (known_success || source == SubmissionSource::FORM_SUBMISSION) { + FireSuccessfulSubmission(source); + return; + } + + check_submission_ = true; + pending_submission_source_ = source; +} + +void AutofillProviderAndroid::OnFocusNoLongerOnForm( + AutofillHandlerProxy* handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!IsCurrentlyLinkedHandler(handler)) + return; + + OnFocusChanged(false, 0, RectF()); +} + +void AutofillProviderAndroid::OnFocusOnFormField( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + size_t index; + if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || + !form_->GetSimilarFieldIndex(field, &index)) + return; + + // Because this will trigger a suggestion query, set request id to browser + // initiated request. + id_ = kNoQueryId; + + OnFocusChanged(true, index, ToClientAreaBound(bounding_box)); +} + +void AutofillProviderAndroid::OnFocusChanged(bool focus_on_form, + size_t index, + const gfx::RectF& bounding_box) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_onFocusChanged( + env, obj, focus_on_form, index, bounding_box.x(), bounding_box.y(), + bounding_box.width(), bounding_box.height()); +} + +void AutofillProviderAndroid::FireFormFieldDidChanged( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + size_t index; + if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) || + !form_->GetSimilarFieldIndex(field, &index)) + return; + + form_->OnFormFieldDidChange(index, field.value); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box); + Java_AutofillProvider_onFormFieldDidChange( + env, obj, index, transformed_bounding.x(), transformed_bounding.y(), + transformed_bounding.width(), transformed_bounding.height()); +} + +void AutofillProviderAndroid::OnDidFillAutofillFormData( + AutofillHandlerProxy* handler, + const FormData& form, + base::TimeTicks timestamp) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (handler != handler_.get() || !IsCurrentlyLinkedForm(form)) + return; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_onDidFillAutofillFormData(env, obj); +} + +void AutofillProviderAndroid::OnFormsSeen(AutofillHandlerProxy* handler, + const std::vector& forms, + const base::TimeTicks) { + handler_for_testing_ = handler->GetWeakPtr(); + if (!check_submission_) + return; + + if (handler != handler_.get()) + return; + + if (form_.get() == nullptr) + return; + + for (auto const& form : forms) { + if (form_->SimilarFormAs(form)) + return; + } + // The form_ disappeared after it was submitted, we consider the submission + // succeeded. + FireSuccessfulSubmission(pending_submission_source_); +} + +void AutofillProviderAndroid::OnHidePopup(AutofillHandlerProxy* handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (handler == handler_.get()) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_hidePopup(env, obj); + } +} + +void AutofillProviderAndroid::Reset(AutofillHandlerProxy* handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (handler == handler_.get()) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_reset(env, obj); + } +} + +bool AutofillProviderAndroid::IsCurrentlyLinkedHandler( + AutofillHandlerProxy* handler) { + return handler == handler_.get(); +} + +bool AutofillProviderAndroid::IsCurrentlyLinkedForm(const FormData& form) { + return form_ && form_->SimilarFormAs(form); +} + +gfx::RectF AutofillProviderAndroid::ToClientAreaBound( + const gfx::RectF& bounding_box) { + gfx::Rect client_area = web_contents_->GetContainerBounds(); + return bounding_box + client_area.OffsetFromOrigin(); +} + +void AutofillProviderAndroid::Reset() { + form_.reset(nullptr); + id_ = kNoQueryId; + check_submission_ = false; +} + +} // namespace autofill diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.h b/chromium/components/autofill/android/provider/autofill_provider_android.h new file mode 100644 index 00000000000..b0830e2338e --- /dev/null +++ b/chromium/components/autofill/android/provider/autofill_provider_android.h @@ -0,0 +1,127 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ANDROID_PROVIDER_AUTOFILL_PROVIDER_ANDROID_H_ +#define COMPONENTS_AUTOFILL_ANDROID_PROVIDER_AUTOFILL_PROVIDER_ANDROID_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/autofill_provider.h" + +namespace content { +class WebContents; +} + +namespace autofill { + +class FormDataAndroid; + +// Android implementation of AutofillProvider, it has one instance per +// WebContents, this class is native peer of AutofillProvider.java. +class AutofillProviderAndroid : public AutofillProvider { + public: + AutofillProviderAndroid(const base::android::JavaRef& jcaller, + content::WebContents* web_contents); + // Invoked when the Java-side AutofillProvider counterpart of this object + // has been changed (either to null or to a new object). + void OnJavaAutofillProviderChanged( + JNIEnv* env, + const base::android::JavaRef& jcaller); + + ~AutofillProviderAndroid() override; + + // AutofillProvider: + void OnQueryFormFieldAutofill( + AutofillHandlerProxy* handler, + int32_t id, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + bool /*unused_autoselect_first_suggestion*/) override; + void OnTextFieldDidChange(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) override; + void OnTextFieldDidScroll(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + void OnSelectControlDidChange(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + void OnFormSubmitted(AutofillHandlerProxy* handler, + const FormData& form, + bool known_success, + mojom::SubmissionSource source) override; + void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override; + void OnFocusOnFormField(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + void OnDidFillAutofillFormData(AutofillHandlerProxy* handler, + const FormData& form, + base::TimeTicks timestamp) override; + void OnFormsSeen(AutofillHandlerProxy* handler, + const std::vector& forms, + const base::TimeTicks timestamp) override; + void OnHidePopup(AutofillHandlerProxy* handler) override; + + void Reset(AutofillHandlerProxy* handler) override; + + // Methods called by Java. + void OnAutofillAvailable(JNIEnv* env, jobject jcaller, jobject form_data); + void OnAcceptDataListSuggestion(JNIEnv* env, jobject jcaller, jstring value); + + void SetAnchorViewRect(JNIEnv* env, + jobject jcaller, + jobject anchor_view, + jfloat x, + jfloat y, + jfloat width, + jfloat height); + + private: + void FireSuccessfulSubmission(mojom::SubmissionSource source); + void OnFocusChanged(bool focus_on_form, + size_t index, + const gfx::RectF& bounding_box); + void FireFormFieldDidChanged(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box); + + bool IsCurrentlyLinkedHandler(AutofillHandlerProxy* handler); + + bool IsCurrentlyLinkedForm(const FormData& form); + + gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box); + + bool ShouldStartNewSession(AutofillHandlerProxy* handler, + const FormData& form); + + void StartNewSession(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box); + + void Reset(); + + int32_t id_; + std::unique_ptr form_; + base::WeakPtr handler_; + JavaObjectWeakGlobalRef java_ref_; + content::WebContents* web_contents_; + bool check_submission_; + // Valid only if check_submission_ is true. + mojom::SubmissionSource pending_submission_source_; + + base::WeakPtr handler_for_testing_; + + DISALLOW_COPY_AND_ASSIGN(AutofillProviderAndroid); +}; +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_ANDROID_PROVIDER_AUTOFILL_PROVIDER_ANDROID_H_ diff --git a/chromium/components/autofill/android/provider/form_data_android.cc b/chromium/components/autofill/android/provider/form_data_android.cc new file mode 100644 index 00000000000..7f24f8967c5 --- /dev/null +++ b/chromium/components/autofill/android/provider/form_data_android.cc @@ -0,0 +1,112 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/android/provider/form_data_android.h" + +#include "base/android/jni_string.h" +#include "components/autofill/android/provider/form_field_data_android.h" +#include "components/autofill/android/provider/jni_headers/FormData_jni.h" +#include "components/autofill/core/browser/form_structure.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertUTF16ToJavaString; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaParamRef; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; + +namespace autofill { + +FormDataAndroid::FormDataAndroid(const FormData& form, + const TransformCallback& callback) + : form_(form), index_(0) { + for (FormFieldData& field : form_.fields) + field.bounds = callback.Run(field.bounds); +} + +FormDataAndroid::~FormDataAndroid() = default; + +ScopedJavaLocalRef FormDataAndroid::GetJavaPeer( + const FormStructure* form_structure) { + // |form_structure| is ephemeral and shouldn't be used outside this call + // stack. + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) { + for (size_t i = 0; i < form_.fields.size(); ++i) { + fields_.push_back(std::unique_ptr( + new FormFieldDataAndroid(&form_.fields[i]))); + } + if (form_structure) + ApplyHeuristicFieldType(*form_structure); + ScopedJavaLocalRef jname = + ConvertUTF16ToJavaString(env, form_.name); + ScopedJavaLocalRef jhost = + ConvertUTF8ToJavaString(env, form_.url.GetOrigin().spec()); + obj = Java_FormData_createFormData(env, reinterpret_cast(this), + jname, jhost, form_.fields.size()); + java_ref_ = JavaObjectWeakGlobalRef(env, obj); + } + return obj; +} + +const FormData& FormDataAndroid::GetAutofillValues() { + for (std::unique_ptr& field : fields_) + field->GetValue(); + return form_; +} + +ScopedJavaLocalRef FormDataAndroid::GetNextFormFieldData(JNIEnv* env) { + DCHECK(index_ <= fields_.size()); + if (index_ == fields_.size()) + return ScopedJavaLocalRef(); + return fields_[index_++]->GetJavaPeer(); +} + +void FormDataAndroid::OnFormFieldDidChange(size_t index, + const base::string16& value) { + form_.fields[index].value = value; + fields_[index]->OnFormFieldDidChange(value); +} + +bool FormDataAndroid::GetFieldIndex(const FormFieldData& field, size_t* index) { + for (size_t i = 0; i < form_.fields.size(); ++i) { + if (form_.fields[i].SameFieldAs(field)) { + *index = i; + return true; + } + } + return false; +} + +bool FormDataAndroid::GetSimilarFieldIndex(const FormFieldData& field, + size_t* index) { + for (size_t i = 0; i < form_.fields.size(); ++i) { + if (form_.fields[i].SimilarFieldAs(field)) { + *index = i; + return true; + } + } + return false; +} + +bool FormDataAndroid::SimilarFormAs(const FormData& form) { + return form_.SimilarFormAs(form); +} + +void FormDataAndroid::ApplyHeuristicFieldType( + const FormStructure& form_structure) { + DCHECK(form_structure.field_count() == fields_.size()); + auto form_field_data_android = fields_.begin(); + for (const auto& autofill_field : form_structure) { + DCHECK(form_field_data_android->get()->SimilarFieldAs(*autofill_field)); + form_field_data_android->get()->set_heuristic_type( + AutofillType(autofill_field->heuristic_type())); + if (++form_field_data_android == fields_.end()) + break; + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/android/provider/form_data_android.h b/chromium/components/autofill/android/provider/form_data_android.h new file mode 100644 index 00000000000..8882a788673 --- /dev/null +++ b/chromium/components/autofill/android/provider/form_data_android.h @@ -0,0 +1,73 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_DATA_ANDROID_H_ +#define COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_DATA_ANDROID_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "components/autofill/core/common/form_data.h" + +namespace autofill { + +class FormFieldDataAndroid; +class FormStructure; + +// This class is native peer of FormData.java, to make autofill::FormData +// available in Java. +class FormDataAndroid { + public: + // The callback func to transform FormFieldData's bounds to viewport's + // coordinates, it is only used in FormDataAndroid constructor and transforms + // bounds in place to avoids an extra copy of FormData. + using TransformCallback = + base::RepeatingCallback; + + FormDataAndroid(const FormData& form, const TransformCallback& callback); + virtual ~FormDataAndroid(); + + base::android::ScopedJavaLocalRef GetJavaPeer( + const FormStructure* form_structure); + + // Get autofill values from Java side and return FormData. + const FormData& GetAutofillValues(); + + base::android::ScopedJavaLocalRef GetNextFormFieldData(JNIEnv* env); + + // Get index of given field, return True and index of focus field if found. + bool GetFieldIndex(const FormFieldData& field, size_t* index); + + // Get index of given field, return True and index of focus field if + // similar field is found. This method compares less attributes than + // GetFieldIndex() does, and should be used when field could be changed + // dynamically, but the changed has no impact on autofill purpose, e.g. css + // style change, see FormFieldData::SimilarFieldAs() for details. + bool GetSimilarFieldIndex(const FormFieldData& field, size_t* index); + + // Return true if this form is similar to the given form. + bool SimilarFormAs(const FormData& form); + + // Invoked when form field which specified by |index| is charged to new + // |value|. + void OnFormFieldDidChange(size_t index, const base::string16& value); + + void ApplyHeuristicFieldType(const FormStructure& form); + + const FormData& form_for_testing() { return form_; } + + private: + // Same as the form passed in from constructor, but FormFieldData's bounds is + // transformed to viewport coordinates. + FormData form_; + std::vector> fields_; + JavaObjectWeakGlobalRef java_ref_; + // keep track of index when popping up fields to Java. + size_t index_; + + DISALLOW_COPY_AND_ASSIGN(FormDataAndroid); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_DATA_ANDROID_H_ diff --git a/chromium/components/autofill/android/provider/form_field_data_android.cc b/chromium/components/autofill/android/provider/form_field_data_android.cc new file mode 100644 index 00000000000..ee5471f5239 --- /dev/null +++ b/chromium/components/autofill/android/provider/form_field_data_android.cc @@ -0,0 +1,106 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/android/provider/form_field_data_android.h" + +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "components/autofill/android/provider/jni_headers/FormFieldData_jni.h" +#include "components/autofill/core/common/autofill_util.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertUTF16ToJavaString; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaParamRef; +using base::android::JavaRef; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfStrings; + +namespace autofill { + +FormFieldDataAndroid::FormFieldDataAndroid(FormFieldData* field) + : heuristic_type_(AutofillType(UNKNOWN_TYPE)), field_ptr_(field) {} + +ScopedJavaLocalRef FormFieldDataAndroid::GetJavaPeer() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) { + ScopedJavaLocalRef jname = + ConvertUTF16ToJavaString(env, field_ptr_->name); + ScopedJavaLocalRef jlabel = + ConvertUTF16ToJavaString(env, field_ptr_->label); + ScopedJavaLocalRef jvalue = + ConvertUTF16ToJavaString(env, field_ptr_->value); + ScopedJavaLocalRef jautocomplete_attr = + ConvertUTF8ToJavaString(env, field_ptr_->autocomplete_attribute); + ScopedJavaLocalRef jplaceholder = + ConvertUTF16ToJavaString(env, field_ptr_->placeholder); + ScopedJavaLocalRef jid = + ConvertUTF16ToJavaString(env, field_ptr_->id_attribute); + ScopedJavaLocalRef jtype = + ConvertUTF8ToJavaString(env, field_ptr_->form_control_type); + ScopedJavaLocalRef joption_values = + ToJavaArrayOfStrings(env, field_ptr_->option_values); + ScopedJavaLocalRef joption_contents = + ToJavaArrayOfStrings(env, field_ptr_->option_contents); + ScopedJavaLocalRef jheuristic_type; + if (!heuristic_type_.IsUnknown()) + jheuristic_type = + ConvertUTF8ToJavaString(env, heuristic_type_.ToString()); + ScopedJavaLocalRef jdatalist_values = + ToJavaArrayOfStrings(env, field_ptr_->datalist_values); + ScopedJavaLocalRef jdatalist_labels = + ToJavaArrayOfStrings(env, field_ptr_->datalist_labels); + + obj = Java_FormFieldData_createFormFieldData( + env, jname, jlabel, jvalue, jautocomplete_attr, + field_ptr_->should_autocomplete, jplaceholder, jtype, jid, + joption_values, joption_contents, IsCheckable(field_ptr_->check_status), + IsChecked(field_ptr_->check_status), field_ptr_->max_length, + jheuristic_type, field_ptr_->bounds.x(), field_ptr_->bounds.y(), + field_ptr_->bounds.right(), field_ptr_->bounds.bottom(), + jdatalist_values, jdatalist_labels); + java_ref_ = JavaObjectWeakGlobalRef(env, obj); + } + return obj; +} + +void FormFieldDataAndroid::GetValue() { + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + if (IsCheckable(field_ptr_->check_status)) { + bool checked = Java_FormFieldData_isChecked(env, obj); + SetCheckStatus(field_ptr_, true, checked); + } else { + ScopedJavaLocalRef jvalue = Java_FormFieldData_getValue(env, obj); + if (jvalue.is_null()) + return; + field_ptr_->value = ConvertJavaStringToUTF16(env, jvalue); + } + field_ptr_->is_autofilled = true; +} + +void FormFieldDataAndroid::OnFormFieldDidChange(const base::string16& value) { + field_ptr_->value = value; + field_ptr_->is_autofilled = false; + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_FormFieldData_updateValue(env, obj, + ConvertUTF16ToJavaString(env, value)); +} + +bool FormFieldDataAndroid::SimilarFieldAs(const FormFieldData& field) const { + return field_ptr_->SimilarFieldAs(field); +} + +} // namespace autofill diff --git a/chromium/components/autofill/android/provider/form_field_data_android.h b/chromium/components/autofill/android/provider/form_field_data_android.h new file mode 100644 index 00000000000..a12ad979bfe --- /dev/null +++ b/chromium/components/autofill/android/provider/form_field_data_android.h @@ -0,0 +1,42 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_FIELD_DATA_ANDROID_H_ +#define COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_FIELD_DATA_ANDROID_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/common/form_field_data.h" + +namespace autofill { + +// This class is native peer of FormFieldData.java, makes +// autofill::FormFieldData available in Java. +class FormFieldDataAndroid { + public: + FormFieldDataAndroid(FormFieldData* field); + virtual ~FormFieldDataAndroid() {} + + base::android::ScopedJavaLocalRef GetJavaPeer(); + void GetValue(); + void OnFormFieldDidChange(const base::string16& value); + bool SimilarFieldAs(const FormFieldData& field) const; + + void set_heuristic_type(const AutofillType& heuristic_type) { + heuristic_type_ = heuristic_type; + } + + private: + AutofillType heuristic_type_; + // Not owned. + FormFieldData* field_ptr_; + JavaObjectWeakGlobalRef java_ref_; + + DISALLOW_COPY_AND_ASSIGN(FormFieldDataAndroid); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_ANDROID_PROVIDER_FORM_FIELD_DATA_ANDROID_H_ diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java new file mode 100644 index 00000000000..6921fcab52c --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillActionModeCallback.java @@ -0,0 +1,60 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import android.content.Context; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; + +/** + * The class to implement autofill context menu. To match the Android native view behavior, the + * autofill context menu only appears when there is no text selected. + */ +public class AutofillActionModeCallback implements ActionMode.Callback { + private final Context mContext; + private final AutofillProvider mAutofillProvider; + private final int mAutofillMenuItemTitle; + private final int mAutofillMenuItem; + + public AutofillActionModeCallback(Context context, AutofillProvider autofillProvider) { + mContext = context; + mAutofillProvider = autofillProvider; + // TODO(michaelbai): Uses the resource directly after sdk roll to Android O MR1. + // crbug.com/740628 + mAutofillMenuItemTitle = + mContext.getResources().getIdentifier("autofill", "string", "android"); + mAutofillMenuItem = mContext.getResources().getIdentifier("autofill", "id", "android"); + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + return mAutofillMenuItemTitle != 0 && mAutofillMenuItem != 0; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (mAutofillMenuItemTitle != 0 && mAutofillProvider.shouldQueryAutofillSuggestion()) { + MenuItem item = menu.add( + Menu.NONE, mAutofillMenuItem, Menu.CATEGORY_SECONDARY, mAutofillMenuItemTitle); + item.setShowAsActionFlags( + MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == mAutofillMenuItem) { + mAutofillProvider.queryAutofillSuggestion(); + mode.finish(); + return true; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) {} +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java new file mode 100644 index 00000000000..0287ac38f97 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java @@ -0,0 +1,206 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Rect; +import android.os.Build; +import android.view.View; +import android.view.autofill.AutofillManager; +import android.view.autofill.AutofillValue; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.Log; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * The class to call Android's AutofillManager. + */ +@TargetApi(Build.VERSION_CODES.O) +public class AutofillManagerWrapper { + // Don't change TAG, it is used for runtime log. + // NOTE: As a result of the above, the tag below still references the name of this class from + // when it was originally developed specifically for Android WebView. + public static final String TAG = "AwAutofillManager"; + + /** + * The observer of suggestion window. + */ + public static interface InputUIObserver { void onInputUIShown(); } + + private static class AutofillInputUIMonitor extends AutofillManager.AutofillCallback { + private WeakReference mManager; + + public AutofillInputUIMonitor(AutofillManagerWrapper manager) { + mManager = new WeakReference(manager); + } + + @Override + public void onAutofillEvent(View view, int virtualId, int event) { + AutofillManagerWrapper manager = mManager.get(); + if (manager == null) return; + manager.mIsAutofillInputUIShowing = (event == EVENT_INPUT_SHOWN); + if (event == EVENT_INPUT_SHOWN) manager.notifyInputUIChange(); + } + } + + private static boolean sIsLoggable; + private AutofillManager mAutofillManager; + private boolean mIsAutofillInputUIShowing; + private AutofillInputUIMonitor mMonitor; + private boolean mDestroyed; + private boolean mDisabled; + private ArrayList> mInputUIObservers; + + public AutofillManagerWrapper(Context context) { + updateLogStat(); + if (isLoggable()) log("constructor"); + mAutofillManager = context.getSystemService(AutofillManager.class); + mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled(); + if (mDisabled) { + if (isLoggable()) log("disabled"); + return; + } + + mMonitor = new AutofillInputUIMonitor(this); + mAutofillManager.registerCallback(mMonitor); + } + + public void notifyVirtualValueChanged(View parent, int childId, AutofillValue value) { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("notifyVirtualValueChanged"); + mAutofillManager.notifyValueChanged(parent, childId, value); + } + + public void commit(int submissionSource) { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("commit source:" + submissionSource); + mAutofillManager.commit(); + } + + public void cancel() { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("cancel"); + mAutofillManager.cancel(); + } + + public void notifyVirtualViewEntered(View parent, int childId, Rect absBounds) { + // Log warning only when the autofill is triggered. + if (mDisabled) { + Log.w(TAG, "Autofill is disabled: AutofillManager isn't available in given Context."); + return; + } + if (checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("notifyVirtualViewEntered"); + mAutofillManager.notifyViewEntered(parent, childId, absBounds); + } + + public void notifyVirtualViewExited(View parent, int childId) { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("notifyVirtualViewExited"); + mAutofillManager.notifyViewExited(parent, childId); + } + + public void requestAutofill(View parent, int virtualId, Rect absBounds) { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("requestAutofill"); + mAutofillManager.requestAutofill(parent, virtualId, absBounds); + } + + public boolean isAutofillInputUIShowing() { + if (mDisabled || checkAndWarnIfDestroyed()) return false; + if (isLoggable()) log("isAutofillInputUIShowing: " + mIsAutofillInputUIShowing); + return mIsAutofillInputUIShowing; + } + + public void destroy() { + if (mDisabled || checkAndWarnIfDestroyed()) return; + if (isLoggable()) log("destroy"); + try { + // The binder in the autofill service side might already be dropped, + // unregisterCallback() will cause various exceptions in this + // scenario (see crbug.com/1078337), catching RuntimeException here prevents crash. + mAutofillManager.unregisterCallback(mMonitor); + } catch (RuntimeException e) { + // We are not logging anything here since some of the exceptions are raised as 'generic' + // RuntimeException which makes it difficult to catch and ignore separately; and the + // RuntimeException seemed only happen in Android O, therefore, isn't actionable. + } finally { + mAutofillManager = null; + mDestroyed = true; + } + } + + public boolean isDisabled() { + return mDisabled; + } + + private boolean checkAndWarnIfDestroyed() { + if (mDestroyed) { + Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper", + new Throwable()); + } + return mDestroyed; + } + + public void addInputUIObserver(InputUIObserver observer) { + if (observer == null) return; + if (mInputUIObservers == null) { + mInputUIObservers = new ArrayList>(); + } + mInputUIObservers.add(new WeakReference(observer)); + } + + public void removeInputUIObserver(InputUIObserver observer) { + if (observer == null) return; + for (Iterator> i = mInputUIObservers.listIterator(); + i.hasNext();) { + WeakReference o = i.next(); + if (o.get() == null || o.get() == observer) i.remove(); + } + } + + @VisibleForTesting + public void notifyInputUIChange() { + for (Iterator> i = mInputUIObservers.listIterator(); + i.hasNext();) { + WeakReference o = i.next(); + InputUIObserver observer = o.get(); + if (observer == null) { + i.remove(); + continue; + } + observer.onInputUIShown(); + } + } + + public void notifyNewSessionStarted() { + updateLogStat(); + if (isLoggable()) log("Session starts"); + } + + /** + * Always check isLoggable() before call this method. + */ + public static void log(String log) { + // Log.i() instead of Log.d() is used here because log.d() is stripped out in release build. + Log.i(TAG, log); + } + + public static boolean isLoggable() { + return sIsLoggable; + } + + private static void updateLogStat() { + // Use 'setprop log.tag.AwAutofillManager DEBUG' to enable the log at runtime. + // NOTE: See the comment on TAG above for why this is still AwAutofillManager. + sIsLoggable = Log.isLoggable(TAG, Log.DEBUG); + } +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java new file mode 100644 index 00000000000..acbf7a61a22 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java @@ -0,0 +1,786 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.view.autofill.AutofillValue; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; +import org.chromium.base.StrictModeContext; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.base.annotations.VerifiesOnO; +import org.chromium.base.metrics.ScopedSysTraceEvent; +import org.chromium.components.version_info.VersionConstants; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.WebContentsAccessibility; +import org.chromium.ui.DropdownItem; +import org.chromium.ui.base.ViewAndroidDelegate; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.display.DisplayAndroid; + +/** + * This class works with Android autofill service to fill web form, it doesn't use chrome's + * autofill service or suggestion UI. All methods are supposed to be called in UI thread. + * + * AutofillProvider handles one autofill session at time, each call of + * queryFormFieldAutofill cancels previous session and starts a new one, the + * calling of other methods shall associate with current session. + * + * This class doesn't have 1:1 mapping to native AutofillProviderAndroid; the + * normal ownership model is that this object is owned by the embedder-specific + * Java WebContents wrapper (e.g., AwContents.java in //android_webview), and + * AutofillProviderAndroid is owned by the embedder-specific C++ WebContents + * wrapper (e.g., native AwContents in //android_webview). + * + * VerifiesOnO since it causes class verification errors, see crbug.com/991851. + */ +@VerifiesOnO +@TargetApi(Build.VERSION_CODES.O) +@JNINamespace("autofill") +public class AutofillProvider { + private static final String TAG = "AutofillProvider"; + private static class FocusField { + public final short fieldIndex; + public final Rect absBound; + + public FocusField(short fieldIndex, Rect absBound) { + this.fieldIndex = fieldIndex; + this.absBound = absBound; + } + } + /** + * The class to wrap the request to framework. + * + * Though framework guarantees always giving us the autofill value of current + * session, we still want to verify this by using unique virtual id which is + * composed of sessionId and form field index, we don't use the request id + * which comes from renderer as session id because it is not unique. + */ + private static class AutofillRequest { + private static final int INIT_ID = 1; // ID can't be 0 in Android. + private static int sSessionId = INIT_ID; + public final int sessionId; + private FormData mFormData; + private FocusField mFocusField; + + public AutofillRequest(FormData formData, FocusField focus) { + sessionId = getNextClientId(); + mFormData = formData; + mFocusField = focus; + } + + public void fillViewStructure(ViewStructure structure) { + structure.setWebDomain(mFormData.mHost); + structure.setHtmlInfo(structure.newHtmlInfoBuilder("form") + .addAttribute("name", mFormData.mName) + .build()); + int index = structure.addChildCount(mFormData.mFields.size()); + short fieldIndex = 0; + for (FormFieldData field : mFormData.mFields) { + ViewStructure child = structure.newChild(index++); + int virtualId = toVirtualId(sessionId, fieldIndex++); + child.setAutofillId(structure.getAutofillId(), virtualId); + if (field.mAutocompleteAttr != null && !field.mAutocompleteAttr.isEmpty()) { + child.setAutofillHints(field.mAutocompleteAttr.split(" +")); + } + child.setHint(field.mPlaceholder); + + RectF bounds = field.getBoundsInContainerViewCoordinates(); + // Field has no scroll. + child.setDimens((int) bounds.left, (int) bounds.top, 0 /* scrollX*/, + 0 /* scrollY */, (int) bounds.width(), (int) bounds.height()); + + ViewStructure.HtmlInfo.Builder builder = + child.newHtmlInfoBuilder("input") + .addAttribute("name", field.mName) + .addAttribute("type", field.mType) + .addAttribute("label", field.mLabel) + .addAttribute("ua-autofill-hints", field.mHeuristicType) + .addAttribute("id", field.mId); + + switch (field.getControlType()) { + case FormFieldData.ControlType.LIST: + child.setAutofillType(View.AUTOFILL_TYPE_LIST); + child.setAutofillOptions(field.mOptionContents); + int i = findIndex(field.mOptionValues, field.getValue()); + if (i != -1) { + child.setAutofillValue(AutofillValue.forList(i)); + } + break; + case FormFieldData.ControlType.TOGGLE: + child.setAutofillType(View.AUTOFILL_TYPE_TOGGLE); + child.setAutofillValue(AutofillValue.forToggle(field.isChecked())); + break; + case FormFieldData.ControlType.TEXT: + case FormFieldData.ControlType.DATALIST: + child.setAutofillType(View.AUTOFILL_TYPE_TEXT); + child.setAutofillValue(AutofillValue.forText(field.getValue())); + if (field.mMaxLength != 0) { + builder.addAttribute("maxlength", String.valueOf(field.mMaxLength)); + } + if (field.getControlType() == FormFieldData.ControlType.DATALIST) { + child.setAutofillOptions(field.mDatalistValues); + } + break; + default: + break; + } + child.setHtmlInfo(builder.build()); + } + } + + public boolean autofill(final SparseArray values) { + for (int i = 0; i < values.size(); ++i) { + int id = values.keyAt(i); + if (toSessionId(id) != sessionId) return false; + AutofillValue value = values.get(id); + if (value == null) continue; + short index = toIndex(id); + if (index < 0 || index >= mFormData.mFields.size()) return false; + FormFieldData field = mFormData.mFields.get(index); + if (field == null) return false; + try { + switch (field.getControlType()) { + case FormFieldData.ControlType.LIST: + int j = value.getListValue(); + if (j < 0 && j >= field.mOptionValues.length) continue; + field.setAutofillValue(field.mOptionValues[j]); + break; + case FormFieldData.ControlType.TOGGLE: + field.setChecked(value.getToggleValue()); + break; + case FormFieldData.ControlType.TEXT: + case FormFieldData.ControlType.DATALIST: + field.setAutofillValue((String) value.getTextValue()); + break; + default: + break; + } + } catch (IllegalStateException e) { + // Refer to crbug.com/1080580 . + Log.e(TAG, "The given AutofillValue wasn't expected, abort autofill.", e); + return false; + } + } + return true; + } + + public void setFocusField(FocusField focusField) { + mFocusField = focusField; + } + + public FocusField getFocusField() { + return mFocusField; + } + + public int getFieldCount() { + return mFormData.mFields.size(); + } + + public AutofillValue getFieldNewValue(int index) { + FormFieldData field = mFormData.mFields.get(index); + if (field == null) return null; + switch (field.getControlType()) { + case FormFieldData.ControlType.LIST: + int i = findIndex(field.mOptionValues, field.getValue()); + if (i == -1) return null; + return AutofillValue.forList(i); + case FormFieldData.ControlType.TOGGLE: + return AutofillValue.forToggle(field.isChecked()); + case FormFieldData.ControlType.TEXT: + case FormFieldData.ControlType.DATALIST: + return AutofillValue.forText(field.getValue()); + default: + return null; + } + } + + public int getVirtualId(short index) { + return toVirtualId(sessionId, index); + } + + public FormFieldData getField(short index) { + return mFormData.mFields.get(index); + } + + private static int findIndex(String[] values, String value) { + if (values != null && value != null) { + for (int i = 0; i < values.length; i++) { + if (value.equals(values[i])) return i; + } + } + return -1; + } + + private static int getNextClientId() { + ThreadUtils.assertOnUiThread(); + if (sSessionId == 0xffff) sSessionId = INIT_ID; + return sSessionId++; + } + + private static int toSessionId(int virtualId) { + return (virtualId & 0xffff0000) >> 16; + } + + private static short toIndex(int virtualId) { + return (short) (virtualId & 0xffff); + } + + private static int toVirtualId(int clientId, short index) { + return (clientId << 16) | index; + } + } + + private final String mProviderName; + private AutofillManagerWrapper mAutofillManager; + private ViewGroup mContainerView; + private WebContents mWebContents; + + private AutofillRequest mRequest; + private long mNativeAutofillProvider; + private AutofillProviderUMA mAutofillUMA; + private AutofillManagerWrapper.InputUIObserver mInputUIObserver; + private long mAutofillTriggeredTimeMillis; + private Context mContext; + private AutofillPopup mDatalistPopup; + private WebContentsAccessibility mWebContentsAccessibility; + private View mAnchorView; + + public AutofillProvider(Context context, ViewGroup containerView, String providerName) { + this(containerView, new AutofillManagerWrapper(context), context, providerName); + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public AutofillProvider(ViewGroup containerView, AutofillManagerWrapper manager, + Context context, String providerName) { + mProviderName = providerName; + try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("AutofillProvider.constructor")) { + assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + mAutofillManager = manager; + mContainerView = containerView; + mAutofillUMA = new AutofillProviderUMA(context); + mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() { + @Override + public void onInputUIShown() { + // Not need to report suggestion window displayed if there is no live autofill + // session. + if (mRequest == null) return; + mAutofillUMA.onSuggestionDisplayed( + System.currentTimeMillis() - mAutofillTriggeredTimeMillis); + } + }; + mAutofillManager.addInputUIObserver(mInputUIObserver); + mContext = context; + } + } + + /** + * Invoked when container view is changed. + * + * @param containerView new container view. + */ + public void onContainerViewChanged(ViewGroup containerView) { + mContainerView = containerView; + } + + /** + * Invoked when autofill service needs the form structure. + * + * @param structure see View.onProvideAutofillVirtualStructure() + * @param flags see View.onProvideAutofillVirtualStructure() + */ + public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { + // This method could be called for the session started by the native + // control outside of the scope of autofill, e.g. the URL bar, in this case, we simply + // return. + if (mRequest == null) return; + + Bundle bundle = structure.getExtras(); + if (bundle != null) { + bundle.putCharSequence("VIRTUAL_STRUCTURE_PROVIDER_NAME", mProviderName); + bundle.putCharSequence( + "VIRTUAL_STRUCTURE_PROVIDER_VERSION", VersionConstants.PRODUCT_VERSION); + } + mRequest.fillViewStructure(structure); + if (AutofillManagerWrapper.isLoggable()) { + AutofillManagerWrapper.log( + "onProvideAutoFillVirtualStructure fields:" + structure.getChildCount()); + } + mAutofillUMA.onVirtualStructureProvided(); + } + + /** + * Invoked when autofill value is available, AutofillProvider shall fill the + * form with the provided values. + * + * @param values the array of autofill values, the key is virtual id of form + * field. + */ + public void autofill(final SparseArray values) { + if (mNativeAutofillProvider != 0 && mRequest != null && mRequest.autofill((values))) { + autofill(mNativeAutofillProvider, mRequest.mFormData); + if (AutofillManagerWrapper.isLoggable()) { + AutofillManagerWrapper.log("autofill values:" + values.size()); + } + mAutofillUMA.onAutofill(); + } + } + + /** + * @return whether query autofill suggestion. + */ + public boolean shouldQueryAutofillSuggestion() { + return mRequest != null && mRequest.getFocusField() != null + && !mAutofillManager.isAutofillInputUIShowing(); + } + + public void queryAutofillSuggestion() { + if (shouldQueryAutofillSuggestion()) { + FocusField focusField = mRequest.getFocusField(); + mAutofillManager.requestAutofill(mContainerView, + mRequest.getVirtualId(focusField.fieldIndex), focusField.absBound); + } + } + + /** + * Invoked when filling form is need. AutofillProvider shall ask autofill + * service for the values with which to fill the form. + * + * @param formData the form needs to fill. + * @param focus the index of focus field in formData + * @param x the boundary of focus field. + * @param y the boundary of focus field. + * @param width the boundary of focus field. + * @param height the boundary of focus field. + */ + @CalledByNative + public void startAutofillSession( + FormData formData, int focus, float x, float y, float width, float height) { + // Check focusField inside short value? + // Autofill Manager might have session that wasn't started by AutofillProvider, + // we just always cancel existing session here. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + mAutofillManager.cancel(); + } + + Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); + if (mRequest != null) notifyViewExitBeforeDestroyRequest(); + transformFormFieldToContainViewCoordinates(formData); + mRequest = new AutofillRequest(formData, new FocusField((short) focus, absBound)); + int virtualId = mRequest.getVirtualId((short) focus); + notifyVirtualViewEntered(mContainerView, virtualId, absBound); + mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled()); + mAutofillTriggeredTimeMillis = System.currentTimeMillis(); + + mAutofillManager.notifyNewSessionStarted(); + } + + /** + * Invoked when form field's value is changed. + * + * @param index index of field in current form. + * @param x the boundary of focus field. + * @param y the boundary of focus field. + * @param width the boundary of focus field. + * @param height the boundary of focus field. + * + */ + @CalledByNative + public void onFormFieldDidChange(int index, float x, float y, float width, float height) { + // Check index inside short value? + if (mRequest == null) return; + + short sIndex = (short) index; + FocusField focusField = mRequest.getFocusField(); + if (focusField == null || sIndex != focusField.fieldIndex) { + onFocusChangedImpl(true, index, x, y, width, height, true /*causedByValueChange*/); + } else { + // Currently there is no api to notify both value and position + // change, before the API is available, we still need to call + // notifyVirtualViewEntered() to tell current coordinates because + // the position could be changed. + int virtualId = mRequest.getVirtualId(sIndex); + Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); + if (!focusField.absBound.equals(absBound)) { + notifyVirtualViewExited(mContainerView, virtualId); + notifyVirtualViewEntered(mContainerView, virtualId, absBound); + // Update focus field position. + mRequest.setFocusField(new FocusField(focusField.fieldIndex, absBound)); + } + } + notifyVirtualValueChanged(index, /* forceNotify = */ false); + mAutofillUMA.onUserChangeFieldValue(mRequest.getField(sIndex).hasPreviouslyAutofilled()); + } + + /** + * Invoked when text field is scrolled. + * + * @param index index of field in current form. + * @param x the boundary of focus field. + * @param y the boundary of focus field. + * @param width the boundary of focus field. + * @param height the boundary of focus field. + * + */ + @CalledByNative + public void onTextFieldDidScroll(int index, float x, float y, float width, float height) { + // crbug.com/730764 - from P and above, Android framework listens to the onScrollChanged() + // and repositions the autofill UI automatically. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) return; + if (mRequest == null) return; + + short sIndex = (short) index; + FocusField focusField = mRequest.getFocusField(); + if (focusField == null || sIndex != focusField.fieldIndex) return; + + int virtualId = mRequest.getVirtualId(sIndex); + Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); + // Notify the new position to the Android framework. Note that we do not call + // notifyVirtualViewExited() here intentionally to avoid flickering. + notifyVirtualViewEntered(mContainerView, virtualId, absBound); + + // Update focus field position. + mRequest.setFocusField(new FocusField(focusField.fieldIndex, absBound)); + } + + private boolean isDatalistField(int childId) { + FormFieldData field = mRequest.getField((short) childId); + return field.mControlType == FormFieldData.ControlType.DATALIST; + } + + private void notifyVirtualValueChanged(int index, boolean forceNotify) { + // The ValueChanged, ViewEntered and ViewExited aren't notified to the autofill service for + // the focused datalist to avoid the potential UI conflict. + // The datalist support was added later and the option list is displayed by WebView, the + // autofill service might also show its suggestions when the datalist (associated the input + // field) is focused, the two UI overlap, the solution is to completely hide the fact that + // the datalist is being focused to the autofill service to prevent it from displaying the + // suggestion. + // The ValueChange will still be sent to autofill service when the form + // submitted or autofilled. + if (!forceNotify && isDatalistField(index)) return; + AutofillValue autofillValue = mRequest.getFieldNewValue(index); + if (autofillValue == null) return; + mAutofillManager.notifyVirtualValueChanged( + mContainerView, mRequest.getVirtualId((short) index), autofillValue); + } + + private void notifyVirtualViewEntered(View parent, int childId, Rect absBounds) { + // Refer to notifyVirtualValueChanged() for the reason of the datalist's special handling. + if (isDatalistField(childId)) return; + mAutofillManager.notifyVirtualViewEntered(parent, childId, absBounds); + } + + private void notifyVirtualViewExited(View parent, int childId) { + // Refer to notifyVirtualValueChanged() for the reason of the datalist's special handling. + if (isDatalistField(childId)) return; + mAutofillManager.notifyVirtualViewExited(parent, childId); + } + + /** + * Invoked when current form will be submitted. + * @param submissionSource the submission source, could be any member defined in + * SubmissionSource.java + */ + @CalledByNative + public void onFormSubmitted(int submissionSource) { + // The changes could be missing, like those made by Javascript, we'd better to notify + // AutofillManager current values. also see crbug.com/353001 and crbug.com/732856. + forceNotifyFormValues(); + mAutofillManager.commit(submissionSource); + mRequest = null; + mAutofillUMA.onFormSubmitted(submissionSource); + } + + /** + * Invoked when focus field changed. + * + * @param focusOnForm whether focus is still on form. + * @param focusItem the index of field has focus + * @param x the boundary of focus field. + * @param y the boundary of focus field. + * @param width the boundary of focus field. + * @param height the boundary of focus field. + */ + @CalledByNative + public void onFocusChanged( + boolean focusOnForm, int focusField, float x, float y, float width, float height) { + onFocusChangedImpl( + focusOnForm, focusField, x, y, width, height, false /*causedByValueChange*/); + } + + @CalledByNative + public void hidePopup() { + if (mDatalistPopup != null) { + mDatalistPopup.dismiss(); + mDatalistPopup = null; + } + if (mWebContentsAccessibility != null) { + mWebContentsAccessibility.onAutofillPopupDismissed(); + } + } + + private void notifyViewExitBeforeDestroyRequest() { + if (mRequest == null) return; + FocusField focusField = mRequest.getFocusField(); + if (focusField == null) return; + notifyVirtualViewExited(mContainerView, mRequest.getVirtualId(focusField.fieldIndex)); + mRequest.setFocusField(null); + } + + private void onFocusChangedImpl(boolean focusOnForm, int focusField, float x, float y, + float width, float height, boolean causedByValueChange) { + // Check focusField inside short value? + // FocusNoLongerOnForm is called after form submitted. + if (mRequest == null) return; + FocusField prev = mRequest.getFocusField(); + if (focusOnForm) { + Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); + if (prev != null && prev.fieldIndex == focusField && absBound.equals(prev.absBound)) { + return; + } + + // Notify focus changed. + if (prev != null) { + notifyVirtualViewExited(mContainerView, mRequest.getVirtualId(prev.fieldIndex)); + } + + notifyVirtualViewEntered( + mContainerView, mRequest.getVirtualId((short) focusField), absBound); + + if (!causedByValueChange) { + // The focus field value might not sync with platform's + // AutofillManager, just notify it value changed. + notifyVirtualValueChanged(focusField, /* forceNotify = */ false); + mAutofillTriggeredTimeMillis = System.currentTimeMillis(); + } + mRequest.setFocusField(new FocusField((short) focusField, absBound)); + } else { + if (prev == null) return; + // Notify focus changed. + notifyVirtualViewExited(mContainerView, mRequest.getVirtualId(prev.fieldIndex)); + mRequest.setFocusField(null); + } + } + + @CalledByNative + protected void showDatalistPopup( + String[] datalistValues, String[] datalistLabels, boolean isRtl) { + if (mRequest == null) return; + FocusField focusField = mRequest.getFocusField(); + if (focusField != null) { + showDatalistPopup(datalistValues, datalistLabels, + mRequest.getField(focusField.fieldIndex).getBounds(), isRtl); + } + } + + /** + * Display the simplest popup for the datalist. This is same as WebView's datalist popup in + * Android pre-o. No suggestion from the autofill service will be presented, No advance + * features of AutofillPopup are used. + */ + private void showDatalistPopup( + String[] datalistValues, String[] datalistLabels, RectF bounds, boolean isRtl) { + final AutofillSuggestion[] suggestions = new AutofillSuggestion[datalistValues.length]; + for (int i = 0; i < suggestions.length; i++) { + suggestions[i] = new AutofillSuggestion(datalistValues[i], datalistLabels[i], + DropdownItem.NO_ICON, false /* isIconAtLeft */, i, false /* isDeletable */, + false /* isMultilineLabel */, false /* isBoldLabel */); + } + if (mWebContentsAccessibility == null) { + mWebContentsAccessibility = WebContentsAccessibility.fromWebContents(mWebContents); + } + if (mDatalistPopup == null) { + if (ContextUtils.activityFromContext(mContext) == null) return; + ViewAndroidDelegate delegate = mWebContents.getViewAndroidDelegate(); + if (mAnchorView == null) mAnchorView = delegate.acquireView(); + setAnchorViewRect(bounds); + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { + mDatalistPopup = new AutofillPopup(mContext, mAnchorView, new AutofillDelegate() { + @Override + public void dismissed() { + onDatalistPopupDismissed(); + } + + @Override + public void suggestionSelected(int listIndex) { + onSuggestionSelected(suggestions[listIndex].getLabel()); + } + + @Override + public void deleteSuggestion(int listIndex) {} + + @Override + public void accessibilityFocusCleared() { + mWebContentsAccessibility.onAutofillPopupAccessibilityFocusCleared(); + } + }); + } catch (RuntimeException e) { + // Deliberately swallowing exception because bad framework implementation can + // throw exceptions in ListPopupWindow constructor. + onDatalistPopupDismissed(); + return; + } + } + mDatalistPopup.filterAndShow(suggestions, isRtl, false); + if (mWebContentsAccessibility != null) { + mWebContentsAccessibility.onAutofillPopupDisplayed(mDatalistPopup.getListView()); + } + } + + private void onDatalistPopupDismissed() { + ViewAndroidDelegate delegate = mWebContents.getViewAndroidDelegate(); + delegate.removeView(mAnchorView); + mAnchorView = null; + } + + private void onSuggestionSelected(String value) { + acceptDataListSuggestion(mNativeAutofillProvider, value); + hidePopup(); + } + + private void setAnchorViewRect(RectF rect) { + setAnchorViewRect(mNativeAutofillProvider, mAnchorView, rect); + } + + /** + * Invoked when current query need to be reset. + */ + @CalledByNative + protected void reset() { + // We don't need to reset anything here, it should be safe to cancel + // current autofill session when new one starts in + // startAutofillSession(). + } + + @CalledByNative + protected void setNativeAutofillProvider(long nativeAutofillProvider) { + if (nativeAutofillProvider == mNativeAutofillProvider) return; + // Setting the mNativeAutofillProvider to 0 may occur as a + // result of WebView.destroy, or because a WebView has been + // gc'ed. In the former case we can go ahead and clean up the + // frameworks autofill manager, but in the latter case the + // binder connection has already been dropped in a framework + // finalizer, and so the methods we call will throw. It's not + // possible to know which case we're in, so just catch the exception + // in AutofillManagerWrapper.destroy(). + if (mNativeAutofillProvider != 0) mRequest = null; + mNativeAutofillProvider = nativeAutofillProvider; + if (nativeAutofillProvider == 0) mAutofillManager.destroy(); + } + + public void setWebContents(WebContents webContents) { + if (webContents == mWebContents) return; + if (mWebContents != null) mRequest = null; + mWebContents = webContents; + } + + @CalledByNative + protected void onDidFillAutofillFormData() { + // The changes were caused by the autofill service autofill form, + // notified it about the result. + forceNotifyFormValues(); + } + + private void forceNotifyFormValues() { + if (mRequest == null) return; + for (int i = 0; i < mRequest.getFieldCount(); ++i) { + notifyVirtualValueChanged(i, /* forceNotify = */ true); + } + } + + @VisibleForTesting + public AutofillPopup getDatalistPopupForTesting() { + return mDatalistPopup; + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public Rect transformToWindowBounds(RectF rect) { + // Convert bounds to device pixel. + WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); + DisplayAndroid displayAndroid = windowAndroid.getDisplay(); + float dipScale = displayAndroid.getDipScale(); + RectF bounds = new RectF(rect); + Matrix matrix = new Matrix(); + matrix.setScale(dipScale, dipScale); + int[] location = new int[2]; + mContainerView.getLocationOnScreen(location); + matrix.postTranslate(location[0], location[1]); + matrix.mapRect(bounds); + return new Rect( + (int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom); + } + + /** + * Transform FormFieldData's bounds to ContainView's coordinates and update the bounds with the + * transformed one. + * + * @param formData the form need to be transformed. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public void transformFormFieldToContainViewCoordinates(FormData formData) { + WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); + DisplayAndroid displayAndroid = windowAndroid.getDisplay(); + float dipScale = displayAndroid.getDipScale(); + Matrix matrix = new Matrix(); + matrix.setScale(dipScale, dipScale); + matrix.postTranslate(mContainerView.getScrollX(), mContainerView.getScrollY()); + + for (FormFieldData field : formData.mFields) { + RectF bounds = new RectF(); + matrix.mapRect(bounds, field.getBounds()); + field.setBoundsInContainerViewCoordinates(bounds); + } + } + + /** + * Send form to renderer for filling. + * + * @param nativeAutofillProvider the native autofill provider. + * @param formData the form to fill. + */ + private void autofill(long nativeAutofillProvider, FormData formData) { + AutofillProviderJni.get().onAutofillAvailable( + nativeAutofillProvider, AutofillProvider.this, formData); + } + + private void acceptDataListSuggestion(long nativeAutofillProvider, String value) { + AutofillProviderJni.get().onAcceptDataListSuggestion( + nativeAutofillProvider, AutofillProvider.this, value); + } + + private void setAnchorViewRect(long nativeAutofillProvider, View anchorView, RectF rect) { + AutofillProviderJni.get().setAnchorViewRect(nativeAutofillProvider, AutofillProvider.this, + anchorView, rect.left, rect.top, rect.width(), rect.height()); + } + + @NativeMethods + interface Natives { + void onAutofillAvailable( + long nativeAutofillProviderAndroid, AutofillProvider caller, FormData formData); + + void onAcceptDataListSuggestion( + long nativeAutofillProviderAndroid, AutofillProvider caller, String value); + + void setAnchorViewRect(long nativeAutofillProviderAndroid, AutofillProvider caller, + View anchorView, float x, float y, float width, float height); + } +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java new file mode 100644 index 00000000000..394cd849c95 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java @@ -0,0 +1,255 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import android.content.Context; + +import org.chromium.autofill.mojom.SubmissionSource; +import org.chromium.base.ContextUtils; +import org.chromium.base.metrics.RecordHistogram; + +import java.util.concurrent.TimeUnit; + +/** + * The class for AutofillProvider-related UMA. Note that most of the concrete histogram + * names include "WebView"; when this class was originally developed it was WebView-specific, + * and when generalizing it we did not change these names to maintain continuity when + * analyzing the histograms. + */ +public class AutofillProviderUMA { + // Records whether the Autofill service is enabled or not. + public static final String UMA_AUTOFILL_ENABLED = "Autofill.WebView.Enabled"; + + // Records whether the Autofill provider is created by activity context or not. + public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT = + "Autofill.WebView.CreatedByActivityContext"; + + // Records what happened in an autofill session. + public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession"; + // The possible value of UMA_AUTOFILL_AUTOFILL_SESSION. + public static final int SESSION_UNKNOWN = 0; + public static final int NO_CALLBACK_FORM_FRAMEWORK = 1; + public static final int NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 2; + public static final int NO_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 3; + public static final int NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 4; + public static final int NO_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 5; + public static final int USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 6; + public static final int USER_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 7; + public static final int USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 8; + public static final int USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 9; + public static final int USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 10; + public static final int USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED = 11; + public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED = 12; + public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13; + public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14; + + // Records whether user changed autofilled field if user ever changed the form. The action isn't + // recorded if user didn't change form at all. + public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD = + "Autofill.WebView.UserChangedAutofilledField"; + + public static final String UMA_AUTOFILL_SUBMISSION_SOURCE = "Autofill.WebView.SubmissionSource"; + // The possible value of UMA_AUTOFILL_SUBMISSION_SOURCE. + public static final int SAME_DOCUMENT_NAVIGATION = 0; + public static final int XHR_SUCCEEDED = 1; + public static final int FRAME_DETACHED = 2; + public static final int DOM_MUTATION_AFTER_XHR = 3; + public static final int PROBABLY_FORM_SUBMITTED = 4; + public static final int FORM_SUBMISSION = 5; + public static final int SUBMISSION_SOURCE_HISTOGRAM_COUNT = 6; + + // The million seconds from user touched the field to the autofill session starting. + public static final String UMA_AUTOFILL_TRIGGERING_TIME = "Autofill.WebView.TriggeringTime"; + + // The million seconds from the autofill session starting to the suggestion being displayed. + public static final String UMA_AUTOFILL_SUGGESTION_TIME = "Autofill.WebView.SuggestionTime"; + + // The expected time range of time is from 10ms to 2 seconds, and 50 buckets is sufficient. + private static final long MIN_TIME_MILLIS = 10; + private static final long MAX_TIME_MILLIS = TimeUnit.SECONDS.toMillis(2); + private static final int NUM_OF_BUCKETS = 50; + + private static void recordTimesHistogram(String name, long durationMillis) { + RecordHistogram.recordCustomTimesHistogram( + name, durationMillis, MIN_TIME_MILLIS, MAX_TIME_MILLIS, NUM_OF_BUCKETS); + } + + private static class SessionRecorder { + public static final int EVENT_VIRTUAL_STRUCTURE_PROVIDED = 0x1 << 0; + public static final int EVENT_SUGGESTION_DISPLAYED = 0x1 << 1; + public static final int EVENT_FORM_AUTOFILLED = 0x1 << 2; + public static final int EVENT_USER_CHANGED_FIELD_VALUE = 0x1 << 3; + public static final int EVENT_FORM_SUBMITTED = 0x1 << 4; + public static final int EVENT_USER_CHANGED_AUTOFILLED_FIELD = 0x1 << 5; + + private Long mSuggestionTimeMillis; + + public void record(int event) { + // Not record any event until we get EVENT_VIRTUAL_STRUCTURE_PROVIDED which makes the + // following events meaningful. + if (event != EVENT_VIRTUAL_STRUCTURE_PROVIDED && mState == 0) return; + if (EVENT_USER_CHANGED_FIELD_VALUE == event && mUserChangedAutofilledField == null) { + mUserChangedAutofilledField = Boolean.valueOf(false); + } else if (EVENT_USER_CHANGED_AUTOFILLED_FIELD == event) { + if (mUserChangedAutofilledField == null) { + mUserChangedAutofilledField = Boolean.valueOf(true); + } + mUserChangedAutofilledField = true; + event = EVENT_USER_CHANGED_FIELD_VALUE; + } + mState |= event; + } + + public void setSuggestionTimeMillis(long suggestionTimeMillis) { + // Only record first suggestion. + if (mSuggestionTimeMillis == null) { + mSuggestionTimeMillis = Long.valueOf(suggestionTimeMillis); + } + } + + public void recordHistogram() { + RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_AUTOFILL_SESSION, + toUMAAutofillSessionValue(), AUTOFILL_SESSION_HISTOGRAM_COUNT); + // Only record if user ever changed form. + if (mUserChangedAutofilledField != null) { + RecordHistogram.recordBooleanHistogram( + UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, mUserChangedAutofilledField); + } + if (mSuggestionTimeMillis != null) { + recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis); + } + } + + private int toUMAAutofillSessionValue() { + if (mState == 0) { + return NO_CALLBACK_FORM_FRAMEWORK; + } else if (mState == EVENT_VIRTUAL_STRUCTURE_PROVIDED) { + return NO_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE)) { + return NO_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; + } else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_FORM_SUBMITTED)) { + return NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE + | EVENT_FORM_SUBMITTED)) { + return NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_FORM_AUTOFILLED)) { + return USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_FORM_AUTOFILLED | EVENT_FORM_SUBMITTED)) { + return USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE + | EVENT_FORM_SUBMITTED)) { + return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE)) { + return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; + } else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED)) { + return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_FORM_SUBMITTED)) { + return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_USER_CHANGED_FIELD_VALUE | EVENT_FORM_SUBMITTED)) { + return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED; + } else if (mState + == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED + | EVENT_USER_CHANGED_FIELD_VALUE)) { + return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED; + } else { + return SESSION_UNKNOWN; + } + } + + private int mState; + private Boolean mUserChangedAutofilledField; + } + + private SessionRecorder mRecorder; + private Boolean mAutofillDisabled; + + public AutofillProviderUMA(Context context) { + RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT, + ContextUtils.activityFromContext(context) != null); + } + + public void onFormSubmitted(int submissionSource) { + if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED); + recordSession(); + // We record this no matter autofill service is disabled or not. + RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE, + toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT); + } + + public void onSessionStarted(boolean autofillDisabled) { + // Record autofill status once per instance and only if user triggers the autofill. + if (mAutofillDisabled == null || mAutofillDisabled.booleanValue() != autofillDisabled) { + RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_ENABLED, !autofillDisabled); + mAutofillDisabled = Boolean.valueOf(autofillDisabled); + } + + if (mRecorder != null) recordSession(); + mRecorder = new SessionRecorder(); + } + + public void onVirtualStructureProvided() { + if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_VIRTUAL_STRUCTURE_PROVIDED); + } + + public void onSuggestionDisplayed(long suggestionTimeMillis) { + if (mRecorder != null) { + mRecorder.record(SessionRecorder.EVENT_SUGGESTION_DISPLAYED); + mRecorder.setSuggestionTimeMillis(suggestionTimeMillis); + } + } + + public void onAutofill() { + if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED); + } + + public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) { + if (mRecorder == null) return; + if (isPreviouslyAutofilled) { + mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_AUTOFILLED_FIELD); + } else { + mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_FIELD_VALUE); + } + } + + private void recordSession() { + if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) { + mRecorder.recordHistogram(); + } + mRecorder = null; + } + + private int toUMASubmissionSource(int source) { + switch (source) { + case SubmissionSource.SAME_DOCUMENT_NAVIGATION: + return SAME_DOCUMENT_NAVIGATION; + case SubmissionSource.XHR_SUCCEEDED: + return XHR_SUCCEEDED; + case SubmissionSource.FRAME_DETACHED: + return FRAME_DETACHED; + case SubmissionSource.DOM_MUTATION_AFTER_XHR: + return DOM_MUTATION_AFTER_XHR; + case SubmissionSource.PROBABLY_FORM_SUBMITTED: + return PROBABLY_FORM_SUBMITTED; + case SubmissionSource.FORM_SUBMISSION: + return FORM_SUBMISSION; + default: + return SUBMISSION_SOURCE_HISTOGRAM_COUNT; + } + } +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormData.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormData.java new file mode 100644 index 00000000000..8069aa887b6 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormData.java @@ -0,0 +1,56 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; + +import java.util.ArrayList; + +/** + * The wrap class of native autofill::FormDataAndroid. + */ +@JNINamespace("autofill") +public class FormData { + public final String mName; + public final String mHost; + public final ArrayList mFields; + + @CalledByNative + private static FormData createFormData( + long nativeObj, String name, String origin, int fieldCount) { + return new FormData(nativeObj, name, origin, fieldCount); + } + + private static ArrayList popupFormFields(long nativeObj, int fieldCount) { + FormFieldData formFieldData = FormDataJni.get().getNextFormFieldData(nativeObj); + ArrayList fields = new ArrayList(fieldCount); + while (formFieldData != null) { + fields.add(formFieldData); + formFieldData = FormDataJni.get().getNextFormFieldData(nativeObj); + } + assert fields.size() == fieldCount; + return fields; + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public FormData(String name, String host, ArrayList fields) { + mName = name; + mHost = host; + mFields = fields; + } + + private FormData(long nativeObj, String name, String host, int fieldCount) { + this(name, host, popupFormFields(nativeObj, fieldCount)); + } + + @NativeMethods + interface Natives { + FormFieldData getNextFormFieldData(long nativeFormDataAndroid); + } +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java new file mode 100644 index 00000000000..eb0a94a6bce --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java @@ -0,0 +1,162 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import android.graphics.RectF; + +import androidx.annotation.IntDef; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The wrap class of native autofill::FormFieldDataAndroid. + */ +@JNINamespace("autofill") +public class FormFieldData { + /** + * Define the control types supported by android.view.autofill.AutofillValue. + * + * Android doesn't have DATALIST control, it is sent to the Autofill service as + * View.AUTOFILL_TYPE_TEXT with AutofillOptions. + */ + @IntDef({ControlType.TEXT, ControlType.TOGGLE, ControlType.LIST, ControlType.DATALIST}) + @Retention(RetentionPolicy.SOURCE) + public @interface ControlType { + int TEXT = 0; + int TOGGLE = 1; + int LIST = 2; + int DATALIST = 3; + } + + public final String mLabel; + public final String mName; + public final String mAutocompleteAttr; + public final boolean mShouldAutocomplete; + public final String mPlaceholder; + public final String mType; + public final String mId; + public final String[] mOptionValues; + public final String[] mOptionContents; + public final @ControlType int mControlType; + public final int mMaxLength; + public final String mHeuristicType; + public final String[] mDatalistValues; + public final String[] mDatalistLabels; + + // The bounds in the viewport's coordinates + private final RectF mBounds; + // The bounds in the container view's coordinates. + private RectF mBoundsInContainerViewCoordinates; + + private boolean mIsChecked; + private String mValue; + // Indicates whether mValue is autofilled. + private boolean mAutofilled; + // Indicates whether this fields was autofilled, but changed by user. + private boolean mPreviouslyAutofilled; + + private FormFieldData(String name, String label, String value, String autocompleteAttr, + boolean shouldAutocomplete, String placeholder, String type, String id, + String[] optionValues, String[] optionContents, boolean isCheckField, boolean isChecked, + int maxLength, String heuristicType, float left, float top, float right, float bottom, + String[] datalistValues, String[] datalistLabels) { + mName = name; + mLabel = label; + mValue = value; + mAutocompleteAttr = autocompleteAttr; + mShouldAutocomplete = shouldAutocomplete; + mPlaceholder = placeholder; + mType = type; + mId = id; + mOptionValues = optionValues; + mOptionContents = optionContents; + mIsChecked = isChecked; + mDatalistLabels = datalistLabels; + mDatalistValues = datalistValues; + if (mOptionValues != null && mOptionValues.length != 0) { + mControlType = ControlType.LIST; + } else if (mDatalistValues != null && mDatalistValues.length != 0) { + mControlType = ControlType.DATALIST; + } else if (isCheckField) { + mControlType = ControlType.TOGGLE; + } else { + mControlType = ControlType.TEXT; + } + mMaxLength = maxLength; + mHeuristicType = heuristicType; + mBounds = new RectF(left, top, right, bottom); + } + + public @ControlType int getControlType() { + return mControlType; + } + + public RectF getBounds() { + return mBounds; + } + + public void setBoundsInContainerViewCoordinates(RectF bounds) { + mBoundsInContainerViewCoordinates = bounds; + } + + public RectF getBoundsInContainerViewCoordinates() { + return mBoundsInContainerViewCoordinates; + } + + /** + * @return value of field. + */ + @CalledByNative + public String getValue() { + return mValue; + } + + public void setAutofillValue(String value) { + mValue = value; + updateAutofillState(true); + } + + public void setChecked(boolean checked) { + mIsChecked = checked; + updateAutofillState(true); + } + + @CalledByNative + private void updateValue(String value) { + mValue = value; + updateAutofillState(false); + } + + @CalledByNative + public boolean isChecked() { + return mIsChecked; + } + + public boolean hasPreviouslyAutofilled() { + return mPreviouslyAutofilled; + } + + private void updateAutofillState(boolean autofilled) { + if (mAutofilled && !autofilled) mPreviouslyAutofilled = true; + mAutofilled = autofilled; + } + + @CalledByNative + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public static FormFieldData createFormFieldData(String name, String label, String value, + String autocompleteAttr, boolean shouldAutocomplete, String placeholder, String type, + String id, String[] optionValues, String[] optionContents, boolean isCheckField, + boolean isChecked, int maxLength, String heuristicType, float left, float top, + float right, float bottom, String[] datalistValues, String[] datalistLabels) { + return new FormFieldData(name, label, value, autocompleteAttr, shouldAutocomplete, + placeholder, type, id, optionValues, optionContents, isCheckField, isChecked, + maxLength, heuristicType, left, top, right, bottom, datalistValues, datalistLabels); + } +} diff --git a/chromium/components/autofill/android/provider/junit/BUILD.gn b/chromium/components/autofill/android/provider/junit/BUILD.gn new file mode 100644 index 00000000000..95cea50bf90 --- /dev/null +++ b/chromium/components/autofill/android/provider/junit/BUILD.gn @@ -0,0 +1,23 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/config.gni") +import("//build/config/android/rules.gni") + +java_library("components_autofill_junit_tests") { + # Platform checks are broken for Robolectric. See https://crbug.com/1071638. + bypass_platform_checks = true + testonly = true + sources = [ "src/org/chromium/components/autofill/AutofillProviderTest.java" ] + deps = [ + "//base:base_java_test_support", + "//base:base_junit_test_support", + "//components/autofill/android/provider:java", + "//content/public/android:content_java", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", + "//ui/android:ui_java", + ] +} diff --git a/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java b/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java new file mode 100644 index 00000000000..e4b12cec808 --- /dev/null +++ b/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java @@ -0,0 +1,115 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.autofill; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.ViewGroup; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.content_public.browser.WebContents; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.display.DisplayAndroid; + +import java.util.ArrayList; + +/** + * The unit tests for AutofillProvider. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class AutofillProviderTest { + private static final float EXPECTED_DIP_SCALE = 2; + private static final int SCROLL_X = 15; + private static final int SCROLL_Y = 155; + private static final int LOCATION_X = 25; + private static final int LOCATION_Y = 255; + + private Context mContext; + private WindowAndroid mWindowAndroid; + private WebContents mWebContents; + private ViewGroup mContainerView; + private AutofillProvider mAutofillProvider; + private DisplayAndroid mDisplayAndroid; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = Mockito.mock(Context.class); + mWindowAndroid = Mockito.mock(WindowAndroid.class); + mDisplayAndroid = Mockito.mock(DisplayAndroid.class); + mWebContents = Mockito.mock(WebContents.class); + mContainerView = Mockito.mock(ViewGroup.class); + mAutofillProvider = new AutofillProvider(mContext, mContainerView, "AutofillProviderTest"); + mAutofillProvider.setWebContents(mWebContents); + + when(mWebContents.getTopLevelNativeWindow()).thenReturn(mWindowAndroid); + when(mWindowAndroid.getDisplay()).thenReturn(mDisplayAndroid); + when(mDisplayAndroid.getDipScale()).thenReturn(EXPECTED_DIP_SCALE); + when(mContainerView.getScrollX()).thenReturn(SCROLL_X); + when(mContainerView.getScrollY()).thenReturn(SCROLL_Y); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + int[] location = (int[]) args[0]; + location[0] = LOCATION_X; + location[1] = LOCATION_Y; + return null; + } + }) + .when(mContainerView) + .getLocationOnScreen(ArgumentMatchers.any()); + } + + @Test + public void testTransformFormFieldToContainViewCoordinates() { + ArrayList fields = new ArrayList(1); + fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, + null, null, null, false, false, 0, null, 10 /* left */, 20 /* top */, + 300 /* right */, 60 /*bottom*/, null, null)); + fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, + null, null, null, false, false, 0, null, 20 /* left */, 100 /* top */, + 400 /* right */, 200 /*bottom*/, null, null)); + FormData formData = new FormData(null, null, fields); + mAutofillProvider.transformFormFieldToContainViewCoordinates(formData); + RectF result = formData.mFields.get(0).getBoundsInContainerViewCoordinates(); + assertEquals(10 * EXPECTED_DIP_SCALE + SCROLL_X, result.left, 0); + assertEquals(20 * EXPECTED_DIP_SCALE + SCROLL_Y, result.top, 0); + assertEquals(300 * EXPECTED_DIP_SCALE + SCROLL_X, result.right, 0); + assertEquals(60 * EXPECTED_DIP_SCALE + SCROLL_Y, result.bottom, 0); + + result = formData.mFields.get(1).getBoundsInContainerViewCoordinates(); + assertEquals(20 * EXPECTED_DIP_SCALE + SCROLL_X, result.left, 0); + assertEquals(100 * EXPECTED_DIP_SCALE + SCROLL_Y, result.top, 0); + assertEquals(400 * EXPECTED_DIP_SCALE + SCROLL_X, result.right, 0); + assertEquals(200 * EXPECTED_DIP_SCALE + SCROLL_Y, result.bottom, 0); + } + + @Test + public void testTransformToWindowBounds() { + RectF source = new RectF(10, 20, 300, 400); + Rect result = mAutofillProvider.transformToWindowBounds(source); + assertEquals(10 * EXPECTED_DIP_SCALE + LOCATION_X, result.left, 0); + assertEquals(20 * EXPECTED_DIP_SCALE + LOCATION_Y, result.top, 0); + assertEquals(300 * EXPECTED_DIP_SCALE + LOCATION_X, result.right, 0); + assertEquals(400 * EXPECTED_DIP_SCALE + LOCATION_Y, result.bottom, 0); + } +} diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index 7cf16fc27ea..ed248d98c85 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -288,17 +288,11 @@ void ContentAutofillDriver::DidEndTextFieldEditing() { autofill_handler_->OnDidEndTextFieldEditing(); } -void ContentAutofillDriver::SetDataList( - const std::vector& values, - const std::vector& labels) { - autofill_handler_->OnSetDataList(values, labels); -} - void ContentAutofillDriver::SelectFieldOptionsDidChange(const FormData& form) { autofill_handler_->SelectFieldOptionsDidChange(form); } -void ContentAutofillDriver::DidNavigateMainFrame( +void ContentAutofillDriver::DidNavigateFrame( content::NavigationHandle* navigation_handle) { if (navigation_handle->IsSameDocument()) return; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index 587b09f7e65..7d201fe0deb 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -115,13 +115,11 @@ class ContentAutofillDriver : public AutofillDriver, base::TimeTicks timestamp) override; void DidPreviewAutofillFormData() override; void DidEndTextFieldEditing() override; - void SetDataList(const std::vector& values, - const std::vector& labels) override; void SelectFieldOptionsDidChange(const FormData& form) override; - // Called when the main frame has navigated. Explicitely will not trigger for - // subframe navigations. See navigation_handle.h for details. - void DidNavigateMainFrame(content::NavigationHandle* navigation_handle); + // DidNavigateFrame() is called on the frame's driver, respectively, when a + // navigation occurs in that specific frame. + void DidNavigateFrame(content::NavigationHandle* navigation_handle); AutofillManager* autofill_manager() { return autofill_manager_; } AutofillHandler* autofill_handler() { return autofill_handler_.get(); } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc index 35e691af47c..2738209f51c 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc @@ -132,18 +132,13 @@ void ContentAutofillDriverFactory::RenderFrameDeleted( void ContentAutofillDriverFactory::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - // For the purposes of this code, a navigation is not important if it has not - // committed yet or if it's in a subframe. - if (!navigation_handle->HasCommitted() || - !navigation_handle->IsInMainFrame()) { - return; + if (navigation_handle->HasCommitted() && + (navigation_handle->IsInMainFrame() || + navigation_handle->HasSubframeNavigationEntryCommitted())) { + NavigationFinished(); + DriverForFrame(navigation_handle->GetRenderFrameHost()) + ->DidNavigateFrame(navigation_handle); } - - // A main frame navigation has occured. We suppress the autofill popup and - // tell the autofill driver. - NavigationFinished(); - DriverForFrame(navigation_handle->GetRenderFrameHost()) - ->DidNavigateMainFrame(navigation_handle); } void ContentAutofillDriverFactory::OnVisibilityChanged( 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 cc75354a844..335cde6bf11 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -282,7 +282,7 @@ class TestContentAutofillDriver : public ContentAutofillDriver { return static_cast(autofill_manager()); } - using ContentAutofillDriver::DidNavigateMainFrame; + using ContentAutofillDriver::DidNavigateFrame; }; class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { @@ -316,7 +316,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { content::MockNavigationHandle navigation_handle(GURL(), main_rfh()); navigation_handle.set_has_committed(true); navigation_handle.set_is_same_document(same_document); - driver_->DidNavigateMainFrame(&navigation_handle); + driver_->DidNavigateFrame(&navigation_handle); } protected: diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index 35475b3d8a0..b7aa9f45bb9 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -51,6 +51,7 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/rect.h" +#include "url/gurl.h" #if BUILDFLAG(ENABLE_PLUGINS) #include "content/public/browser/plugin_service.h" @@ -316,7 +317,7 @@ FingerprintDataLoader::FingerprintDataLoader( content::GetDeviceService().BindGeolocationContext( geolocation_context_.BindNewPipeAndPassReceiver()); geolocation_context_->BindGeolocation( - geolocation_.BindNewPipeAndPassReceiver()); + geolocation_.BindNewPipeAndPassReceiver(), GURL::EmptyGURL()); geolocation_->SetHighAccuracy(false); geolocation_->QueryNextPosition( base::BindOnce(&FingerprintDataLoader::OnGotGeoposition, diff --git a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom index 3e7e9c944a5..28b90f991f2 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom @@ -90,7 +90,7 @@ interface PasswordAutofillAgent { // Lets the renderer know that there are no saved credentials for filling. // This is the "no results" equivalent of FillPasswordForm. - InformNoSavedCredentials(); + InformNoSavedCredentials(bool should_show_popup_without_passwords); // Fills the given |credential| into the last focused text input. FillIntoFocusedField(bool is_password, mojo_base.mojom.String16 credential); diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom index 96fcfbf363b..1c9d27910e0 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom @@ -71,10 +71,6 @@ interface AutofillDriver { // Sent when a text field is done editing. DidEndTextFieldEditing(); - - // Informs browser of data list values for the current field. - SetDataList(array values, - array labels); }; // There is one instance of this interface per web contents in the browser diff --git a/chromium/components/autofill/content/renderer/BUILD.gn b/chromium/components/autofill/content/renderer/BUILD.gn index 82ce0f34d5a..6ee2bee980b 100644 --- a/chromium/components/autofill/content/renderer/BUILD.gn +++ b/chromium/components/autofill/content/renderer/BUILD.gn @@ -80,6 +80,7 @@ jumbo_static_library("test_support") { "//components/autofill/content/renderer", "//services/service_manager/public/cpp", "//skia", + "//testing/gmock", "//testing/gtest", "//third_party/blink/public:blink", ] diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 307fa136447..39e65c63370 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -86,6 +86,7 @@ using blink::WebVector; namespace autofill { +using form_util::ExtractMask; using form_util::FindFormAndFieldForFormControlElement; using form_util::UnownedCheckoutFormElementsAndFieldSetsToFormData; using mojom::SubmissionSource; @@ -98,32 +99,12 @@ namespace { // upon, instead of multiple in close succession (debounce time). size_t kWaitTimeForSelectOptionsChangesMs = 50; -// Gets all the data list values (with corresponding label) for the given -// element. -void GetDataListSuggestions(const WebInputElement& element, - std::vector* values, - std::vector* labels) { - for (const auto& option : element.FilteredDataListOptions()) { - values->push_back(option.Value().Utf16()); - if (option.Value() != option.Label()) - labels->push_back(option.Label().Utf16()); - else - labels->push_back(base::string16()); - } -} - -// Trim the vector before sending it to the browser process to ensure we -// don't send too much data through the IPC. -void TrimStringVectorForIPC(std::vector* strings) { - // Limit the size of the vector. - if (strings->size() > kMaxListSize) - strings->resize(kMaxListSize); - - // Limit the size of the strings in the vector. - for (size_t i = 0; i < strings->size(); ++i) { - if ((*strings)[i].length() > kMaxDataLength) - (*strings)[i].resize(kMaxDataLength); - } +// Helper function to return EXTRACT_DATALIST if kAutofillExtractAllDatalist is +// enabled, otherwise EXTRACT_NONE is returned. +ExtractMask GetExtractDatalistMask() { + return base::FeatureList::IsEnabled(features::kAutofillExtractAllDatalists) + ? form_util::EXTRACT_DATALIST + : form_util::EXTRACT_NONE; } } // namespace @@ -160,6 +141,9 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame, &AutofillAgent::BindPendingReceiver, base::Unretained(this))); } +// The destructor is not guaranteed to be called. Destruction happens (only) +// through the OnDestruct() event, which posts a task to delete this object. +// The process may be killed before this deletion can happen. AutofillAgent::~AutofillAgent() { RemoveFormObserver(this); } @@ -175,17 +159,13 @@ bool AutofillAgent::FormDataCompare::operator()(const FormData& lhs, std::tie(rhs.name, rhs.url, rhs.action, rhs.is_form_tag); } -void AutofillAgent::DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) { +void AutofillAgent::DidCommitProvisionalLoad(ui::PageTransition transition) { blink::WebFrame* frame = render_frame()->GetWebFrame(); // TODO(dvadym): check if we need to check if it is main frame navigation // http://crbug.com/443155 if (frame->Parent()) return; // Not a top-level navigation. - if (is_same_document_navigation) - return; - // Navigation to a new page or a page refresh. element_.Reset(); @@ -225,9 +205,11 @@ void AutofillAgent::DidChangeScrollOffsetImpl( FormData form; FormFieldData field; - if (FindFormAndFieldForFormControlElement(element_, field_data_manager_.get(), - form_util::EXTRACT_BOUNDS, &form, - &field)) { + if (FindFormAndFieldForFormControlElement( + element_, field_data_manager_.get(), + static_cast(form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), + &form, &field)) { GetAutofillDriver()->TextFieldDidScroll(form, field, field.bounds); } @@ -237,14 +219,6 @@ void AutofillAgent::DidChangeScrollOffsetImpl( void AutofillAgent::FocusedElementChanged(const WebElement& element) { was_focused_before_now_ = false; - - if ((IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) && - !element.IsNull() && - element.GetDocument().GetFrame()->HasTransientUserActivation()) { - focused_node_was_last_clicked_ = true; - HandleFocusChangeComplete(); - } - HidePopup(); if (element.IsNull()) { @@ -258,14 +232,30 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { const WebInputElement* input = ToWebInputElement(&element); + bool focus_moved_to_new_form = false; if (!last_interacted_form_.IsNull() && (!input || last_interacted_form_ != input->Form())) { // The focused element is not part of the last interacted form (could be // in a different form). GetAutofillDriver()->FocusNoLongerOnForm(); - return; + focus_moved_to_new_form = true; + } + + // Calls HandleFocusChangeComplete() after notifying the focus is no longer on + // the previous form, then early return. No need to notify the newly focused + // element because that will be done by HandleFocusChangeComplete() which + // triggers FormControlElementClicked(). + // Refer to http://crbug.com/1105254 + if ((IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) && + !element.IsNull() && + element.GetDocument().GetFrame()->HasTransientUserActivation()) { + focused_node_was_last_clicked_ = true; + HandleFocusChangeComplete(); } + if (focus_moved_to_new_form) + return; + if (!input || !input->IsEnabled() || input->IsReadOnly() || !input->IsTextField()) return; @@ -274,9 +264,11 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { FormData form; FormFieldData field; - if (FindFormAndFieldForFormControlElement(element_, field_data_manager_.get(), - form_util::EXTRACT_BOUNDS, &form, - &field)) { + if (FindFormAndFieldForFormControlElement( + element_, field_data_manager_.get(), + static_cast(form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), + &form, &field)) { GetAutofillDriver()->FocusOnFormField(form, field, field.bounds); } } @@ -355,9 +347,11 @@ void AutofillAgent::OnTextFieldDidChange(const WebInputElement& element) { FormData form; FormFieldData field; - if (FindFormAndFieldForFormControlElement(element, field_data_manager_.get(), - form_util::EXTRACT_BOUNDS, &form, - &field)) { + if (FindFormAndFieldForFormControlElement( + element, field_data_manager_.get(), + static_cast(form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), + &form, &field)) { GetAutofillDriver()->TextFieldDidChange(form, field, field.bounds, AutofillTickClock::NowTicks()); } @@ -616,9 +610,8 @@ bool AutofillAgent::CollectFormlessElements(FormData* output) { if (control_elements.size() > kMaxParseableFields) return false; - const form_util::ExtractMask extract_mask = - static_cast(form_util::EXTRACT_VALUE | - form_util::EXTRACT_OPTIONS); + const ExtractMask extract_mask = static_cast( + form_util::EXTRACT_VALUE | form_util::EXTRACT_OPTIONS); return UnownedCheckoutFormElementsAndFieldSetsToFormData( fieldsets, control_elements, nullptr, document, field_data_manager_.get(), @@ -784,15 +777,18 @@ void AutofillAgent::QueryAutofillSuggestions( FormData form; FormFieldData field; - if (!FindFormAndFieldForFormControlElement(element, field_data_manager_.get(), - form_util::EXTRACT_BOUNDS, &form, - &field)) { + if (!FindFormAndFieldForFormControlElement( + element, field_data_manager_.get(), + static_cast(form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), + &form, &field)) { // If we didn't find the cached form, at least let autocomplete have a shot // at providing suggestions. WebFormControlElementToFormField( element, nullptr, - static_cast(form_util::EXTRACT_VALUE | - form_util::EXTRACT_BOUNDS), + static_cast(form_util::EXTRACT_VALUE | + form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), &field); } @@ -803,20 +799,15 @@ void AutofillAgent::QueryAutofillSuggestions( return; } - std::vector data_list_values; - std::vector data_list_labels; - const WebInputElement* input_element = ToWebInputElement(&element); - if (input_element) { - // Find the datalist values and send them to the browser process. - GetDataListSuggestions(*input_element, &data_list_values, - &data_list_labels); - TrimStringVectorForIPC(&data_list_values); - TrimStringVectorForIPC(&data_list_labels); + if (!base::FeatureList::IsEnabled(features::kAutofillExtractAllDatalists)) { + if (const WebInputElement* input_element = ToWebInputElement(&element)) { + // Find the datalist values and send them to the browser process. + form_util::GetDataListSuggestions(*input_element, &field.datalist_values, + &field.datalist_labels); + } } is_popup_possibly_visible_ = true; - - GetAutofillDriver()->SetDataList(data_list_values, data_list_labels); GetAutofillDriver()->QueryFormFieldAutofill(autofill_query_id_, form, field, field.bounds, autoselect_first_suggestion); @@ -1041,7 +1032,9 @@ void AutofillAgent::OnProvisionallySaveForm( FormData form; FormFieldData field; if (FindFormAndFieldForFormControlElement( - element, field_data_manager_.get(), form_util::EXTRACT_BOUNDS, + element, field_data_manager_.get(), + static_cast(form_util::EXTRACT_BOUNDS | + GetExtractDatalistMask()), &form, &field)) { GetAutofillDriver()->SelectControlDidChange(form, field, field.bounds); } diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index b0e7943afb2..be2b158814f 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -168,8 +168,7 @@ class AutofillAgent : public content::RenderFrameObserver, }; // content::RenderFrameObserver: - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; void DidFinishDocumentLoad() override; void DidChangeScrollOffset() override; void FocusedElementChanged(const blink::WebElement& element) override; diff --git a/chromium/components/autofill/content/renderer/focus_test_utils.h b/chromium/components/autofill/content/renderer/focus_test_utils.h index a20563153b8..038e8fcedb0 100644 --- a/chromium/components/autofill/content/renderer/focus_test_utils.h +++ b/chromium/components/autofill/content/renderer/focus_test_utils.h @@ -5,9 +5,9 @@ #ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_FOCUS_TEST_UTILS_H_ #define COMPONENTS_AUTOFILL_CONTENT_RENDERER_FOCUS_TEST_UTILS_H_ -#include "base/callback.h" -#include "string" +#include +#include "base/callback.h" #include "third_party/blink/public/web/web_document.h" namespace autofill { diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index c1f47cb9575..35f247f215c 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -1470,11 +1470,6 @@ bool UnownedFormElementsAndFieldSetsToFormData( FormData* form, FormFieldData* field) { form->url = GetCanonicalOriginForDocument(document); - if (IsAutofillFieldMetadataEnabled() && !document.Body().IsNull()) { - SCOPED_UMA_HISTOGRAM_TIMER( - "PasswordManager.ButtonTitlePerformance.NoFormTag"); - form->button_titles = InferButtonTitlesForForm(document.Body()); - } if (document.GetFrame() && document.GetFrame()->Top()) { form->main_frame_origin = document.GetFrame()->Top()->GetSecurityOrigin(); } else { @@ -1514,6 +1509,20 @@ bool ScriptModifiedUsernameAcceptable( return field_data_manager->FindMachedValue(value); } +// Trim the vector before sending it to the browser process to ensure we +// don't send too much data through the IPC. +void TrimStringVectorForIPC(std::vector* strings) { + // Limit the size of the vector. + if (strings->size() > kMaxListSize) + strings->resize(kMaxListSize); + + // Limit the size of the strings in the vector. + for (auto& string : *strings) { + if (string.length() > kMaxDataLength) + string.resize(kMaxDataLength); + } +} + // Helper function that strips any authentication data, as well as query and // ref portions of URL. GURL StripAuthAndParams(const GURL& gurl) { @@ -1527,6 +1536,20 @@ GURL StripAuthAndParams(const GURL& gurl) { } // namespace +void GetDataListSuggestions(const WebInputElement& element, + std::vector* values, + std::vector* labels) { + for (const auto& option : element.FilteredDataListOptions()) { + values->push_back(option.Value().Utf16()); + if (option.Value() != option.Label()) + labels->push_back(option.Label().Utf16()); + else + labels->push_back(base::string16()); + } + TrimStringVectorForIPC(values); + TrimStringVectorForIPC(labels); +} + bool ExtractFormData(const WebFormElement& form_element, const FieldDataManager& field_data_manager, FormData* data) { @@ -1583,7 +1606,7 @@ GURL GetCanonicalOriginForDocument(const WebDocument& document) { return StripAuthAndParams(full_origin); } -GURL GetOriginWithoutAuthForDocument(const WebDocument& document) { +GURL GetDocumentUrlWithoutAuth(const WebDocument& document) { GURL::Replacements rep; rep.ClearUsername(); rep.ClearPassword(); @@ -1647,6 +1670,11 @@ base::string16 GetFormIdentifier(const WebFormElement& form) { return identifier; } +FormRendererId GetFormRendererId(const blink::WebFormElement& form) { + return form.IsNull() ? FormRendererId() + : FormRendererId(form.UniqueRendererFormId()); +} + base::i18n::TextDirection GetTextDirectionForElement( const blink::WebFormControlElement& element) { // Use 'text-align: left|right' if set or 'direction' otherwise. @@ -1766,6 +1794,12 @@ void WebFormControlElementToFormField( } } } + if (extract_mask & EXTRACT_DATALIST) { + if (auto* input = blink::ToWebInputElement(&element)) { + GetDataListSuggestions(*input, &field->datalist_values, + &field->datalist_labels); + } + } if (!(extract_mask & EXTRACT_VALUE)) return; @@ -1834,11 +1868,6 @@ bool WebFormElementToFormData( form->action = GetCanonicalActionForForm(form_element); form->is_action_empty = form_element.Action().IsNull() || form_element.Action().IsEmpty(); - if (IsAutofillFieldMetadataEnabled()) { - SCOPED_UMA_HISTOGRAM_TIMER( - "PasswordManager.ButtonTitlePerformance.HasFormTag"); - form->button_titles = InferButtonTitlesForForm(form_element); - } if (frame->Top()) { form->main_frame_origin = frame->Top()->GetSecurityOrigin(); } else { @@ -2179,6 +2208,41 @@ base::string16 FindChildText(const WebNode& node) { return FindChildTextWithIgnoreList(node, std::set()); } +ButtonTitleList GetButtonTitles(const WebFormElement& web_form, + const WebDocument& document, + ButtonTitlesCache* button_titles_cache) { + DCHECK(button_titles_cache); + if (!IsAutofillFieldMetadataEnabled() && web_form.IsNull()) + return ButtonTitleList(); + + // True if the cache has no entry for |web_form|. + bool cache_miss = true; + // Iterator pointing to the entry for |web_form| if the entry for |web_form| + // is found. + ButtonTitlesCache::iterator form_position; + std::tie(form_position, cache_miss) = button_titles_cache->emplace( + GetFormRendererId(web_form), ButtonTitleList()); + if (!cache_miss) + return form_position->second; + + ButtonTitleList button_titles; + DCHECK(!web_form.IsNull() || !document.IsNull()); + if (web_form.IsNull()) { + const WebElement& body = document.Body(); + if (!body.IsNull()) { + SCOPED_UMA_HISTOGRAM_TIMER( + "PasswordManager.ButtonTitlePerformance.NoFormTag"); + button_titles = InferButtonTitlesForForm(body); + } + } else { + SCOPED_UMA_HISTOGRAM_TIMER( + "PasswordManager.ButtonTitlePerformance.HasFormTag"); + button_titles = InferButtonTitlesForForm(web_form); + } + form_position->second = std::move(button_titles); + return form_position->second; +} + base::string16 FindChildTextWithIgnoreListForTesting( const WebNode& node, const std::set& divs_to_skip) { @@ -2192,10 +2256,6 @@ bool InferLabelForElementForTesting(const WebFormControlElement& element, return InferLabelForElement(element, stop_words, label, label_source); } -ButtonTitleList InferButtonTitlesForTesting(const WebElement& form_element) { - return InferButtonTitlesForForm(form_element); -} - WebFormElement FindFormByUniqueRendererId(WebDocument doc, FormRendererId form_renderer_id) { for (const auto& form : doc.Forms()) { diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h index 877d8aa285b..1a33e640255 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.h +++ b/chromium/components/autofill/content/renderer/form_autofill_util.h @@ -10,6 +10,7 @@ #include #include +#include "base/containers/flat_map.h" #include "base/i18n/rtl.h" #include "base/macros.h" #include "base/strings/string16.h" @@ -45,6 +46,10 @@ class FieldDataManager; namespace form_util { +// Mapping from a form element's render id to results of button titles +// heuristics for a given form element. +using ButtonTitlesCache = base::flat_map; + // A bit field mask to extract data from WebFormControlElement. // Copied to components/autofill/ios/browser/resources/autofill_controller.js. enum ExtractMask { @@ -59,8 +64,18 @@ enum ExtractMask { // WebFormControlElement. EXTRACT_BOUNDS = 1 << 3, // Extract bounds from WebFormControlElement, // could trigger layout if needed. + EXTRACT_DATALIST = 1 << 4, // Extract datalist from WebFormControlElement, + // the total number of options is up to + // kMaxListSize and each option has as far as + // kMaxDataLength. }; +// Gets up to kMaxListSize data list values (with corresponding label) for the +// given element, each value and label have as far as kMaxDataLength. +void GetDataListSuggestions(const blink::WebInputElement& element, + std::vector* values, + std::vector* labels); + // Extract FormData from the form element and return whether the operation was // successful. bool ExtractFormData(const blink::WebFormElement& form_element, @@ -89,7 +104,7 @@ bool AreFormContentsVisible(const blink::WebFormElement& form); // strip unnecessary data (e.g. query params and HTTP credentials). GURL GetCanonicalActionForForm(const blink::WebFormElement& form); GURL GetCanonicalOriginForDocument(const blink::WebDocument& document); -GURL GetOriginWithoutAuthForDocument(const blink::WebDocument& document); +GURL GetDocumentUrlWithoutAuth(const blink::WebDocument& document); // Returns true if |element| is a month input element. bool IsMonthInput(const blink::WebInputElement* element); @@ -123,6 +138,10 @@ bool IsWebElementVisible(const blink::WebElement& element); // attribute. base::string16 GetFormIdentifier(const blink::WebFormElement& form); +// Returns the |unique_renderer_id| of a given |WebFormElement|. If +// |WebFormElement::IsNull()|, returns a null renderer ID. +FormRendererId GetFormRendererId(const blink::WebFormElement& form); + // Returns text alignment for |element|. base::i18n::TextDirection GetTextDirectionForElement( const blink::WebFormControlElement& element); @@ -273,6 +292,15 @@ void PreviewSuggestion(const base::string16& suggestion, // Whitespace is trimmed from text accumulated at descendant nodes. base::string16 FindChildText(const blink::WebNode& node); +// Returns the button titles for |web_form| (or unowned buttons in |document| if +// |web_form| is null). |button_titles_cache| can be used to spare recomputation +// if called multiple times for the same form. Button titles computation for +// unowned buttons is enabled only in Dev and Canary (crbug.com/1086446), +// otherwise the method returns an empty list. +ButtonTitleList GetButtonTitles(const blink::WebFormElement& web_form, + const blink::WebDocument& document, + ButtonTitlesCache* button_titles_cache); + // Exposed for testing purpose base::string16 FindChildTextWithIgnoreListForTesting( const blink::WebNode& node, @@ -281,8 +309,6 @@ bool InferLabelForElementForTesting(const blink::WebFormControlElement& element, const std::vector& stop_words, base::string16* label, FormFieldData::LabelSource* label_source); -ButtonTitleList InferButtonTitlesForTesting( - const blink::WebElement& form_element); // Returns form by unique renderer id. Return null element if there is no form // with given form renderer id. diff --git a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc index 9693e877d2f..34575e77b47 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc @@ -4,12 +4,15 @@ #include "components/autofill/content/renderer/form_autofill_util.h" +#include "base/metrics/field_trial.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "components/autofill/core/common/renderer_id.h" #include "content/public/test/render_view_test.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_vector.h" @@ -138,6 +141,14 @@ const char kDivTableExample6[] = // TODO(crbug.com/796918): Should be "label" or "label-" const char kDivTableExample6Expected[] = ""; +void VerifyButtonTitleCache(const WebFormElement& form_target, + const ButtonTitleList& expected_button_titles, + const ButtonTitlesCache& actual_cache) { + EXPECT_THAT(actual_cache, + testing::ElementsAre(testing::Pair(GetFormRendererId(form_target), + expected_button_titles))); +} + class FormAutofillUtilsTest : public content::RenderViewTest { public: FormAutofillUtilsTest() {} @@ -272,8 +283,8 @@ TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) { } } -TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) { - const char kHtml[] = +TEST_F(FormAutofillUtilsTest, GetButtonTitles) { + constexpr char kHtml[] = "
" " " " " @@ -294,8 +305,11 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) { ASSERT_FALSE(target.IsNull()); const WebFormElement& form_target = target.ToConst(); ASSERT_FALSE(form_target.IsNull()); + ButtonTitlesCache cache; + + autofill::ButtonTitleList actual = + GetButtonTitles(form_target, web_frame->GetDocument(), &cache); - autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target); autofill::ButtonTitleList expected = { {base::UTF8ToUTF16("Clear field"), ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE}, @@ -309,9 +323,11 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) { {base::UTF8ToUTF16("Join"), ButtonTitleType::DIV}, {base::UTF8ToUTF16("Start"), ButtonTitleType::SPAN}}; EXPECT_EQ(expected, actual); + + VerifyButtonTitleCache(form_target, expected, cache); } -TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) { +TEST_F(FormAutofillUtilsTest, GetButtonTitles_TooLongTitle) { std::string title; for (int i = 0; i < 300; ++i) title += "a"; @@ -330,8 +346,10 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) { ASSERT_FALSE(target.IsNull()); const WebFormElement& form_target = target.ToConst(); ASSERT_FALSE(form_target.IsNull()); + ButtonTitlesCache cache; - autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target); + autofill::ButtonTitleList actual = + GetButtonTitles(form_target, web_frame->GetDocument(), &cache); int total_length = 0; for (auto title : actual) { @@ -341,8 +359,14 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) { EXPECT_EQ(200, total_length); } -TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) { - const char kNoFormHtml[] = +TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless) { + // Button titles computation and crowdsourcing for less forms are + // enabled only if |AutofillFieldMetadata| (Dev and Canary) is enabled. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.Init(); + base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Enabled"); + + constexpr char kNoFormHtml[] = "
" " " " " @@ -358,10 +382,12 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) { LoadHTML(kNoFormHtml); WebLocalFrame* web_frame = GetMainFrame(); ASSERT_NE(nullptr, web_frame); - const WebElement& body = web_frame->GetDocument().Body(); - ASSERT_FALSE(body.IsNull()); + WebFormElement form_target; + ASSERT_FALSE(web_frame->GetDocument().Body().IsNull()); + ButtonTitlesCache cache; - autofill::ButtonTitleList actual = InferButtonTitlesForTesting(body); + autofill::ButtonTitleList actual = + GetButtonTitles(form_target, web_frame->GetDocument(), &cache); autofill::ButtonTitleList expected = { {base::UTF8ToUTF16("Show password"), ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE}, @@ -370,6 +396,42 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) { {base::UTF8ToUTF16("Register"), ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE}}; EXPECT_EQ(expected, actual); + + VerifyButtonTitleCache(form_target, expected, cache); +} + +TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless_DisabledByDefault) { + // Button titles computation and crowdsourcing for less forms should be + // disabled if |AutofillFieldMetadata| is disabled. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.Init(); + base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Disabled"); + + constexpr char kNoFormHtml[] = + "
" + " " + " " + " " + "
" + "" + " " + " " + " " + "
Ignore this
" + ""; + + LoadHTML(kNoFormHtml); + WebLocalFrame* web_frame = GetMainFrame(); + ASSERT_NE(nullptr, web_frame); + WebFormElement form_target; + ASSERT_FALSE(web_frame->GetDocument().Body().IsNull()); + ButtonTitlesCache cache; + + autofill::ButtonTitleList actual = + GetButtonTitles(form_target, web_frame->GetDocument(), &cache); + + EXPECT_TRUE(actual.empty()); + EXPECT_TRUE(cache.empty()); } TEST_F(FormAutofillUtilsTest, IsEnabled) { @@ -788,6 +850,84 @@ TEST_F(FormAutofillUtilsTest, ExtractUnownedBounds) { EXPECT_FALSE(form_data.fields.back().bounds.IsEmpty()); } +TEST_F(FormAutofillUtilsTest, GetDataListSuggestions) { + LoadHTML( + ""); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_control = doc.GetElementById("i1").To(); + std::vector values; + std::vector labels; + GetDataListSuggestions(web_control, &values, &labels); + ASSERT_EQ(values.size(), 2u); + ASSERT_EQ(labels.size(), 2u); + EXPECT_EQ(values[0], base::UTF8ToUTF16("1")); + EXPECT_EQ(values[1], base::UTF8ToUTF16("2")); + EXPECT_EQ(labels[0], base::UTF8ToUTF16("")); + EXPECT_EQ(labels[1], base::UTF8ToUTF16("")); +} + +TEST_F(FormAutofillUtilsTest, GetDataListSuggestionsWithLabels) { + LoadHTML( + ""); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_control = doc.GetElementById("i1").To(); + std::vector values; + std::vector labels; + GetDataListSuggestions(web_control, &values, &labels); + ASSERT_EQ(values.size(), 2u); + ASSERT_EQ(labels.size(), 2u); + EXPECT_EQ(values[0], base::UTF8ToUTF16("1")); + EXPECT_EQ(values[1], base::UTF8ToUTF16("2")); + EXPECT_EQ(labels[0], base::UTF8ToUTF16("one")); + EXPECT_EQ(labels[1], base::UTF8ToUTF16("two")); +} + +TEST_F(FormAutofillUtilsTest, ExtractDataList) { + LoadHTML( + ""); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_control = doc.GetElementById("i1").To(); + FormData form_data; + FormFieldData form_field_data; + ASSERT_TRUE(FindFormAndFieldForFormControlElement( + web_control, nullptr /*field_data_manager*/, EXTRACT_DATALIST, &form_data, + &form_field_data)); + + auto& values = form_data.fields.back().datalist_values; + auto& labels = form_data.fields.back().datalist_labels; + ASSERT_EQ(values.size(), 2u); + ASSERT_EQ(labels.size(), 2u); + EXPECT_EQ(values[0], base::UTF8ToUTF16("1")); + EXPECT_EQ(values[1], base::UTF8ToUTF16("2")); + EXPECT_EQ(labels[0], base::UTF8ToUTF16("one")); + EXPECT_EQ(labels[1], base::UTF8ToUTF16("two")); + EXPECT_EQ(form_field_data.datalist_values, values); + EXPECT_EQ(form_field_data.datalist_labels, labels); +} + +TEST_F(FormAutofillUtilsTest, NotExtractDataList) { + LoadHTML( + ""); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_control = doc.GetElementById("i1").To(); + FormData form_data; + FormFieldData form_field_data; + ASSERT_TRUE(FindFormAndFieldForFormControlElement( + web_control, nullptr /*field_data_manager*/, &form_data, + &form_field_data)); + + EXPECT_TRUE(form_data.fields.back().datalist_values.empty()); + EXPECT_TRUE(form_data.fields.back().datalist_labels.empty()); +} + } // namespace } // namespace form_util } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc index 1bde0197a15..fe638091f41 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -18,6 +18,7 @@ #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/strings/string_number_conversions.h" #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/page_form_analyser_logger.h" #include "components/autofill/core/common/autofill_constants.h" @@ -420,37 +421,19 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, std::vector control_elements; - // First check the synthetic form. - bool found_synthetic_form = false; - if (form.data.SameFormAs(synthetic_form_)) { - found_synthetic_form = true; + if (form.data.unique_renderer_id.is_null()) { // Form is synthetic. WebDocument document = frame_->GetDocument(); control_elements = form_util::GetUnownedAutofillableFormFieldElements( document.All(), nullptr); - } - - if (!found_synthetic_form) { - // Find the real form by searching through the WebDocuments. - bool found_form = false; - + } else { for (const WebFormElement& form_element : frame_->GetDocument().Forms()) { - // To match two forms, we look for the form's name and the number of - // fields on that form. (Form names may not be unique.) - // Note: WebString() == WebString(string16()) does not evaluate to |true| - // -- WebKit distinguishes between a "null" string (lhs) and an "empty" - // string (rhs). We don't want that distinction, so forcing to string16. - base::string16 element_name = form_util::GetFormIdentifier(form_element); - if (element_name == form.data.name) { - found_form = true; + FormRendererId form_id(form_element.UniqueRendererFormId()); + if (form_id == form.data.unique_renderer_id) { control_elements = form_util::ExtractAutofillableElementsInForm(form_element); - if (control_elements.size() == form.fields.size()) - break; + break; } } - - if (!found_form) - return false; } if (control_elements.size() != form.fields.size()) { @@ -464,11 +447,9 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, WebFormControlElement& element = control_elements[i]; const FormFieldData& field_data = form.data.fields[i]; - if (element.NameForAutofill().Utf16() != field_data.name) { - // Keep things simple. Don't show predictions for elements whose names - // were modified between page load and the server's response to our query. + FieldRendererId field_id(element.UniqueRendererFormControlId()); + if (field_id != field_data.unique_renderer_id) continue; - } const FormFieldDataPredictions& field = form.fields[i]; // Possibly add a console warning for this field regarding the usage of @@ -492,6 +473,11 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, const base::string16 truncated_label = field_data.label.substr( 0, std::min(field_data.label.length(), kMaxLabelSize)); + std::string form_id = + base::NumberToString(form.data.unique_renderer_id.value()); + std::string field_id = + base::NumberToString(field.field.unique_renderer_id.value()); + std::string title = base::StrCat({"overall type: ", field.overall_type, // "\nserver type: ", field.server_type, // @@ -500,7 +486,9 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, "\nparseable name: ", field.parseable_name, // "\nsection: ", field.section, // "\nfield signature: ", field.signature, // - "\nform signature: ", form.signature}); + "\nform signature: ", form.signature, // + "\nform renderer id: ", form_id, // + "\nfield renderer id: ", field_id}); // Set this debug string to the title so that a developer can easily debug // by hovering the mouse over the input field. diff --git a/chromium/components/autofill/content/renderer/form_tracker.cc b/chromium/components/autofill/content/renderer/form_tracker.cc index e077f40a8b7..8613e8d3938 100644 --- a/chromium/components/autofill/content/renderer/form_tracker.cc +++ b/chromium/components/autofill/content/renderer/form_tracker.cc @@ -126,14 +126,13 @@ void FormTracker::FormControlDidChangeImpl( } } -void FormTracker::DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) { +void FormTracker::DidCommitProvisionalLoad(ui::PageTransition transition) { DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_); - if (!is_same_document_navigation) { - ResetLastInteractedElements(); - return; - } + ResetLastInteractedElements(); +} +void FormTracker::DidFinishSameDocumentNavigation() { + DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_); FireSubmissionIfFormDisappear(SubmissionSource::SAME_DOCUMENT_NAVIGATION); } diff --git a/chromium/components/autofill/content/renderer/form_tracker.h b/chromium/components/autofill/content/renderer/form_tracker.h index ed4e403ff26..1ac3a8a1272 100644 --- a/chromium/components/autofill/content/renderer/form_tracker.h +++ b/chromium/components/autofill/content/renderer/form_tracker.h @@ -87,8 +87,8 @@ class FormTracker : public content::RenderFrameObserver { FormSubmittedBySameDocumentNavigation); // content::RenderFrameObserver: - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; + void DidFinishSameDocumentNavigation() override; void DidStartNavigation( const GURL& url, base::Optional navigation_type) override; diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector.cc b/chromium/components/autofill/content/renderer/html_based_username_detector.cc index 9929ea02fd1..1006ccd24ab 100644 --- a/chromium/components/autofill/content/renderer/html_based_username_detector.cc +++ b/chromium/components/autofill/content/renderer/html_based_username_detector.cc @@ -284,13 +284,6 @@ void FindUsernameFieldInternal( } } -// Returns the |unique_renderer_id| of a given |WebFormElement|. If -// |WebFormElement::IsNull()| return a null renderer ID. -FormRendererId GetFormRendererId(WebFormElement form) { - return form.IsNull() ? FormRendererId() - : FormRendererId(form.UniqueRendererFormId()); -} - } // namespace const std::vector& GetPredictionsFieldBasedOnHtmlAttributes( @@ -311,8 +304,8 @@ const std::vector& GetPredictionsFieldBasedOnHtmlAttributes( bool cache_miss = true; // Iterator pointing to the entry for |form| if the entry for |form| is found. UsernameDetectorCache::iterator form_position; - std::tie(form_position, cache_miss) = username_detector_cache->insert( - std::make_pair(GetFormRendererId(form), std::vector())); + std::tie(form_position, cache_miss) = username_detector_cache->emplace( + form_util::GetFormRendererId(form), std::vector()); if (cache_miss) { std::vector username_predictions; diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc b/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc new file mode 100644 index 00000000000..d39bca5d8cd --- /dev/null +++ b/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc @@ -0,0 +1,345 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/stringprintf.h" +#include "components/autofill/content/renderer/form_autofill_util.h" +#include "components/autofill/content/renderer/html_based_username_detector.h" +#include "content/public/test/render_view_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/web/web_document.h" +#include "third_party/blink/public/web/web_local_frame.h" + +using blink::WebElement; +using blink::WebFormControlElement; +using blink::WebFormElement; +using blink::WebLocalFrame; +using blink::WebString; + +namespace autofill { + +namespace { + +struct TextField { + const char* name; + const char* id; + const char* value; + const char* label; +}; + +constexpr char kTestForm[] = R"( +
+ + + + + + + + +
+)"; + +std::string GetFormHTML(const TextField& first_field, + const TextField& second_field) { + return base::StringPrintf( + kTestForm, first_field.id, first_field.label, first_field.name, + first_field.id, first_field.value, second_field.id, second_field.label, + second_field.name, second_field.id, second_field.value); +} + +class HtmlBasedUsernameDetectorTest : public content::RenderViewTest { + protected: + struct TestCase { + const TextField first_text_field_parameter; + const TextField second_text_field_parameter; + const WebString expected_username_id; + }; + + FormData LoadFormDataFromHtml(const std::string& html) { + LoadHTML(html.data()); + const WebFormElement& form = GetFormElement(); + return GetFormDataFromForm(form); + } + + FormData GetFormDataFromForm(const WebFormElement& form) { + FormData form_data; + EXPECT_TRUE(form_util::WebFormElementToFormData( + form, WebFormControlElement(), nullptr, form_util::EXTRACT_NONE, + &form_data, nullptr)); + + return form_data; + } + + FieldRendererId GetRendererIdFromWebElementId(const WebString& id) { + const WebLocalFrame* frame = GetMainFrame(); + const WebElement& element = frame->GetDocument().GetElementById(id); + EXPECT_FALSE(element.IsNull()); + return FieldRendererId(element.ToConst() + .UniqueRendererFormControlId()); + } + + WebFormElement GetFormElement() { + const WebLocalFrame* frame = GetMainFrame(); + const blink::WebVector& forms = + frame->GetDocument().Forms(); + EXPECT_EQ(1U, forms.size()); + EXPECT_FALSE(forms[0].IsNull()); + + return forms[0]; + } + + std::vector GetFormControlElements() { + const WebFormElement& form = GetFormElement(); + blink::WebVector control_elements = + form.GetFormControlElements(); + return control_elements.ReleaseVector(); + } + + void PredictAndCheckUsernameId(const std::string& html, + const WebString& expected_username_id) { + const FormData& form_data = LoadFormDataFromHtml(html); + const std::vector& control_elements = + GetFormControlElements(); + + // Get the expected renderer id from the expected username id. + const FieldRendererId expected_renderer_id = + GetRendererIdFromWebElementId(expected_username_id); + + // Run predictions and test the result. + UsernameDetectorCache cache; + const std::vector& renderer_ids = + GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data, + &cache); + + ASSERT_EQ(1u, cache.size()); + ASSERT_FALSE(cache.begin()->second.empty()); + EXPECT_EQ(expected_renderer_id, cache.begin()->second[0]); + ASSERT_FALSE(renderer_ids.empty()); + EXPECT_EQ(expected_renderer_id, renderer_ids[0]); + } +}; + +} // namespace + +TEST_F(HtmlBasedUsernameDetectorTest, DeveloperGroupAttributes) { + // Each test case consists of a set of parameters to be plugged into + // the TestCase struct, plus the corresponding expectations. The test data + // contains cases that are identified by HTML detector, and not by + // base heuristic. Thus, username field does not necessarely have to + // be right before password field. These tests basically check + // searching in developer group (i.e. name and id attribute, + // concatenated, with "$" guard in between). + const TestCase test_cases[] = { + // There are both field name and id. + {{"username", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // there is no field id. + {{"username", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // Upper or mixed case shouldn't matter. + {{"uSeRnAmE", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // Check removal of special characters. + {{"u1_s2-e3~r4/n5(a)6m#e", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // Check guard between field name and field id. + {{"us", "ername", "johnsmith"}, {"email", "id", "js@google.com"}, "id"}, + // Check removal of fields with latin negative words in developer group. + {{"email", "x", "js@google.com"}, + {"fake_username", "y", "johnsmith"}, + "x"}, + {{"email", "mail", "js@google.com"}, + {"user_name", "fullname", "johnsmith"}, + "mail"}, + // Identify latin translations of "username". + {{"benutzername", "x", "johnsmith"}, + {"email", "y", "js@google.com"}, + "x"}, + // Identify latin translations of "user". + {{"utilizator", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // Identify technical words. + {{"loginid", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "x1d"}, + // Identify weak words. + {{"usrname", "x1d", "johnsmith"}, + {"email", "y1d", "js@google.com"}, + "y1d"}, + // If a word matches in maximum 2 fields, it is accepted. + // First encounter is selected as username. + {{"username", "x1d", "johnsmith"}, + {"repeat_username", "y1d", "johnsmith"}, + "x1d"}, + // A short word should be enclosed between delimiters. Otherwise, an + // Occurrence doesn't count. + {{"identity_name", "idn", "johnsmith"}, {"id", "xid", "123"}, "xid"}}; + + for (size_t i = 0; i < base::size(test_cases); ++i) { + SCOPED_TRACE(testing::Message() << "Iteration " << i); + + const std::string& form_html = + GetFormHTML(test_cases[i].first_text_field_parameter, + test_cases[i].second_text_field_parameter); + + PredictAndCheckUsernameId(form_html, test_cases[i].expected_username_id); + } +} + +TEST_F(HtmlBasedUsernameDetectorTest, UserGroupAttributes) { + // Each test case consists of a set of parameters to be plugged into + // the TestCase struct, plus the corresponding expectations. The test data + // contains cases that are identified by HTML detector, and not by + // base heuristic. Thus, username field does not necessarely have to + // be right before password field. These tests basically check + // searching in user group + const TestCase test_cases[] = { + // Label information will decide username. + {{"name1", "id1", "johnsmith", "Username:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Placeholder information will decide username. + {{"name1", "id1", "js@google.com", "Email:"}, + {"name2", "id2", "johnsmith", "Username:"}, + "id2"}, + // Check removal of special characters. + {{"name1", "id1", "johnsmith", "U s er n a m e:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Check removal of fields with latin negative words in user group. + {{"name1", "id1", "johnsmith", "Username password:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id2"}, + // Check removal of fields with non-latin negative words in user group. + {{"name1", "id1", "js@google.com", "Email:"}, + {"name2", "id2", "johnsmith", "የይለፍቃልየይለፍቃል:"}, + "id1"}, + // Identify latin translations of "username". + {{"name1", "id1", "johnsmith", "Username:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Identify non-latin translations of "username". + {{"name1", "id1", "johnsmith", "用户名:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Identify latin translations of "user". + {{"name1", "id1", "johnsmith", "Wosuta:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Identify non-latin translations of "user". + {{"name1", "id1", "johnsmith", "истифода:"}, + {"name2", "id2", "js@google.com", "Email:"}, + "id1"}, + // Identify weak words. + {{"name1", "id1", "johnsmith", "Insert your login details:"}, + {"name2", "id2", "js@google.com", "Insert your email:"}, + "id1"}, + // Check user group priority, compared to developer group. + // User group should have higher priority than developer group. + {{"email", "id1", "js@google.com", "Username:"}, + {"username", "id2", "johnsmith", "Email:"}, + "id1"}, + // Check treatment for short dictionary words. "uid" has higher priority, + // but its occurrence is ignored because it is a part of another word. + { + {"name1", "noword", "johnsmith", "Insert your id:"}, + {"name2", "uidentical", "js@google.com", "Insert something:"}, + "noword", + }}; + + for (size_t i = 0; i < base::size(test_cases); ++i) { + SCOPED_TRACE(testing::Message() << "Iteration " << i); + + const std::string& form_html = + GetFormHTML(test_cases[i].first_text_field_parameter, + test_cases[i].second_text_field_parameter); + + PredictAndCheckUsernameId(form_html, test_cases[i].expected_username_id); + } +} + +TEST_F(HtmlBasedUsernameDetectorTest, SeveralDetections) { + // If word matches in more than 2 fields, we don't match on it. + // We search for match with another word. + const std::string& test_form = R"( +
+ + + + + +
+ )"; + PredictAndCheckUsernameId(test_form, "yuser"); +} + +TEST_F(HtmlBasedUsernameDetectorTest, HTMLDetectorCache) { + const TextField text_fields[] = { + {"unknown", "12345"}, + {"something", "smith"}, + }; + + const std::string& form_html = GetFormHTML(text_fields[0], text_fields[1]); + + FormData form_data = LoadFormDataFromHtml(form_html); + std::vector control_elements = + GetFormControlElements(); + + UsernameDetectorCache cache; + std::vector field_ids = + GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data, + &cache); + + // No signals from HTML attributes. The classifier found nothing and cached + // it. + ASSERT_EQ(1u, cache.size()); + EXPECT_TRUE(field_ids.empty()); + const WebFormElement& form = GetFormElement(); + EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first); + EXPECT_TRUE(cache.begin()->second.empty()); + + // Changing attributes would change the classifier's output. But the output + // will be the same because it was cached in |username_detector_cache|. + control_elements[0].SetAttribute("name", "id"); + form_data = GetFormDataFromForm(GetFormElement()); + field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements, + form_data, &cache); + ASSERT_EQ(1u, cache.size()); + EXPECT_TRUE(field_ids.empty()); + EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first); + EXPECT_TRUE(cache.begin()->second.empty()); + + // Clear the cache. The classifier will find username field and cache it. + cache.clear(); + ASSERT_EQ(4u, control_elements.size()); + field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements, + form_data, &cache); + ASSERT_EQ(1u, cache.size()); + EXPECT_EQ(1u, field_ids.size()); + EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first); + ASSERT_EQ(1u, cache.begin()->second.size()); + EXPECT_EQ(FieldRendererId(control_elements[0].UniqueRendererFormControlId()), + cache.begin()->second[0]); + + // Change the attributes again ("username" is stronger signal than "id"), + // but keep the cache. The classifier's output should be the same. + control_elements[1].SetAttribute("name", "username"); + form_data = GetFormDataFromForm(GetFormElement()); + field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements, + form_data, &cache); + + ASSERT_EQ(1u, cache.size()); + EXPECT_EQ(1u, field_ids.size()); + EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first); + ASSERT_EQ(1u, cache.begin()->second.size()); + EXPECT_EQ(FieldRendererId(control_elements[0].UniqueRendererFormControlId()), + cache.begin()->second[0]); +} + +} // namespace autofill diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index d85705681a9..4d283f72a0f 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -24,7 +24,6 @@ #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" #include "components/autofill/content/renderer/password_generation_agent.h" #include "components/autofill/content/renderer/prefilled_values_detector.h" @@ -36,7 +35,6 @@ #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/signatures.h" -#include "components/password_manager/core/common/password_manager_features.h" #include "components/safe_browsing/buildflags.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/render_frame.h" @@ -257,9 +255,9 @@ WebString GetFormSignatureAsWebString(const FormData& form_data) { // Annotate |fields| with field signatures and form signature as HTML // attributes. void AnnotateFieldsWithSignatures( - std::vector* fields, + std::vector& fields, const blink::WebString& form_signature) { - for (blink::WebFormControlElement& control_element : *fields) { + for (blink::WebFormControlElement& control_element : fields) { FieldSignature field_signature = CalculateFieldSignatureByNameAndType( control_element.NameForAutofill().Utf16(), control_element.FormControlTypeForAutofill().Utf8()); @@ -272,37 +270,6 @@ void AnnotateFieldsWithSignatures( } } -// Annotate |forms| and all fields in the |frame| with form and field signatures -// as HTML attributes. -void AnnotateFormsAndFieldsWithSignatures(WebLocalFrame* frame, - WebVector* forms) { - for (WebFormElement& form : *forms) { - std::unique_ptr form_data( - CreateFormDataFromWebForm(form, /*field_data_manager=*/nullptr, - /*username_detector_cache=*/nullptr)); - WebString form_signature; - if (form_data) { - form_signature = GetFormSignatureAsWebString(*form_data); - form.SetAttribute(WebString::FromASCII(kDebugAttributeForFormSignature), - form_signature); - } - std::vector form_fields = - form_util::ExtractAutofillableElementsInForm(form); - AnnotateFieldsWithSignatures(&form_fields, form_signature); - } - - std::vector unowned_elements = - form_util::GetUnownedAutofillableFormFieldElements( - frame->GetDocument().All(), nullptr); - std::unique_ptr form_data(CreateFormDataFromUnownedInputElements( - *frame, /*field_data_manager=*/nullptr, - /*username_detector_cache=*/nullptr)); - WebString form_signature; - if (form_data) - form_signature = GetFormSignatureAsWebString(*form_data); - AnnotateFieldsWithSignatures(&unowned_elements, form_signature); -} - // Returns true iff there is a password field in |frame|. bool HasPasswordField(const WebLocalFrame& frame) { static base::NoDestructor kPassword("password"); @@ -416,14 +383,6 @@ bool HasDocumentWithValidFrame(const WebInputElement& element) { return frame && frame->View(); } -bool ShowPopupWithoutPasswords(const WebInputElement& password_element) { - if (!base::FeatureList::IsEnabled( - password_manager::features::kEnablePasswordsAccountStorage)) { - return false; - } - return !password_element.IsNull() && IsElementEditable(password_element); -} - // This method tries to fix `fields` with empty typed or filled properties by // matching them against previously filled or typed in fields with the same // value and copying their filled or typed mask. @@ -905,9 +864,10 @@ bool PasswordAutofillAgent::ShowSuggestions( FindPasswordInfoForElement(element, UseFallbackData(true), &username_element, &password_element, &password_info); - if (!password_info && !ShowPopupWithoutPasswords(password_element)) { + if (!password_info) { MaybeCheckSafeBrowsingReputation(element); - return false; + if (!CanShowPopupWithoutPasswords(password_element)) + return false; } // Check that all fillable elements are editable. @@ -1015,6 +975,31 @@ void PasswordAutofillAgent::UserGestureObserved() { gatekeeper_.OnUserGesture(); } +void PasswordAutofillAgent::AnnotateFormsAndFieldsWithSignatures( + WebVector& forms) { + for (WebFormElement& form : forms) { + std::unique_ptr form_data = GetFormDataFromWebForm(form); + WebString form_signature; + if (form_data) { + form_signature = GetFormSignatureAsWebString(*form_data); + form.SetAttribute(WebString::FromASCII(kDebugAttributeForFormSignature), + form_signature); + } + std::vector form_fields = + form_util::ExtractAutofillableElementsInForm(form); + AnnotateFieldsWithSignatures(form_fields, form_signature); + } + + std::vector unowned_elements = + form_util::GetUnownedAutofillableFormFieldElements( + render_frame()->GetWebFrame()->GetDocument().All(), nullptr); + std::unique_ptr form_data = GetFormDataFromUnownedInputElements(); + WebString form_signature; + if (form_data) + form_signature = GetFormSignatureAsWebString(*form_data); + AnnotateFieldsWithSignatures(unowned_elements, form_signature); +} + void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { std::unique_ptr logger; if (logging_state_active_) { @@ -1046,7 +1031,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { WebVector forms = frame->GetDocument().Forms(); if (IsShowAutofillSignaturesEnabled()) - AnnotateFormsAndFieldsWithSignatures(frame, &forms); + AnnotateFormsAndFieldsWithSignatures(forms); if (logger) logger->LogNumber(Logger::STRING_NUMBER_OF_ALL_FORMS, forms.size()); @@ -1131,7 +1116,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { password_forms_data.back().url = form_util::GetCanonicalOriginForDocument(frame->GetDocument()); password_forms_data.back().full_url = - form_util::GetOriginWithoutAuthForDocument(frame->GetDocument()); + form_util::GetDocumentUrlWithoutAuth(frame->GetDocument()); } if (!password_forms_data.empty()) { sent_request_to_store_ = true; @@ -1161,12 +1146,9 @@ void PasswordAutofillAgent::DidFinishLoad() { } void PasswordAutofillAgent::DidCommitProvisionalLoad( - bool is_same_document_navigation, ui::PageTransition transition) { - if (!is_same_document_navigation) { - checked_safe_browsing_reputation_ = false; - recorded_first_filling_result_ = false; - } + checked_safe_browsing_reputation_ = false; + recorded_first_filling_result_ = false; } void PasswordAutofillAgent::OnFrameDetached() { @@ -1312,7 +1294,10 @@ void PasswordAutofillAgent::AnnotateFieldsWithParsingResult( "confirmation_password_element"); } -void PasswordAutofillAgent::InformNoSavedCredentials() { +void PasswordAutofillAgent::InformNoSavedCredentials( + bool should_show_popup_without_passwords) { + should_show_popup_without_passwords_ = should_show_popup_without_passwords; + autofilled_elements_cache_.clear(); // Clear the actual field values. @@ -1382,7 +1367,8 @@ void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) { std::unique_ptr PasswordAutofillAgent::GetFormDataFromWebForm( const WebFormElement& web_form) { return CreateFormDataFromWebForm(web_form, field_data_manager_.get(), - &username_detector_cache_); + &username_detector_cache_, + &button_titles_cache_); } std::unique_ptr @@ -1398,7 +1384,8 @@ PasswordAutofillAgent::GetFormDataFromUnownedInputElements() { if (!web_frame) return nullptr; return CreateFormDataFromUnownedInputElements( - *web_frame, field_data_manager_.get(), &username_detector_cache_); + *web_frame, field_data_manager_.get(), &username_detector_cache_, + &button_titles_cache_); } //////////////////////////////////////////////////////////////////////////////// @@ -1430,6 +1417,7 @@ void PasswordAutofillAgent::CleanupOnDocumentShutdown() { web_input_to_password_info_.clear(); password_to_username_.clear(); last_supplied_password_info_iter_ = web_input_to_password_info_.end(); + should_show_popup_without_passwords_ = false; browser_has_form_to_process_ = false; field_data_manager_.get()->ClearData(); username_autofill_state_ = WebAutofillState::kNotFilled; @@ -1535,7 +1523,7 @@ bool PasswordAutofillAgent::FillUserNameAndPassword( // This is a heuristic guess. If the credential is stored for // www.example.com, the username may be prefilled with "@example.com". std::string possible_email_domain = - GetRegistryControlledDomain(fill_data.origin); + GetRegistryControlledDomain(fill_data.url); prefilled_placeholder_username = !username_element.Value().IsEmpty() && @@ -1886,4 +1874,10 @@ void PasswordAutofillAgent::SetLastUpdatedFormAndField( : FieldRendererId(input.UniqueRendererFormControlId()); } +bool PasswordAutofillAgent::CanShowPopupWithoutPasswords( + const WebInputElement& password_element) const { + return should_show_popup_without_passwords_ && !password_element.IsNull() && + IsElementEditable(password_element); +} + } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h index 011425f9186..4852ec2a3b8 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -20,6 +20,7 @@ #include "components/autofill/content/common/mojom/autofill_driver.mojom.h" #include "components/autofill/content/renderer/autofill_agent.h" #include "components/autofill/content/renderer/field_data_manager.h" +#include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/form_tracker.h" #include "components/autofill/content/renderer/html_based_username_detector.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" @@ -99,10 +100,6 @@ enum class FillingResult { kMaxValue = kNoFillableElementsFound, }; -// Names of HTML attributes to show form and field signatures for debugging. -extern const char kDebugAttributeForFormSignature[]; -extern const char kDebugAttributeForFieldSignature[]; - class FieldDataManager; class RendererSavePasswordProgressLogger; class PasswordGenerationAgent; @@ -133,7 +130,8 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // mojom::PasswordAutofillAgent: void FillPasswordForm(const PasswordFormFillData& form_data) override; - void InformNoSavedCredentials() override; + void InformNoSavedCredentials( + bool should_show_popup_without_passwords) override; void FillIntoFocusedField(bool is_password, const base::string16& credential) override; void SetLoggingState(bool active) override; @@ -235,8 +233,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, void DidFinishLoad() override; void ReadyToCommitNavigation( blink::WebDocumentLoader* document_loader) override; - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; void OnDestruct() override; const scoped_refptr GetFieldDataManager() { @@ -345,6 +342,12 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, DISALLOW_COPY_AND_ASSIGN(PasswordValueGatekeeper); }; + // Annotate |forms| and all fields in the current frame with form and field + // signatures as HTML attributes. Used by + // chrome://flags/#enable-show-autofill-signatures only. + void AnnotateFormsAndFieldsWithSignatures( + blink::WebVector& forms); + // Scans the given frame for password forms and sends them up to the browser. // If |only_visible| is true, only forms visible in the layout are sent. void SendPasswordForms(bool only_visible); @@ -467,6 +470,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, void SetLastUpdatedFormAndField(const blink::WebFormElement& form, const blink::WebFormControlElement& input); + bool CanShowPopupWithoutPasswords( + const blink::WebInputElement& password_element) const; + // The logins we have filled so far with their associated info. WebInputToPasswordInfoMap web_input_to_password_info_; // A (sort-of) reverse map to |web_input_to_password_info_|. @@ -474,6 +480,8 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // The chronologically last insertion into |web_input_to_password_info_|. WebInputToPasswordInfoMap::iterator last_supplied_password_info_iter_; + bool should_show_popup_without_passwords_ = false; + // Map WebFormControlElement to the pair of: // 1) The most recent text that user typed or PasswordManager autofilled in // input elements. Used for storing username/password before JavaScript @@ -513,10 +521,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // Records the username typed before suggestions preview. base::string16 username_query_prefix_; - // The HTML based username detector's cache which maps form elements to - // username predictions. - UsernameDetectorCache username_detector_cache_; - // This notifier is used to avoid sending redundant messages to the password // manager driver mojo interface. FocusStateNotifier focus_state_notifier_; @@ -543,6 +547,14 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // structure. Replace FormData with a smaller structure. std::map forms_structure_cache_; + // The HTML based username detector's cache which maps form elements to + // username predictions. + UsernameDetectorCache username_detector_cache_; + + // Stores the mapping from a form element's ID to results of button titles + // heuristics for that form. + form_util::ButtonTitlesCache button_titles_cache_; + // Flag to prevent that multiple PasswordManager.FirstRendererFillingResult // UMA metrics are recorded per page load. This is reset on // DidCommitProvisionalLoad() but only for non-same-document-navigations. 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 a216162bcfd..93824cae0bd 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc @@ -9,7 +9,6 @@ #include "base/no_destructor.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" -#include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/html_based_username_detector.h" #include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/renderer_id.h" @@ -128,7 +127,8 @@ bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) { std::unique_ptr CreateFormDataFromWebForm( const WebFormElement& web_form, const FieldDataManager* field_data_manager, - UsernameDetectorCache* username_detector_cache) { + UsernameDetectorCache* username_detector_cache, + form_util::ButtonTitlesCache* button_titles_cache) { if (web_form.IsNull()) return nullptr; @@ -136,7 +136,7 @@ std::unique_ptr CreateFormDataFromWebForm( form_data->url = form_util::GetCanonicalOriginForDocument(web_form.GetDocument()); form_data->full_url = - form_util::GetOriginWithoutAuthForDocument(web_form.GetDocument()); + form_util::GetDocumentUrlWithoutAuth(web_form.GetDocument()); form_data->is_gaia_with_skip_save_password_form = IsGaiaWithSkipSavePasswordForm(web_form) || IsGaiaReauthenticationForm(web_form); @@ -153,6 +153,8 @@ std::unique_ptr CreateFormDataFromWebForm( } form_data->username_predictions = GetUsernamePredictions( control_elements.ReleaseVector(), *form_data, username_detector_cache); + form_data->button_titles = form_util::GetButtonTitles( + web_form, web_form.GetDocument(), button_titles_cache); return form_data; } @@ -160,7 +162,8 @@ std::unique_ptr CreateFormDataFromWebForm( std::unique_ptr CreateFormDataFromUnownedInputElements( const WebLocalFrame& frame, const FieldDataManager* field_data_manager, - UsernameDetectorCache* username_detector_cache) { + UsernameDetectorCache* username_detector_cache, + form_util::ButtonTitlesCache* button_titles_cache) { std::vector fieldsets; std::vector control_elements = form_util::GetUnownedFormFieldElements(frame.GetDocument().All(), @@ -179,9 +182,12 @@ std::unique_ptr CreateFormDataFromUnownedInputElements( form_data->url = form_util::GetCanonicalOriginForDocument(frame.GetDocument()); form_data->full_url = - form_util::GetOriginWithoutAuthForDocument(frame.GetDocument()); + form_util::GetDocumentUrlWithoutAuth(frame.GetDocument()); form_data->username_predictions = GetUsernamePredictions( control_elements, *form_data, username_detector_cache); + form_data->button_titles = form_util::GetButtonTitles( + WebFormElement(), frame.GetDocument(), button_titles_cache); + return form_data; } 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 8701d51e57e..07a2ad05095 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h @@ -12,6 +12,7 @@ #include #include "base/strings/string_piece.h" +#include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/html_based_username_detector.h" #include "components/autofill/core/common/password_form.h" #include "third_party/blink/public/platform/web_string.h" @@ -42,14 +43,16 @@ bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form); std::unique_ptr CreateFormDataFromWebForm( const blink::WebFormElement& web_form, const FieldDataManager* field_data_manager, - UsernameDetectorCache* username_detector_cache); + UsernameDetectorCache* username_detector_cache, + form_util::ButtonTitlesCache* button_titles_cache); // Same as CreateFormDataFromWebForm() but for input elements that are // not enclosed in
element. std::unique_ptr CreateFormDataFromUnownedInputElements( const blink::WebLocalFrame& frame, const FieldDataManager* field_data_manager, - UsernameDetectorCache* username_detector_cache); + UsernameDetectorCache* username_detector_cache, + form_util::ButtonTitlesCache* button_titles_cache); // The "Realm" for the sign-on. This is scheme, host, port. std::string GetSignOnRealm(const GURL& origin); diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index 959fa2d7388..a87d298ca38 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -160,10 +160,7 @@ void PasswordGenerationAgent::BindPendingReceiver( } void PasswordGenerationAgent::DidCommitProvisionalLoad( - bool is_same_document_navigation, ui::PageTransition transition) { - if (is_same_document_navigation) - return; // Update stats for main frame navigation. if (!render_frame()->GetWebFrame()->Parent()) { if (current_generation_item_) { diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h index 6baf4393658..2eeb0e3044e 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.h +++ b/chromium/components/autofill/content/renderer/password_generation_agent.h @@ -97,8 +97,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, struct GenerationItemInfo; // RenderFrameObserver: - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; void DidChangeScrollOffset() override; void OnDestruct() override; diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index d8de972ccdb..3696ebea775 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -169,6 +169,8 @@ jumbo_static_library("browser") { "metrics/form_event_logger_base.h", "metrics/form_events.h", "payments/account_info_getter.h", + "payments/autofill_offer_manager.cc", + "payments/autofill_offer_manager.h", "payments/autofill_wallet_model_type_controller.cc", "payments/autofill_wallet_model_type_controller.h", "payments/card_unmask_delegate.cc", @@ -501,6 +503,7 @@ jumbo_static_library("test_support") { sources += [ "payments/test_credit_card_fido_authenticator.cc", "payments/test_credit_card_fido_authenticator.h", + "payments/test_internal_authenticator.cc", "payments/test_internal_authenticator.h", ] @@ -718,6 +721,8 @@ if (use_libfuzzer) { # TODO(crbug.com/896313): Reduce the dependency on "browser". ":browser", "//base:base", + "//components/autofill/core/browser", + "//components/autofill/core/browser:test_support", "//components/autofill/core/browser/proto", "//components/autofill/core/common", "//third_party/libprotobuf-mutator", diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css index 4d4b7509401..36b5c68b20a 100644 --- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css @@ -175,8 +175,8 @@ html { display: none; } -.hide-AddressProfileFormImport.log-entry[scope='AddressProfileFormImport'], -.hide-AddressProfileFormImport.log-entry[scope='AddressProfileFormImport'] + hr { +.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'], +.hide-AddressProfileFormImport .log-entry[scope='AddressProfileFormImport'] + hr { display: none; } diff --git a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc index a47451ca2e5..a5b7a87d286 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc @@ -168,8 +168,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOff) { std::unique_ptr form_structure = CreateValidCreditCardForm(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_FALSE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -183,8 +183,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn) { // With valid input, the function extracts the credit card form properly. std::unique_ptr form_structure = CreateValidCreditCardForm(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -199,8 +199,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Secure) { form_structure->DetermineHeuristicTypes(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -217,8 +217,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_NotSecure) { form_structure->DetermineHeuristicTypes(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_FALSE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -233,8 +233,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Javascript) { form_structure->DetermineHeuristicTypes(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -249,8 +249,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_WeirdJs) { form_structure->DetermineHeuristicTypes(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -264,8 +264,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_EmptyAction) { form_structure->DetermineHeuristicTypes(); auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); } @@ -275,8 +275,8 @@ TEST_F(AutofillAssistantTest, ShowAssistForCreditCard_ValidCard_CancelCvc) { // Will extract the credit card form data. auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); // Create a valid card for the assist. @@ -297,8 +297,8 @@ TEST_F(AutofillAssistantTest, ShowAssistForCreditCard_ValidCard_SubmitCvc) { // Will extract the credit card form data. auto& form_structures = *autofill_manager_->mutable_form_structures(); - auto signature = form_structure->form_signature(); - form_structures[signature] = std::move(form_structure); + auto renderer_id = form_structure->unique_renderer_id(); + form_structures[renderer_id] = std::move(form_structure); EXPECT_TRUE(autofill_assistant_->CanShowCreditCardAssist()); // Create a valid card for the assist. diff --git a/chromium/components/autofill/core/browser/autofill_client.cc b/chromium/components/autofill/core/browser/autofill_client.cc index 3e56a59c33e..ac6e1eae560 100644 --- a/chromium/components/autofill/core/browser/autofill_client.cc +++ b/chromium/components/autofill/core/browser/autofill_client.cc @@ -4,10 +4,33 @@ #include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/ui/suggestion.h" #include "components/version_info/channel.h" namespace autofill { +AutofillClient::PopupOpenArgs::PopupOpenArgs() = default; +AutofillClient::PopupOpenArgs::PopupOpenArgs( + const gfx::RectF& element_bounds, + base::i18n::TextDirection text_direction, + std::vector suggestions, + AutoselectFirstSuggestion autoselect_first_suggestion, + PopupType popup_type) + : element_bounds(element_bounds), + text_direction(text_direction), + suggestions(std::move(suggestions)), + autoselect_first_suggestion(autoselect_first_suggestion), + popup_type(popup_type) {} +AutofillClient::PopupOpenArgs::PopupOpenArgs( + const AutofillClient::PopupOpenArgs&) = default; +AutofillClient::PopupOpenArgs::PopupOpenArgs(AutofillClient::PopupOpenArgs&&) = + default; +AutofillClient::PopupOpenArgs::~PopupOpenArgs() = default; +AutofillClient::PopupOpenArgs& AutofillClient::PopupOpenArgs::operator=( + const AutofillClient::PopupOpenArgs&) = default; +AutofillClient::PopupOpenArgs& AutofillClient::PopupOpenArgs::operator=( + AutofillClient::PopupOpenArgs&&) = default; + version_info::Channel AutofillClient::GetChannel() const { return version_info::Channel::UNKNOWN; } diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index b1cd8283f70..be85907d853 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -15,6 +15,7 @@ #include "base/i18n/rtl.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" +#include "base/util/type_safety/strong_alias.h" #include "base/values.h" #include "build/build_config.h" #include "components/autofill/core/browser/payments/legal_message_line.h" @@ -24,6 +25,7 @@ #include "components/security_state/core/security_state.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/base/window_open_disposition.h" +#include "ui/gfx/geometry/rect_f.h" #include "url/gurl.h" #if !defined(OS_IOS) @@ -36,10 +38,6 @@ namespace content { class RenderFrameHost; } -namespace gfx { -class RectF; -} - namespace signin { class IdentityManager; } @@ -177,6 +175,31 @@ class AutofillClient : public RiskDataLoader { bool show_prompt = false; }; + // Required arguments to create a dropdown showing autofill suggestions. + struct PopupOpenArgs { + using AutoselectFirstSuggestion = + ::util::StrongAlias; + + PopupOpenArgs(); + PopupOpenArgs(const gfx::RectF& element_bounds, + base::i18n::TextDirection text_direction, + std::vector suggestions, + AutoselectFirstSuggestion autoselect_first_suggestion, + PopupType popup_type); + PopupOpenArgs(const PopupOpenArgs&); + PopupOpenArgs(PopupOpenArgs&&); + ~PopupOpenArgs(); + PopupOpenArgs& operator=(const PopupOpenArgs&); + PopupOpenArgs& operator=(PopupOpenArgs&&); + + gfx::RectF element_bounds; + base::i18n::TextDirection text_direction = + base::i18n::TextDirection::UNKNOWN_DIRECTION; + std::vector suggestions; + AutoselectFirstSuggestion autoselect_first_suggestion{false}; + PopupType popup_type = PopupType::kUnspecified; + }; + // Callback to run after local credit card save is offered. Sends whether the // prompt was accepted, declined, or ignored in |user_decision|. typedef base::OnceCallback @@ -210,7 +233,7 @@ class AutofillClient : public RiskDataLoader { typedef base::RepeatingCallback WebauthnDialogCallback; - ~AutofillClient() override {} + ~AutofillClient() override = default; // Returns the channel for the installation. In branded builds, this will be // version_info::Channel::{STABLE,BETA,DEV,CANARY}. In unbranded builds, or @@ -414,11 +437,7 @@ class AutofillClient : public RiskDataLoader { // |identifiers| for the element at |element_bounds|. |delegate| will be // notified of popup events. virtual void ShowAutofillPopup( - const gfx::RectF& element_bounds, - base::i18n::TextDirection text_direction, - const std::vector& suggestions, - bool autoselect_first_suggestion, - PopupType popup_type, + const PopupOpenArgs& open_args, base::WeakPtr delegate) = 0; // Update the data list values shown by the Autofill popup, if visible. @@ -430,6 +449,12 @@ class AutofillClient : public RiskDataLoader { // |UpdatePopup| to update the open popup in-place. virtual void PinPopupView() = 0; + // The returned arguments allow to reopen the dropdown with + // |ShowAutofillPopup| even if the controller is destroyed temporarily. + // This function ensures that the element's bounds are transformed back to the + // screen space-independent bounds. + virtual PopupOpenArgs GetReopenPopupArgs() const = 0; + // Returns (not elided) suggestions currently held by the UI. virtual base::span GetPopupSuggestions() const = 0; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index 92847181730..1fe15d7faeb 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -27,7 +27,6 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_driver.h" #include "components/autofill/core/browser/autofill_metrics.h" -#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/logging/log_protobufs.h" #include "components/autofill/core/browser/proto/legacy_proto_bridge.h" @@ -587,7 +586,7 @@ bool GetAPIQueryPayload(const AutofillQueryContents& query, } // namespace struct AutofillDownloadManager::FormRequestData { - std::vector form_signatures; + FormAndFieldSignatures signatures; RequestType request_type; std::string payload; int num_attempts = 0; @@ -640,9 +639,8 @@ bool AutofillDownloadManager::StartQueryRequest( // Encode the query for the requested forms. AutofillQueryContents query; - FormRequestData request_data; - if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures, - &query)) { + FormAndFieldSignatures signatures; + if (!FormStructure::EncodeQueryRequest(forms, &query, &signatures)) { return false; } @@ -665,17 +663,27 @@ bool AutofillDownloadManager::StartQueryRequest( return false; } + FormRequestData request_data; + request_data.signatures = std::move(signatures); request_data.request_type = AutofillDownloadManager::REQUEST_QUERY; request_data.payload = std::move(payload); AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_SENT); std::string query_data; - if (CheckCacheForQueryRequest(request_data.form_signatures, &query_data)) { + if (CheckCacheForQueryRequest(request_data.signatures, &query_data)) { DVLOG(1) << "AutofillDownloadManager: query request has been retrieved " - << "from the cache, form signatures: " - << GetCombinedSignature(request_data.form_signatures); + << "from the cache, form signatures:" << + [&request_data] { + std::string form_sigs; + for (const auto& form_and_fields : request_data.signatures) { + base::StrAppend( + &form_sigs, + {" ", base::NumberToString(form_and_fields.first.value())}); + } + return form_sigs; + }(); observer_->OnLoadedServerPredictions(std::move(query_data), - request_data.form_signatures); + request_data.signatures); return true; } @@ -710,9 +718,10 @@ bool AutofillDownloadManager::StartUploadRequest( return false; AutofillUploadContents upload; + FormAndFieldSignatures signatures; if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled, login_form_signature, observed_submission, - &upload)) { + &upload, &signatures)) { return false; } @@ -743,7 +752,7 @@ bool AutofillDownloadManager::StartUploadRequest( } FormRequestData request_data; - request_data.form_signatures.push_back(form.FormSignatureAsStr()); + request_data.signatures = std::move(signatures); request_data.request_type = AutofillDownloadManager::REQUEST_UPLOAD; request_data.payload = std::move(payload); @@ -925,32 +934,27 @@ bool AutofillDownloadManager::StartRequest(FormRequestData request_data) { } void AutofillDownloadManager::CacheQueryRequest( - const std::vector& forms_in_query, + const FormAndFieldSignatures& forms_in_query, const std::string& query_data) { - std::string signature = GetCombinedSignature(forms_in_query); for (auto it = cached_forms_.begin(); it != cached_forms_.end(); ++it) { - if (it->first == signature) { + if (it->first == forms_in_query) { // We hit the cache, move to the first position and return. - std::pair data = *it; + auto data = *it; cached_forms_.erase(it); cached_forms_.push_front(data); return; } } - std::pair data; - data.first = signature; - data.second = query_data; - cached_forms_.push_front(data); + cached_forms_.emplace_front(forms_in_query, query_data); while (cached_forms_.size() > max_form_cache_size_) cached_forms_.pop_back(); } bool AutofillDownloadManager::CheckCacheForQueryRequest( - const std::vector& forms_in_query, + const FormAndFieldSignatures& forms_in_query, std::string* query_data) const { - std::string signature = GetCombinedSignature(forms_in_query); for (const auto& it : cached_forms_) { - if (it.first == signature) { + if (it.first == forms_in_query) { // We hit the cache, fill the data and return. *query_data = it.second; return true; @@ -959,23 +963,6 @@ bool AutofillDownloadManager::CheckCacheForQueryRequest( return false; } -std::string AutofillDownloadManager::GetCombinedSignature( - const std::vector& forms_in_query) const { - size_t total_size = forms_in_query.size(); - for (size_t i = 0; i < forms_in_query.size(); ++i) - total_size += forms_in_query[i].length(); - std::string signature; - - signature.reserve(total_size); - - for (size_t i = 0; i < forms_in_query.size(); ++i) { - if (i) - signature.append(","); - signature.append(forms_in_query[i]); - } - return signature; -} - // static int AutofillDownloadManager::GetMaxServerAttempts() { // This value is constant for the life of the browser, so we cache it @@ -995,7 +982,7 @@ void AutofillDownloadManager::OnSimpleLoaderComplete( std::unique_ptr simple_loader = std::move(*it); url_loaders_.erase(it); - CHECK(request_data.form_signatures.size()); + CHECK(request_data.signatures.size() > 0); int response_code = -1; // Invalid response code. if (simple_loader->ResponseInfo() && simple_loader->ResponseInfo()->headers) { response_code = simple_loader->ResponseInfo()->headers->response_code(); @@ -1024,7 +1011,7 @@ void AutofillDownloadManager::OnSimpleLoaderComplete( << response_code << " and error message from the server " << error_message; - observer_->OnServerRequestError(request_data.form_signatures[0], + observer_->OnServerRequestError(request_data.signatures.front().first, request_data.request_type, response_code); LogFailingPayloadSize(request_data.request_type, @@ -1053,11 +1040,11 @@ void AutofillDownloadManager::OnSimpleLoaderComplete( } if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) { - CacheQueryRequest(request_data.form_signatures, *response_body); + CacheQueryRequest(request_data.signatures, *response_body); UMA_HISTOGRAM_BOOLEAN("Autofill.Query.WasInCache", simple_loader->LoadedFromCache()); observer_->OnLoadedServerPredictions(std::move(*response_body), - request_data.form_signatures); + request_data.signatures); return; } diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.h b/chromium/components/autofill/core/browser/autofill_download_manager.h index 88ce53237a5..4531f498907 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.h +++ b/chromium/components/autofill/core/browser/autofill_download_manager.h @@ -20,6 +20,8 @@ #include "base/strings/string_piece.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/form_structure.h" +#include "components/autofill/core/common/signatures.h" #include "components/variations/variations_http_header_provider.h" #include "net/base/backoff_entry.h" #include "services/network/public/cpp/simple_url_loader.h" @@ -30,7 +32,6 @@ class PrefService; namespace autofill { class AutofillDriver; -class FormStructure; class LogManager; const size_t kMaxAPIQueryGetSize = 10240; // 10 KiB @@ -52,10 +53,10 @@ class AutofillDownloadManager { public: // Called when field type predictions are successfully received from the // server. |response| contains the server response for the forms - // represented by |form_signatures|. + // represented by |signatures|. virtual void OnLoadedServerPredictions( std::string response, - const std::vector& form_signatures) = 0; + const FormAndFieldSignatures& signatures) = 0; // These notifications are used to help with testing. // Called when heuristic either successfully considered for upload and @@ -65,7 +66,7 @@ class AutofillDownloadManager { // |form_signature| - the signature of the requesting form. // |request_type| - type of request that failed. // |http_error| - HTTP error code. - virtual void OnServerRequestError(const std::string& form_signature, + virtual void OnServerRequestError(FormSignature form_signature, RequestType request_type, int http_error) {} @@ -135,7 +136,8 @@ class AutofillDownloadManager { FRIEND_TEST_ALL_PREFIXES(AutofillDownloadManagerTest, RetryLimit_Query); struct FormRequestData; - typedef std::list > QueryRequestCache; + typedef std::list> + QueryRequestCache; // Returns the URL and request method to use when issuing the request // described by |request_data|. If the returned method is GET, the URL @@ -163,11 +165,11 @@ class AutofillDownloadManager { // Caches query request. |forms_in_query| is a vector of form signatures in // the query. |query_data| is the successful data returned over the wire. - void CacheQueryRequest(const std::vector& forms_in_query, + void CacheQueryRequest(const FormAndFieldSignatures& forms_in_query, const std::string& query_data); // Returns true if query is in the cache, while filling |query_data|, false // otherwise. |forms_in_query| is a vector of form signatures in the query. - bool CheckCacheForQueryRequest(const std::vector& forms_in_query, + bool CheckCacheForQueryRequest(const FormAndFieldSignatures& forms_in_query, std::string* query_data) const; // Concatenates |forms_in_query| into one signature. std::string GetCombinedSignature( 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 6b31dc6ef00..22fd6ac32b1 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -211,7 +211,7 @@ class AutofillDownloadManagerTest : public AutofillDownloadManager::Observer, // AutofillDownloadManager::Observer implementation. void OnLoadedServerPredictions( std::string response_xml, - const std::vector& form_signatures) override { + const FormAndFieldSignatures& form_signatures) override { ResponseData response; response.response = std::move(response_xml); response.type_of_response = QUERY_SUCCESSFULL; @@ -224,11 +224,11 @@ class AutofillDownloadManagerTest : public AutofillDownloadManager::Observer, responses_.push_back(response); } - void OnServerRequestError(const std::string& form_signature, + void OnServerRequestError(FormSignature form_signature, AutofillDownloadManager::RequestType request_type, int http_error) override { ResponseData response; - response.signature = form_signature; + response.signature = base::NumberToString(form_signature.value()); response.error = http_error; response.type_of_response = request_type == AutofillDownloadManager::REQUEST_QUERY @@ -1401,7 +1401,7 @@ class AutofillServerCommunicationTest // AutofillDownloadManager::Observer implementation. void OnLoadedServerPredictions( std::string /* response_xml */, - const std::vector& /*form_signatures */) override { + const FormAndFieldSignatures& /*form_signatures */) override { ASSERT_TRUE(run_loop_); run_loop_->QuitWhenIdle(); } @@ -2007,6 +2007,8 @@ TEST_P(AutofillUploadTest, RichMetadata) { EXPECT_TRUE(upload.randomized_form_metadata().has_id()); EXPECT_TRUE(upload.randomized_form_metadata().has_name()); EXPECT_TRUE(upload.randomized_form_metadata().has_url()); + ASSERT_TRUE(upload.randomized_form_metadata().has_checksum_for_url()); + EXPECT_EQ(upload.randomized_form_metadata().checksum_for_url(), 3608731642); EXPECT_EQ(3, upload.field_size()); for (const auto& f : upload.field()) { ASSERT_TRUE(f.has_randomized_field_metadata()); diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index ef21685dbc3..86f1fbca419 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -225,34 +225,4 @@ bool IsInAutofillSuggestionsDisabledExperiment() { return group_name == "Disabled"; } -bool OfferStoreUnmaskedCards(bool is_off_the_record) { -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - // The checkbox can be forced on with a flag, but by default we don't store - // on Linux due to lack of system keychain integration. See crbug.com/162735 - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableOfferStoreUnmaskedWalletCards); -#else - // Never offer to store unmasked cards when off the record. - if (is_off_the_record) { - return false; - } - - // Query the field trial before checking command line flags to ensure UMA - // reports the correct group. - std::string group_name = - base::FieldTrialList::FindFullName("OfferStoreUnmaskedWalletCards"); - - // The checkbox can be forced on or off with flags. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableOfferStoreUnmaskedWalletCards)) - return true; - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableOfferStoreUnmaskedWalletCards)) - return false; - - // Otherwise use the field trial to show the checkbox or not. - return group_name != "Disabled"; -#endif -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h index d291ce0af08..d5b92be5822 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.h +++ b/chromium/components/autofill/core/browser/autofill_experiments.h @@ -44,11 +44,6 @@ bool IsCreditCardMigrationEnabled(PersonalDataManager* personal_data_manager, // disables providing suggestions. bool IsInAutofillSuggestionsDisabledExperiment(); -// Returns true if the user should be offered to locally store unmasked cards. -// This controls whether the option is presented at all rather than the default -// response of the option. -bool OfferStoreUnmaskedCards(bool is_off_the_record); - } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index 6cc56740ad1..ca64c2ca325 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -39,6 +39,9 @@ namespace autofill { namespace { +using AutoselectFirstSuggestion = + AutofillClient::PopupOpenArgs::AutoselectFirstSuggestion; + // Returns true if the suggestion entry is an Autofill warning message. // Warning messages should display on top of suggestion list. bool IsAutofillWarningEntry(int frontend_id) { @@ -162,9 +165,10 @@ void AutofillExternalDelegate::OnSuggestionsReturned( // Send to display. if (query_field_.is_focusable && GetAutofillDriver()->CanShowAutofillUi()) { - manager_->client()->ShowAutofillPopup( + autofill::AutofillClient::PopupOpenArgs open_args( element_bounds_, query_field_.text_direction, suggestions, - autoselect_first_suggestion, popup_type_, GetWeakPtr()); + AutoselectFirstSuggestion(autoselect_first_suggestion), popup_type_); + manager_->client()->ShowAutofillPopup(open_args, GetWeakPtr()); } } 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 b70b30a792f..fd2f179f617 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" +#include "base/i18n/rtl.h" #include "base/macros.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" @@ -35,6 +36,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" using base::ASCIIToUTF16; using testing::_; @@ -51,15 +53,22 @@ const int kAutofillProfileId = 1; class MockAutofillDriver : public TestAutofillDriver { public: - MockAutofillDriver() {} + MockAutofillDriver() = default; // Mock methods to enable testability. - MOCK_METHOD1(RendererShouldAcceptDataListSuggestion, - void(const base::string16&)); - MOCK_METHOD0(RendererShouldClearFilledSection, void()); - MOCK_METHOD0(RendererShouldClearPreviewedForm, void()); - MOCK_METHOD1(RendererShouldFillFieldWithValue, void(const base::string16&)); - MOCK_METHOD1(RendererShouldPreviewFieldWithValue, - void(const base::string16&)); + MOCK_METHOD(void, + RendererShouldAcceptDataListSuggestion, + (const base::string16&), + (override)); + MOCK_METHOD(void, RendererShouldClearFilledSection, (), (override)); + MOCK_METHOD(void, RendererShouldClearPreviewedForm, (), (override)); + MOCK_METHOD(void, + RendererShouldFillFieldWithValue, + (const base::string16&), + (override)); + MOCK_METHOD(void, + RendererShouldPreviewFieldWithValue, + (const base::string16&), + (override)); private: DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver); @@ -67,25 +76,23 @@ class MockAutofillDriver : public TestAutofillDriver { class MockAutofillClient : public TestAutofillClient { public: - MockAutofillClient() {} - - MOCK_METHOD1(ScanCreditCard, void(CreditCardScanCallback callbacK)); - - MOCK_METHOD6(ShowAutofillPopup, - void(const gfx::RectF& element_bounds, - base::i18n::TextDirection text_direction, - const std::vector& suggestions, - bool autoselect_first_suggestion, - PopupType popup_type, - base::WeakPtr delegate)); - - MOCK_METHOD2(UpdateAutofillPopupDataListValues, - void(const std::vector& values, - const std::vector& lables)); - - MOCK_METHOD1(HideAutofillPopup, void(PopupHidingReason)); - - MOCK_METHOD1(ExecuteCommand, void(int)); + MockAutofillClient() = default; + MOCK_METHOD(void, + ScanCreditCard, + (CreditCardScanCallback callbacK), + (override)); + MOCK_METHOD(void, + ShowAutofillPopup, + (const autofill::AutofillClient::PopupOpenArgs& open_args, + base::WeakPtr delegate), + (override)); + MOCK_METHOD(void, + UpdateAutofillPopupDataListValues, + (const std::vector& values, + const std::vector& lables), + (override)); + MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override)); + MOCK_METHOD(void, ExecuteCommand, (int), (override)); private: DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); @@ -99,21 +106,24 @@ class MockAutofillManager : public AutofillManager { client, client->GetPersonalDataManager(), client->GetAutocompleteHistoryManager()) {} - ~MockAutofillManager() override {} PopupType GetPopupType(const FormData& form, const FormFieldData& field) override { return PopupType::kPersonalInformation; } - MOCK_METHOD2(ShouldShowScanCreditCard, - bool(const FormData& form, const FormFieldData& field)); - - MOCK_METHOD2(ShouldShowCreditCardSigninPromo, - bool(const FormData& form, const FormFieldData& field)); - - MOCK_METHOD2(OnUserHideSuggestions, - void(const FormData& form, const FormFieldData& field)); + MOCK_METHOD(bool, + ShouldShowScanCreditCard, + (const FormData& form, const FormFieldData& field), + (override)); + MOCK_METHOD(bool, + ShouldShowCreditCardSigninPromo, + (const FormData& form, const FormFieldData& field), + (override)); + MOCK_METHOD(void, + OnUserHideSuggestions, + (const FormData& form, const FormFieldData& field), + (override)); bool ShouldShowCardsFromAccountOption(const FormData& form, const FormFieldData& field) { @@ -124,19 +134,22 @@ class MockAutofillManager : public AutofillManager { should_show_cards_from_account_option_ = true; } - MOCK_METHOD5(FillOrPreviewForm, - void(AutofillDriver::RendererFormDataAction action, - int query_id, - const FormData& form, - const FormFieldData& field, - int unique_id)); - - MOCK_METHOD5(FillCreditCardForm, - void(int query_id, - const FormData& form, - const FormFieldData& field, - const CreditCard& credit_card, - const base::string16& cvc)); + MOCK_METHOD(void, + FillOrPreviewForm, + (AutofillDriver::RendererFormDataAction action, + int query_id, + const FormData& form, + const FormFieldData& field, + int unique_id), + (override)); + MOCK_METHOD(void, + FillCreditCardForm, + (int query_id, + const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const base::string16& cvc), + (override)); private: bool should_show_cards_from_account_option_ = false; @@ -208,9 +221,9 @@ TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) { // The enums must be cast to ints to prevent compile errors on linux_rel. auto element_ids = testing::ElementsAre( kAutofillProfileId, static_cast(POPUP_ITEM_ID_AUTOFILL_OPTIONS)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // This should call ShowAutofillPopup. std::vector autofill_item; @@ -218,6 +231,9 @@ TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) { autofill_item[0].frontend_id = kAutofillProfileId; external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); EXPECT_CALL( *autofill_manager_, @@ -243,10 +259,9 @@ TEST_F(AutofillExternalDelegateUnitTest, // The enums must be cast to ints to prevent compile errors on linux_rel. auto element_ids = testing::ElementsAre( kAutofillProfileId, static_cast(POPUP_ITEM_ID_AUTOFILL_OPTIONS)); - - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); base::UserActionTester user_action_tester; @@ -258,6 +273,9 @@ TEST_F(AutofillExternalDelegateUnitTest, kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); EXPECT_EQ(0, user_action_tester.GetActionCount( "Signin_Impression_FromAutofillDropdown")); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); EXPECT_CALL( *autofill_manager_, @@ -284,9 +302,9 @@ TEST_F(AutofillExternalDelegateUnitTest, auto element_ids = testing::ElementsAre( static_cast(POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); base::UserActionTester user_action_tester; @@ -296,6 +314,9 @@ TEST_F(AutofillExternalDelegateUnitTest, kQueryId, items, /*autoselect_first_suggestion=*/false); EXPECT_EQ(1, user_action_tester.GetActionCount( "Signin_Impression_FromAutofillDropdown")); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); EXPECT_CALL(autofill_client_, ExecuteCommand(autofill::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO)); @@ -328,9 +349,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) { static_cast(POPUP_ITEM_ID_SEPARATOR), #endif kAutofillProfileId, static_cast(POPUP_ITEM_ID_AUTOFILL_OPTIONS)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // This should call ShowAutofillPopup. std::vector autofill_item; @@ -338,20 +359,25 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) { autofill_item[0].frontend_id = kAutofillProfileId; external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); // Try calling OnSuggestionsReturned with no Autofill values and ensure // the datalist items are still shown. // The enum must be cast to an int to prevent compile errors on linux_rel. - EXPECT_CALL( - autofill_client_, - ShowAutofillPopup(_, _, - SuggestionVectorIdsAre(testing::ElementsAre( - static_cast(POPUP_ITEM_ID_DATALIST_ENTRY))), - false, PopupType::kPersonalInformation, _)); + + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); autofill_item.clear(); external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, + SuggestionVectorIdsAre(testing::ElementsAre( + static_cast(POPUP_ITEM_ID_DATALIST_ENTRY)))); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Test that datalist values can get updated while a popup is showing. @@ -378,9 +404,9 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) { static_cast(POPUP_ITEM_ID_SEPARATOR), #endif kAutofillProfileId, static_cast(POPUP_ITEM_ID_AUTOFILL_OPTIONS)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // Ensure the popup is displayed. std::vector autofill_item; @@ -388,6 +414,9 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) { autofill_item[0].frontend_id = kAutofillProfileId; external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); // This would normally get called from ShowAutofillPopup, but it is mocked so // we need to call OnPopupShown ourselves. @@ -428,9 +457,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutofillDatalistValues) { static_cast(POPUP_ITEM_ID_SEPARATOR), #endif kAutofillProfileId, static_cast(POPUP_ITEM_ID_AUTOFILL_OPTIONS)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // Have an Autofill item that is identical to one of the datalist entries. std::vector autofill_item; @@ -440,6 +469,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutofillDatalistValues) { autofill_item[0].frontend_id = kAutofillProfileId; external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Test that we de-dupe autocomplete values against datalist values, keeping the @@ -467,9 +499,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) { static_cast(POPUP_ITEM_ID_SEPARATOR), #endif static_cast(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // Have an Autocomplete item that is identical to one of the datalist entries // and one that is distinct. @@ -482,6 +514,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) { autocomplete_items[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY; external_delegate_->OnSuggestionsReturned( kQueryId, autocomplete_items, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Test that the Autofill popup is able to display warnings explaining why @@ -490,14 +525,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) { TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) { IssueOnQuery(kQueryId); - // The enums must be cast to ints to prevent compile errors on linux_rel. - EXPECT_CALL( - autofill_client_, - ShowAutofillPopup( - _, _, - SuggestionVectorIdsAre(testing::ElementsAre(static_cast( - POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE))), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // This should call ShowAutofillPopup. std::vector autofill_item; @@ -506,6 +536,15 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) { POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE; external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + + // The enums must be cast to ints to prevent compile errors on linux_rel. + EXPECT_THAT(open_args.suggestions, + SuggestionVectorIdsAre(testing::ElementsAre(static_cast( + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE)))); + EXPECT_EQ(open_args.element_bounds, gfx::RectF()); + EXPECT_EQ(open_args.text_direction, base::i18n::UNKNOWN_DIRECTION); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Test that Autofill warnings are removed if there are also autocomplete @@ -514,13 +553,9 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarningsNotShown_WithSuggestions) { IssueOnQuery(kQueryId); - // The enums must be cast to ints to prevent compile errors on linux_rel. - EXPECT_CALL(autofill_client_, - ShowAutofillPopup( - _, _, - SuggestionVectorIdsAre(testing::ElementsAre( - static_cast(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); // This should call ShowAutofillPopup. std::vector suggestions; @@ -532,6 +567,13 @@ TEST_F(AutofillExternalDelegateUnitTest, suggestions[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY; external_delegate_->OnSuggestionsReturned( kQueryId, suggestions, /*autoselect_first_suggestion=*/false); + + // The enums must be cast to ints to prevent compile errors on linux_rel. + EXPECT_THAT(open_args.suggestions, + SuggestionVectorIdsAre(testing::ElementsAre( + static_cast(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY)))); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Test that the Autofill delegate doesn't try and fill a form with a @@ -768,9 +810,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIcon) { auto element_icons = testing::ElementsAre(std::string(), testing::StartsWith("googlePay")); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); std::vector autofill_item; autofill_item.push_back(Suggestion()); @@ -779,6 +821,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIcon) { // This should call ShowAutofillPopup. external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, true); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIconsAre(element_icons)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } TEST_F(AutofillExternalDelegateUnitTest, @@ -788,9 +833,9 @@ TEST_F(AutofillExternalDelegateUnitTest, auto element_icons = testing::ElementsAre( std::string(), std::string() /* Autofill setting item does not have icon. */); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); std::vector autofill_item; autofill_item.push_back(Suggestion()); @@ -799,6 +844,9 @@ TEST_F(AutofillExternalDelegateUnitTest, // This should call ShowAutofillPopup. external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorIconsAre(element_icons)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) { @@ -806,9 +854,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) { auto element_values = testing::ElementsAre( base::string16(), l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorValuesAre(element_values), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); std::vector autofill_item; autofill_item.push_back(Suggestion()); @@ -817,6 +865,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) { // This should call ShowAutofillPopup. external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Tests that the prompt to show account cards shows up when the corresponding @@ -830,9 +881,9 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest, base::string16(), l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS), l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorValuesAre(element_values), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); std::vector autofill_item; autofill_item.push_back(Suggestion()); @@ -840,6 +891,9 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest, external_delegate_->OnSuggestionsReturned( kQueryId, autofill_item, /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } // Tests that the prompt to show account cards shows up when the corresponding @@ -851,13 +905,16 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest, auto element_values = testing::ElementsAre( l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS)); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorValuesAre(element_values), - false, PopupType::kPersonalInformation, _)); + AutofillClient::PopupOpenArgs open_args; + EXPECT_CALL(autofill_client_, ShowAutofillPopup) + .WillOnce(testing::SaveArg<0>(&open_args)); external_delegate_->OnSuggestionsReturned( kQueryId, std::vector(), /*autoselect_first_suggestion=*/false); + EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values)); + EXPECT_FALSE(open_args.autoselect_first_suggestion); + EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc index 60c09c74bf0..ddb4f6816d3 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.cc +++ b/chromium/components/autofill/core/browser/autofill_handler.cc @@ -5,13 +5,16 @@ #include "components/autofill/core/browser/autofill_handler.h" #include "base/containers/adapters.h" +#include "base/feature_list.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/common/autofill_data_validation.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_internals/log_message.h" #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_tick_clock.h" +#include "components/autofill/core/common/renderer_id.h" #include "components/autofill/core/common/signatures.h" #include "ui/gfx/geometry/rect_f.h" @@ -79,46 +82,62 @@ void AutofillHandler::OnFormsSeen(const std::vector& forms, if (forms.empty()) return; - // Parse each of the forms. Because parsing a given FormData may invalidate - // and replace a form parsed before it (invalidating any pointers we might - // hold) we track the newly created form signatures instead of remembering - // the pointer values. - std::set new_form_signatures; + std::set new_form_renderer_ids; for (const FormData& form : forms) { const auto parse_form_start_time = AutofillTickClock::NowTicks(); - FormStructure* cached_form_structure = nullptr; - FormStructure* form_structure = nullptr; - // Try to find the FormStructure that corresponds to |form| if the form - // contains credit card fields only. - // |cached_form_structure| may still be nullptr after this call. - ignore_result(FindCachedForm(form, &cached_form_structure)); + FormStructure* cached_form_structure = + FindCachedFormByRendererId(form.unique_renderer_id); + // Autofill used to ignore cache hits for non-credit-card forms. The + // motivation behind this is probably to have credit-card forms preserve + // their original signature, whereas non-credit-card forms would use the + // most recent form signature. Ignoring cache hits however appears to be + // part of breaking profile imports and voting for dynamic forms. See + // crbug/1091401#c15 for details. + // + // Therefore, if the kAutofillKeepInitialFormValuesInCache experiment is + // enabled, we do not ignore cache hits, but in those cases where the old + // code would have ignored the cache hit we update the FormStructure's + // FormSignature. + // Otherwise, if the experiment disabled, we just ignore the cache hit. + // + // TODO(crbug.com/1100231) Clean up when experiment is complete. + const bool kOldBehavior = !base::FeatureList::IsEnabled( + features::kAutofillKeepInitialFormValuesInCache); + bool update_form_signature = false; if (cached_form_structure) { for (const FormType& form_type : cached_form_structure->GetFormTypes()) { if (form_type != CREDIT_CARD_FORM) { - cached_form_structure = nullptr; + update_form_signature = true; + if (kOldBehavior) + cached_form_structure = nullptr; break; } } } - if (!ParseForm(form, cached_form_structure, &form_structure)) + FormStructure* form_structure = ParseForm(form, cached_form_structure); + if (!form_structure) continue; DCHECK(form_structure); - new_form_signatures.insert(form_structure->form_signature()); + + if (update_form_signature && !kOldBehavior) + form_structure->set_form_signature(CalculateFormSignature(form)); + + new_form_renderer_ids.insert(form_structure->unique_renderer_id()); AutofillMetrics::LogParseFormTiming(AutofillTickClock::NowTicks() - parse_form_start_time); } - if (new_form_signatures.empty()) + if (new_form_renderer_ids.empty()) return; // Populate the set of newly created form structures and call the // OnFormsParsed handler. std::vector new_form_structures; - new_form_structures.reserve(new_form_signatures.size()); - for (auto signature : new_form_signatures) { - FormStructure* form_structure = nullptr; - if (FindCachedForm(signature, &form_structure) && form_structure) { + new_form_structures.reserve(new_form_renderer_ids.size()); + for (auto renderer_id : new_form_renderer_ids) { + FormStructure* form_structure = FindCachedFormByRendererId(renderer_id); + if (form_structure) { new_form_structures.push_back(form_structure); } else { NOTREACHED(); @@ -204,8 +223,9 @@ bool AutofillHandler::GetCachedFormAndField(const FormData& form, FormStructure** form_structure, AutofillField** autofill_field) { // Maybe find an existing FormStructure that corresponds to |form|. - FormStructure* cached_form = nullptr; - if (FindCachedForm(form, &cached_form)) { + FormStructure* cached_form = + FindCachedFormByRendererId(form.unique_renderer_id); + if (cached_form) { DCHECK(cached_form); if (!CachedFormNeedsUpdate(form, *cached_form)) { // There is no data to return if there are no auto-fillable fields. @@ -221,7 +241,8 @@ bool AutofillHandler::GetCachedFormAndField(const FormData& form, // The form is new or updated, parse it and discard |cached_form|. // i.e., |cached_form| is no longer valid after this call. - if (!ParseForm(form, std::move(cached_form), form_structure)) + *form_structure = ParseForm(form, cached_form); + if (!*form_structure) return false; // Annotate the updated form with its predicted types. @@ -236,52 +257,40 @@ bool AutofillHandler::GetCachedFormAndField(const FormData& form, return *autofill_field != nullptr; } -bool AutofillHandler::FindCachedForm(FormSignature form_signature, - FormStructure** form_structure) const { - auto it = form_structures_.find(form_signature); - if (it != form_structures_.end()) { - *form_structure = it->second.get(); - return true; - } - return false; -} - -bool AutofillHandler::FindCachedForm(const FormData& form, - FormStructure** form_structure) const { - // Find the FormStructure that corresponds to |form|. - if (FindCachedForm(autofill::CalculateFormSignature(form), form_structure)) - return true; - - // The form might have been modified by JavaScript which resulted in a change - // of form signature. Compare it to all the forms in the cache to look for a - // match. - for (const auto& it : form_structures_) { - if (*it.second == form) { - *form_structure = it.second.get(); - return true; +size_t AutofillHandler::FindCachedFormsBySignature( + FormSignature form_signature, + std::vector* form_structures) const { + size_t hits_num = 0; + for (const auto& p : form_structures_) { + if (p.second->form_signature() == form_signature) { + ++hits_num; + if (form_structures) + form_structures->push_back(p.second.get()); } } + return hits_num; +} - *form_structure = nullptr; - return false; +FormStructure* AutofillHandler::FindCachedFormByRendererId( + FormRendererId form_renderer_id) const { + auto it = form_structures_.find(form_renderer_id); + return it != form_structures_.end() ? it->second.get() : nullptr; } -bool AutofillHandler::ParseForm(const FormData& form, - const FormStructure* cached_form, - FormStructure** parsed_form_structure) { - DCHECK(parsed_form_structure); +FormStructure* AutofillHandler::ParseForm(const FormData& form, + const FormStructure* cached_form) { if (form_structures_.size() >= kAutofillHandlerMaxFormCacheSize) { if (log_manager_) { log_manager_->Log() << LoggingScope::kAbortParsing << LogMessage::kAbortParsingTooManyForms << form; } - return false; + return nullptr; } auto form_structure = std::make_unique(form); form_structure->ParseFieldTypesFromAutocompleteAttributes(); if (!form_structure->ShouldBeParsed(log_manager_)) - return false; + return nullptr; if (cached_form) { // We need to keep the server data if available. We need to use them while @@ -300,17 +309,17 @@ bool AutofillHandler::ParseForm(const FormData& form, // Hold the parsed_form_structure we intend to return. We can use this to // reference the form_signature when transferring ownership below. - *parsed_form_structure = form_structure.get(); + FormStructure* parsed_form_structure = form_structure.get(); // Ownership is transferred to |form_structures_| which maintains it until // the form is parsed again or the AutofillHandler is destroyed. // // Note that this insert/update takes ownership of the new form structure // and also destroys the previously cached form structure. - form_structures_[(*parsed_form_structure)->form_signature()] = + form_structures_[parsed_form_structure->unique_renderer_id()] = std::move(form_structure); - return true; + return parsed_form_structure; } void AutofillHandler::Reset() { diff --git a/chromium/components/autofill/core/browser/autofill_handler.h b/chromium/components/autofill/core/browser/autofill_handler.h index 942d74de177..a7965f77308 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.h +++ b/chromium/components/autofill/core/browser/autofill_handler.h @@ -16,6 +16,7 @@ #include "components/autofill/core/browser/autofill_driver.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" +#include "components/autofill/core/common/renderer_id.h" #include "components/autofill/core/common/signatures.h" namespace gfx { @@ -46,9 +47,6 @@ class AutofillHandler { virtual void OnFormParsed() = 0; }; - using FormStructureMap = - std::map>; - virtual ~AutofillHandler(); // Invoked when the value of textfield is changed. @@ -108,10 +106,6 @@ class AutofillHandler { // Invoked when popup window should be hidden. virtual void OnHidePopup() = 0; - // Invoked when data list need to be set. - virtual void OnSetDataList(const std::vector& values, - const std::vector& labels) = 0; - // Invoked when the options of a select element in the |form| changed. virtual void SelectFieldOptionsDidChange(const FormData& form) = 0; @@ -140,14 +134,18 @@ class AutofillHandler { } // Returns the present form structures seen by Autofill handler. - const FormStructureMap& form_structures() const { return form_structures_; } + const std::map>& + form_structures() const { + return form_structures_; + } AutofillDriver* driver() { return driver_; } #if defined(UNIT_TEST) // A public wrapper that calls |mutable_form_structures| for testing purposes // only. - FormStructureMap* mutable_form_structures_for_test() { + std::map>* + mutable_form_structures_for_test() { return mutable_form_structures(); } #endif @@ -192,30 +190,28 @@ class AutofillHandler { virtual void OnFormsParsed(const std::vector& form_structures, const base::TimeTicks timestamp) = 0; - // Fills |form_structure| with a pointer to the cached form structure - // corresponding to |form_signature|. Returns false if no cached form - // structure is found with a matching signature. - bool FindCachedForm(FormSignature form_signature, - FormStructure** form_structure) const WARN_UNUSED_RESULT; + // Returns the number of FormStructures with the given |form_signature| and + // appends them to |form_structures|. Runs in linear time. + size_t FindCachedFormsBySignature( + FormSignature form_signature, + std::vector* form_structures) const; - // Fills |form_structure| with a pointer to the cached form structure - // corresponding to |form|. This will do a direct match of the form's - // signature as well as fuzzy match of the forms structure if no directly - // matching form signature is found. Returns false if no match is found. - bool FindCachedForm(const FormData& form, - FormStructure** form_structure) const WARN_UNUSED_RESULT; + // Returns nullptr if no cached form structure is found with a matching + // |renderer_id|. Runs in logarithmic time. + FormStructure* FindCachedFormByRendererId(FormRendererId renderer_id) const; // Parses the |form| with the server data retrieved from the |cached_form| - // (if any), and writes it to the |parse_form_structure|. Adds the - // |parse_form_structure| to the |form_structures_|. Returns true if the form - // is parsed. - bool ParseForm(const FormData& form, - const FormStructure* cached_form, - FormStructure** parsed_form_structure); + // (if any). Returns nullptr if the form should not be parsed. Otherwise, adds + // the returned form structure to the |form_structures_|. + FormStructure* ParseForm(const FormData& form, + const FormStructure* cached_form); bool value_from_dynamic_change_form_ = false; - FormStructureMap* mutable_form_structures() { return &form_structures_; } + std::map>* + mutable_form_structures() { + return &form_structures_; + } private: // Provides driver-level context to the shared code of the component. Must @@ -225,7 +221,7 @@ class AutofillHandler { LogManager* const log_manager_; // Our copy of the form data. - FormStructureMap form_structures_; + std::map> form_structures_; // Will be not null only for |SaveCardBubbleViewsFullFormBrowserTest|. ObserverForTest* observer_for_testing_ = nullptr; diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc index 8a9fcaf4558..a246c8c8d72 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc @@ -88,11 +88,9 @@ void AutofillHandlerProxy::OnDidPreviewAutofillFormData() {} void AutofillHandlerProxy::OnDidEndTextFieldEditing() {} -void AutofillHandlerProxy::OnHidePopup() {} - -void AutofillHandlerProxy::OnSetDataList( - const std::vector& values, - const std::vector& labels) {} +void AutofillHandlerProxy::OnHidePopup() { + provider_->OnHidePopup(this); +} void AutofillHandlerProxy::SelectFieldOptionsDidChange(const FormData& form) {} diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.h b/chromium/components/autofill/core/browser/autofill_handler_proxy.h index 152292c21ab..2abce2fdb02 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.h +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.h @@ -28,8 +28,6 @@ class AutofillHandlerProxy : public AutofillHandler { void OnDidPreviewAutofillFormData() override; void OnDidEndTextFieldEditing() override; void OnHidePopup() override; - void OnSetDataList(const std::vector& values, - const std::vector& labels) override; void SelectFieldOptionsDidChange(const FormData& form) override; void Reset() override; diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 1513e981190..4e8321b85e3 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -248,6 +248,12 @@ bool IsAddressForm(FieldTypeGroup field_type_group) { void LogAutofillTypePredictionsAvailable( LogManager* log_manager, const std::vector& forms) { + if (VLOG_IS_ON(1)) { + VLOG(1) << "Parsed forms:"; + for (FormStructure* form : forms) + VLOG(1) << *form; + } + if (!log_manager || !log_manager->IsLoggingActive()) return; @@ -425,7 +431,24 @@ int TypeValueFormFillingLimit(ServerFieldType field_type) { } // namespace -AutofillManager::FillingContext::FillingContext() = default; +AutofillManager::FillingContext::FillingContext( + const AutofillField& field, + const AutofillProfile* optional_profile, + const CreditCard* optional_credit_card, + const base::string16* optional_cvc) + : profile(optional_profile ? base::make_optional(*optional_profile) + : base::nullopt), + credit_card(optional_credit_card + ? base::make_optional(std::make_pair( + *optional_credit_card, + optional_cvc ? *optional_cvc : base::string16())) + : base::nullopt), + filled_field_name(field.unique_name()), + original_fill_time(AutofillTickClock::NowTicks()) { + DCHECK(optional_profile || optional_credit_card); + DCHECK(optional_credit_card || !optional_cvc); + DCHECK(profile || credit_card); +} AutofillManager::FillingContext::~FillingContext() = default; @@ -892,6 +915,7 @@ void AutofillManager::OnQueryFormFieldAutofillImpl( const FormFieldData& field, const gfx::RectF& transformed_box, bool autoselect_first_suggestion) { + SetDataList(field.datalist_values, field.datalist_labels); external_delegate_->OnQuery(query_id, form, field, transformed_box); std::vector suggestions; @@ -1022,9 +1046,9 @@ void AutofillManager::FillOrPreviewCreditCardForm( credit_card_, *form_structure, *autofill_field, sync_state_); } - FillOrPreviewDataModelForm( - action, query_id, form, field, credit_card_, /*is_credit_card=*/true, - /*cvc=*/base::string16(), form_structure, autofill_field); + FillOrPreviewDataModelForm(action, query_id, form, field, /*profile=*/nullptr, + &credit_card_, /*cvc=*/nullptr, form_structure, + autofill_field); } void AutofillManager::FillOrPreviewProfileForm( @@ -1040,22 +1064,11 @@ void AutofillManager::FillOrPreviewProfileForm( if (action == AutofillDriver::FORM_DATA_ACTION_FILL) { address_form_event_logger_->OnDidFillSuggestion( profile, *form_structure, *autofill_field, sync_state_); - - // Set up the information needed for an eventual refill of this form. - if (!form_structure->GetIdentifierForRefill().empty()) { - auto& entry = - filling_contexts_map_[form_structure->GetIdentifierForRefill()]; - auto filling_context = std::make_unique(); - filling_context->temp_data_model = profile; - filling_context->filled_field_name = autofill_field->unique_name(); - filling_context->original_fill_time = AutofillTickClock::NowTicks(); - entry = std::move(filling_context); - } } - FillOrPreviewDataModelForm( - action, query_id, form, field, profile, /*is_credit_card=*/false, - /*cvc=*/base::string16(), form_structure, autofill_field); + FillOrPreviewDataModelForm(action, query_id, form, field, &profile, + /*credit_card=*/nullptr, + /*cvc=*/nullptr, form_structure, autofill_field); } void AutofillManager::FillOrPreviewForm( @@ -1096,9 +1109,9 @@ void AutofillManager::FillCreditCardForm(int query_id, if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field)) return; - FillOrPreviewDataModelForm( - AutofillDriver::FORM_DATA_ACTION_FILL, query_id, form, field, credit_card, - /*is_credit_card=*/true, cvc, form_structure, autofill_field); + FillOrPreviewDataModelForm(AutofillDriver::FORM_DATA_ACTION_FILL, query_id, + form, field, /*profile=*/nullptr, &credit_card, + &cvc, form_structure, autofill_field); } void AutofillManager::FillProfileForm(const autofill::AutofillProfile& profile, @@ -1167,11 +1180,13 @@ void AutofillManager::OnDidFillAutofillFormData(const FormData& form, UpdatePendingForm(form); - FormStructure* form_structure = nullptr; std::set form_types; // Find the FormStructure that corresponds to |form|. Use default form type if // form is not present in our cache, which will happen rarely. - if (FindCachedForm(form, &form_structure)) { + + FormStructure* form_structure = + FindCachedFormByRendererId(form.unique_renderer_id); + if (form_structure) { form_types = form_structure->GetFormTypes(); } @@ -1354,8 +1369,8 @@ void AutofillManager::SetTestDelegate(AutofillManagerTestDelegate* delegate) { test_delegate_ = delegate; } -void AutofillManager::OnSetDataList(const std::vector& values, - const std::vector& labels) { +void AutofillManager::SetDataList(const std::vector& values, + const std::vector& labels) { if (!IsValidString16Vector(values) || !IsValidString16Vector(labels) || values.size() != labels.size()) return; @@ -1364,14 +1379,13 @@ void AutofillManager::OnSetDataList(const std::vector& values, } void AutofillManager::SelectFieldOptionsDidChange(const FormData& form) { - FormStructure* form_structure = nullptr; - // Look for a cached version of the form. It will be a null pointer if none is // found, which is fine. - FormStructure* cached_form = nullptr; - ignore_result(FindCachedForm(form, &cached_form)); + FormStructure* cached_form = + FindCachedFormByRendererId(form.unique_renderer_id); - if (!ParseForm(form, cached_form, &form_structure)) + FormStructure* form_structure = ParseForm(form, cached_form); + if (!form_structure) return; if (ShouldTriggerRefill(*form_structure)) @@ -1380,21 +1394,23 @@ void AutofillManager::SelectFieldOptionsDidChange(const FormData& form) { void AutofillManager::OnLoadedServerPredictions( std::string response, - const std::vector& form_signatures) { - // Get the current valid FormStructures represented by |form_signatures|. + const FormAndFieldSignatures& signatures) { + // Get the current valid FormStructures represented by |signatures|. std::vector queried_forms; - queried_forms.reserve(form_signatures.size()); - for (const std::string& signature : form_signatures) { - // The |signature| is the direct string representation of the FormSignature. - // Convert it to a uint64_t to do the lookup. - FormSignature form_signature; - FormStructure* form_structure; - if (base::StringToUint64(signature, &form_signature.value()) && - FindCachedForm(form_signature, &form_structure)) { - queried_forms.push_back(form_structure); - } + queried_forms.reserve(signatures.size()); + for (const auto& p : signatures) { + FindCachedFormsBySignature(p.first, &queried_forms); } + // Each form signature in |signatures| is supposed to be unique, and therefore + // appear only once. This ensures that FindCachedFormsBySignature() produces + // an output without duplicates in the forms. + // TODO(crbug/1064709): |queried_forms| could be a set data structure; their + // order should be irrelevant. + DCHECK_EQ(queried_forms.size(), + std::set(queried_forms.begin(), queried_forms.end()) + .size()); + // If there are no current forms corresponding to the queried signatures, drop // the query response. if (queried_forms.empty()) @@ -1404,10 +1420,12 @@ void AutofillManager::OnLoadedServerPredictions( if (base::FeatureList::IsEnabled(features::kAutofillUseApi)) { // Parse response from API. FormStructure::ParseApiQueryResponse(std::move(response), queried_forms, + signatures, form_interactions_ukm_logger_.get()); } else { // Parse response from legacy server. FormStructure::ParseQueryResponse(std::move(response), queried_forms, + signatures, form_interactions_ukm_logger_.get()); } @@ -1687,15 +1705,25 @@ void AutofillManager::FillOrPreviewDataModelForm( int query_id, const FormData& form, const FormFieldData& field, - const AutofillDataModel& data_model, - bool is_credit_card, - const base::string16& cvc, + const AutofillProfile* optional_profile, + const CreditCard* optional_credit_card, + const base::string16* optional_cvc, FormStructure* form_structure, AutofillField* autofill_field, bool is_refill) { + DCHECK(optional_profile || optional_credit_card); + DCHECK(optional_credit_card || !optional_cvc); DCHECK(form_structure); DCHECK(autofill_field); + const AutofillDataModel& data_model = [&]() -> const AutofillDataModel& { + if (optional_profile) + return *optional_profile; + else + return *optional_credit_card; + }(); + bool is_credit_card = !!optional_credit_card; + LogBuffer buffer; buffer << "is credit card section: " << is_credit_card << Br{}; buffer << "is refill: " << is_refill << Br{}; @@ -1707,21 +1735,25 @@ void AutofillManager::FillOrPreviewDataModelForm( FormData result = form; DCHECK_EQ(form_structure->field_count(), form.fields.size()); + if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) { + filling_contexts_map_[form_structure->GetIdentifierForRefill()] = + std::make_unique(*autofill_field, optional_profile, + optional_credit_card, optional_cvc); + } + // Only record the types that are filled for an eventual refill if all the // following are satisfied: // The refilling feature is enabled. // A form with the given name is already filled. // A refill has not been attempted for that form yet. // This fill is not a refill attempt. - // This is not a credit card fill. FillingContext* filling_context = nullptr; auto itr = filling_contexts_map_.find(form_structure->GetIdentifierForRefill()); if (itr != filling_contexts_map_.end()) filling_context = itr->second.get(); bool could_attempt_refill = filling_context != nullptr && - !filling_context->attempted_refill && - !is_refill && !is_credit_card; + !filling_context->attempted_refill && !is_refill; // Count the number of times the value of a specific type was filled into the // form. @@ -1796,11 +1828,12 @@ void AutofillManager::FillOrPreviewDataModelForm( continue; } + ServerFieldType field_type = cached_field->Type().GetStorableType(); + // Don't fill expired cards expiration date. - if (data_util::IsCreditCardExpirationType( - cached_field->Type().GetStorableType()) && - static_cast(&data_model) - ->IsExpired(AutofillClock::Now())) { + if (data_util::IsCreditCardExpirationType(field_type) && + (!optional_credit_card || + optional_credit_card->IsExpired(AutofillClock::Now()))) { buffer << Tr{} << field_number << "Skipped: don't fill expiration date of expired cards"; continue; @@ -1808,7 +1841,6 @@ void AutofillManager::FillOrPreviewDataModelForm( // A field with a specific type is only allowed to be filled a limited // number of times given by |TypeValueFormFillingLimit(field_type)|. - const auto field_type = cached_field->Type().GetStorableType(); if (type_filling_count[field_type] >= TypeValueFormFillingLimit(field_type)) { buffer << Tr{} << field_number @@ -1831,12 +1863,14 @@ void AutofillManager::FillOrPreviewDataModelForm( bool has_value_before = !result.fields[i].value.empty(); bool is_autofilled_before = result.fields[i].is_autofilled; + const base::string16 kEmptyCvc{}; std::string failure_to_fill; // Reason for failing to fill. // Fill the non-empty value from |data_model| into the result vector, which // will be sent to the renderer. FillFieldWithValue( - cached_field, data_model, &result.fields[i], should_notify, cvc, + cached_field, data_model, &result.fields[i], should_notify, + optional_cvc ? *optional_cvc : kEmptyCvc, data_util::DetermineGroups(form_structure->GetServerFieldTypes()), &failure_to_fill); @@ -1880,9 +1914,9 @@ std::unique_ptr AutofillManager::ValidateSubmittedForm( const FormData& form) { // Ignore forms not present in our cache. These are typically forms with // wonky JavaScript that also makes them not auto-fillable. - FormStructure* cached_submitted_form; - if (!FindCachedForm(form, &cached_submitted_form) || - !ShouldUploadForm(*cached_submitted_form)) { + FormStructure* cached_submitted_form = + FindCachedFormByRendererId(form.unique_renderer_id); + if (!cached_submitted_form || !ShouldUploadForm(*cached_submitted_form)) { return nullptr; } @@ -2425,8 +2459,9 @@ bool AutofillManager::ShouldTriggerRefill(const FormStructure& form_structure) { } void AutofillManager::TriggerRefill(const FormData& form) { - FormStructure* form_structure = nullptr; - if (!FindCachedForm(form, &form_structure)) + FormStructure* form_structure = + FindCachedFormByRendererId(form.unique_renderer_id); + if (!form_structure) return; DCHECK(form_structure); @@ -2460,12 +2495,23 @@ void AutofillManager::TriggerRefill(const FormData& form) { return; FormFieldData field = *autofill_field; - base::string16 cvc; - FillOrPreviewDataModelForm( - AutofillDriver::RendererFormDataAction::FORM_DATA_ACTION_FILL, - /*query_id=*/-1, form, field, filling_context->temp_data_model, - /*is_credit_card=*/false, cvc, form_structure, autofill_field, - /*is_refill=*/true); + if (filling_context->credit_card) { + FillOrPreviewDataModelForm( + AutofillDriver::RendererFormDataAction::FORM_DATA_ACTION_FILL, + /*query_id=*/-1, form, field, + /*profile=*/nullptr, &filling_context->credit_card.value().first, + &filling_context->credit_card.value().second, form_structure, + autofill_field, + /*is_refill=*/true); + } + if (filling_context->profile) { + FillOrPreviewDataModelForm( + AutofillDriver::RendererFormDataAction::FORM_DATA_ACTION_FILL, + /*query_id=*/-1, form, field, &filling_context->profile.value(), + /*credic_card=*/nullptr, /*cvc=*/nullptr, form_structure, + autofill_field, + /*is_refill=*/true); + } } void AutofillManager::GetAvailableSuggestions( diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index ec2e22519c7..71fb7e85857 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -17,6 +17,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "base/time/time.h" #include "base/timer/timer.h" @@ -228,8 +229,6 @@ class AutofillManager : public AutofillHandler, void OnDidPreviewAutofillFormData() override; void OnDidEndTextFieldEditing() override; void OnHidePopup() override; - void OnSetDataList(const std::vector& values, - const std::vector& labels) override; void SelectFieldOptionsDidChange(const FormData& form) override; void Reset() override; @@ -280,8 +279,8 @@ class AutofillManager : public AutofillHandler, // purposes only. void OnLoadedServerPredictionsForTest( std::string response, - const std::vector& form_signatures) { - OnLoadedServerPredictions(response, form_signatures); + const FormAndFieldSignatures& signatures) { + OnLoadedServerPredictions(response, signatures); } // A public wrapper that calls |MakeFrontendID| for testing purposes only. @@ -386,17 +385,25 @@ class AutofillManager : public AutofillHandler, private: // Keeps track of the filling context for a form, used to make refill attemps. struct FillingContext { - FillingContext(); + // |optional_profile| or |optional_credit_card| must be non-null. + // If |optional_credit_card| is non-null, |optional_cvc| may be non-null. + FillingContext(const AutofillField& field, + const AutofillProfile* optional_profile, + const CreditCard* optional_credit_card, + const base::string16* optional_cvc); ~FillingContext(); // Whether a refill attempt was made. bool attempted_refill = false; - // The profile that was used for the initial fill. - AutofillProfile temp_data_model; + // The profile or credit card that was used for the initial fill. + // The std::string associated with the credit card is the CVC, which may be + // empty. + const base::Optional profile; + const base::Optional> credit_card; // The name of the field that was initially filled. - base::string16 filled_field_name; - // The time at which the initial fill occured. - base::TimeTicks original_fill_time; + const base::string16 filled_field_name; + // The time at which the initial fill occurred. + const base::TimeTicks original_fill_time; // The timer used to trigger a refill. base::OneShotTimer on_refill_timer; // The field type groups that were initially filled. @@ -430,7 +437,7 @@ class AutofillManager : public AutofillHandler, // AutofillDownloadManager::Observer: void OnLoadedServerPredictions( std::string response, - const std::vector& form_signatures) override; + const FormAndFieldSignatures& signatures) override; // CreditCardAccessManager::Accessor void OnCreditCardFetched( @@ -477,9 +484,9 @@ class AutofillManager : public AutofillHandler, int query_id, const FormData& form, const FormFieldData& field, - const AutofillDataModel& data_model, - bool is_credit_card, - const base::string16& cvc, + const AutofillProfile* optional_profile, + const CreditCard* optional_credit_card, + const base::string16* optional_cvc, FormStructure* form_structure, AutofillField* autofill_field, bool is_refill = false); @@ -596,6 +603,8 @@ class AutofillManager : public AutofillHandler, FormEventLoggerBase* GetEventFormLogger( FieldTypeGroup field_type_group) const; + void SetDataList(const std::vector& values, + const std::vector& labels); AutofillClient* const client_; LogManager* log_manager_; diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index ec5ccdf8e0e..79f827e5c8e 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -96,7 +96,6 @@ using features::kAutofillEnforceMinRequiredFieldsForHeuristics; using features::kAutofillEnforceMinRequiredFieldsForQuery; using features::kAutofillEnforceMinRequiredFieldsForUpload; using features::kAutofillRestrictUnownedFieldsToFormlessCheckout; -using mojom::ButtonTitleType; using mojom::SubmissionIndicatorEvent; using mojom::SubmissionSource; @@ -832,6 +831,7 @@ TEST_F(AutofillManagerTest, OnFormsSeen_DifferentFormStructures) { // Different form structure. FormData form2; + form2.unique_renderer_id.value() = 2; form2.name = ASCIIToUTF16("MyForm"); form2.url = GURL("https://myform.com/form.html"); form2.action = GURL("https://myform.com/submit.html"); @@ -862,6 +862,7 @@ TEST_F(AutofillManagerTest, OnFormsSeen_SendAutofillTypePredictionsToRenderer) { FormData form2; FormFieldData field; test::CreateTestFormField("Querty", "qwerty", "", "text", &field); + form2.unique_renderer_id.value() = 2; form2.name = ASCIIToUTF16("NonQueryable"); form2.url = form1.url; form2.action = GURL("https://myform.com/submit.html"); @@ -4716,6 +4717,9 @@ TEST_F(AutofillManagerTest, AutocompleteSuggestions_SomeWhenAutofillDisabled) { autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); + external_delegate_ = std::make_unique( + autofill_manager_.get(), autofill_driver_.get(), + /*call_parent_methods=*/false); autofill_manager_->SetExternalDelegate(external_delegate_.get()); // Set up our form data. @@ -4742,6 +4746,9 @@ TEST_F(AutofillManagerTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); + external_delegate_ = std::make_unique( + autofill_manager_.get(), autofill_driver_.get(), + /*call_parent_methods=*/false); autofill_manager_->SetExternalDelegate(external_delegate_.get()); // Set up our form data. @@ -4812,6 +4819,9 @@ TEST_F(AutofillManagerTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); + external_delegate_ = std::make_unique( + autofill_manager_.get(), autofill_driver_.get(), + /*call_parent_methods=*/false); autofill_manager_->SetExternalDelegate(external_delegate_.get()); // Set up our form data. @@ -4840,6 +4850,9 @@ TEST_F(AutofillManagerTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); + external_delegate_ = std::make_unique( + autofill_manager_.get(), autofill_driver_.get(), + /*call_parent_methods=*/false); autofill_manager_->SetExternalDelegate(external_delegate_.get()); // Set up our form data. @@ -4890,6 +4903,9 @@ TEST_F(AutofillManagerTest, AutocompleteOffRespectedForAutocomplete) { autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); + external_delegate_ = std::make_unique( + autofill_manager_.get(), autofill_driver_.get(), + /*call_parent_methods=*/false); autofill_manager_->SetExternalDelegate(external_delegate_.get()); EXPECT_CALL(*(autocomplete_history_manager_.get()), @@ -4947,6 +4963,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromLegacyServer) { // Similarly, a second form. FormData form2; + form2.unique_renderer_id.value() = 2; form2.name = ASCIIToUTF16("MyForm"); form2.url = GURL("http://myform.com/form.html"); form2.action = GURL("http://myform.com/submit.html"); @@ -4981,9 +4998,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromLegacyServer) { std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); - std::vector signatures; - signatures.push_back(form_structure->FormSignatureAsStr()); - signatures.push_back(form_structure2->FormSignatureAsStr()); + FormAndFieldSignatures signatures = + test::GetEncodedSignatures({form_structure, form_structure2}); base::HistogramTester histogram_tester; autofill_manager_->OnLoadedServerPredictionsForTest(response_string, @@ -5022,6 +5038,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromApi) { // First form on the page. FormData form; + form.unique_renderer_id.value() = 1; form.name = ASCIIToUTF16("MyForm"); form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); @@ -5045,6 +5062,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromApi) { // Second form on the page. FormData form2; + form2.unique_renderer_id.value() = 2; form2.name = ASCIIToUTF16("MyForm2"); form2.url = GURL("http://myform.com/form.html"); form2.action = GURL("http://myform.com/submit.html"); @@ -5084,8 +5102,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromApi) { std::string encoded_response_string; base::Base64Encode(response_string, &encoded_response_string); - std::vector signatures = {form_structure->FormSignatureAsStr(), - form_structure2->FormSignatureAsStr()}; + FormAndFieldSignatures signatures = + test::GetEncodedSignatures({form_structure, form_structure2}); // Run method under test. base::HistogramTester histogram_tester; @@ -5127,6 +5145,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions_ResetManager) { // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); form_structure->DetermineHeuristicTypes(); + FormAndFieldSignatures signatures = + test::GetEncodedSignatures(*form_structure); autofill_manager_->AddSeenFormStructure( std::unique_ptr(form_structure)); @@ -5146,9 +5166,6 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions_ResetManager) { std::string response_string_base64; base::Base64Encode(response_string, &response_string_base64); - std::vector signatures; - signatures.push_back(form_structure->FormSignatureAsStr()); - // Reset the manager (such as during a navigation). autofill_manager_->Reset(); @@ -5209,12 +5226,9 @@ TEST_F(AutofillManagerTest, DetermineHeuristicsWithOverallPrediction) { std::string response_string_base64; base::Base64Encode(response_string, &response_string_base64); - std::vector signatures; - signatures.push_back(form_structure->FormSignatureAsStr()); - base::HistogramTester histogram_tester; - autofill_manager_->OnLoadedServerPredictionsForTest(response_string_base64, - signatures); + autofill_manager_->OnLoadedServerPredictionsForTest( + response_string_base64, test::GetEncodedSignatures(*form_structure)); // Verify that FormStructure::ParseQueryResponse was called (here and below). histogram_tester.ExpectBucketCount("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_RESPONSE_RECEIVED, @@ -7641,8 +7655,9 @@ TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_AddressOnly) { // Create a form with name and address fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7684,8 +7699,9 @@ TEST_F(AutofillManagerTest, // Create a form with address fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7726,8 +7742,9 @@ TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_ContactOnly) { // Create a form with name and contact fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7768,8 +7785,9 @@ TEST_F(AutofillManagerTest, // Create a form with contact fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7809,8 +7827,9 @@ TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_Other) { // Create a form with name fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7850,8 +7869,9 @@ TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_AddressPlusEmail) { // Create a form with name, address, and email fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7901,8 +7921,9 @@ TEST_F(AutofillManagerTest, // Create a form with address and email fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -7949,8 +7970,9 @@ TEST_F(AutofillManagerTest, DidShowSuggestions_LogByType_AddressPlusPhone) { // Create a form with name fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -8000,8 +8022,9 @@ TEST_F(AutofillManagerTest, // Create a form with name, address, and phone fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -8049,8 +8072,9 @@ TEST_F(AutofillManagerTest, // Create a form with name, address, phone, and email fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = @@ -8101,8 +8125,9 @@ TEST_F(AutofillManagerTest, // Create a form with address, phone, and email fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.button_titles = {std::make_pair( - ASCIIToUTF16("Submit"), ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); form.main_frame_origin = diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index ee304fed0c1..9cdccda5142 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -20,6 +20,7 @@ #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/form_structure.h" +#include "components/autofill/core/browser/form_types.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_tick_clock.h" @@ -110,8 +111,9 @@ std::string PreviousSaveCreditCardPromptUserDecisionToString( // accessed from the unit test file. It is not exposed in the header file, // however, because it is not intended for consumption outside of the metrics // implementation. -int GetFieldTypeGroupMetric(ServerFieldType field_type, - AutofillMetrics::FieldTypeQualityMetric metric) { +int GetFieldTypeGroupPredictionQualityMetric( + ServerFieldType field_type, + AutofillMetrics::FieldTypeQualityMetric metric) { DCHECK_LT(metric, AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS); FieldTypeGroupForMetrics group = GROUP_AMBIGUOUS; @@ -227,6 +229,26 @@ int GetFieldTypeGroupMetric(ServerFieldType field_type, return (group << 8) | metric; } +// This function encodes the integer value of a |ServerFieldType| and the +// metric value of an |AutofilledFieldUserEdtingStatus| into a 16 bit integer. +// The lower four bits are used to encode the editing status and the higher +// 12 bits are used to encode the field type. +int GetFieldTypeUserEditStatusMetric( + ServerFieldType server_type, + AutofillMetrics::AutofilledFieldUserEditingStatusMetric metric) { + static_assert(ServerFieldType::MAX_VALID_FIELD_TYPE <= (UINT16_MAX >> 4), + "Autofill::ServerTypes value needs more than 12 bits."); + + static_assert( + static_cast( + AutofillMetrics::AutofilledFieldUserEditingStatusMetric::kMaxValue) <= + (UINT16_MAX >> 12), + "AutofillMetrics::AutofilledFieldUserEditingStatusMetric value needs " + "more than 4 bits"); + + return (server_type << 4) | static_cast(metric); +} + namespace { const char* GetQualityMetricPredictionSource( @@ -355,9 +377,9 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused( DVLOG(2) << "TRUE POSITIVE"; base::UmaHistogramSparse(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE); - base::UmaHistogramSparse( - type_specific_histogram, - GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE)); + base::UmaHistogramSparse(type_specific_histogram, + GetFieldTypeGroupPredictionQualityMetric( + actual_type, AutofillMetrics::TRUE_POSITIVE)); if (log_rationalization_metrics) { bool duplicated_filling = DuplicatedFilling(form, field); base::UmaHistogramSparse( @@ -378,8 +400,8 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused( if (predicted_type != UNKNOWN_TYPE) base::UmaHistogramSparse( type_specific_histogram, - GetFieldTypeGroupMetric(predicted_type, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH)); + GetFieldTypeGroupPredictionQualityMetric( + predicted_type, AutofillMetrics::FALSE_NEGATIVE_MISMATCH)); if (log_rationalization_metrics) { // Logging RATIONALIZATION_OK despite of type mismatch here because autofill // would have got it wrong with or without rationalization. Rationalization @@ -418,9 +440,9 @@ void LogPredictionQualityMetricsForCommonFields( // predict that type with which the field was filled. base::UmaHistogramSparse(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE); - base::UmaHistogramSparse( - type_specific_histogram, - GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE)); + base::UmaHistogramSparse(type_specific_histogram, + GetFieldTypeGroupPredictionQualityMetric( + actual_type, AutofillMetrics::TRUE_POSITIVE)); return; } @@ -435,8 +457,9 @@ void LogPredictionQualityMetricsForCommonFields( : (is_ambiguous ? AutofillMetrics::FALSE_POSITIVE_AMBIGUOUS : AutofillMetrics::FALSE_POSITIVE_UNKNOWN)); base::UmaHistogramSparse(aggregate_histogram, metric); - base::UmaHistogramSparse(type_specific_histogram, - GetFieldTypeGroupMetric(predicted_type, metric)); + base::UmaHistogramSparse( + type_specific_histogram, + GetFieldTypeGroupPredictionQualityMetric(predicted_type, metric)); return; } @@ -449,8 +472,8 @@ void LogPredictionQualityMetricsForCommonFields( AutofillMetrics::FALSE_NEGATIVE_UNKNOWN); base::UmaHistogramSparse( type_specific_histogram, - GetFieldTypeGroupMetric(actual_type, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN)); + GetFieldTypeGroupPredictionQualityMetric( + actual_type, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN)); return; } @@ -465,12 +488,12 @@ void LogPredictionQualityMetricsForCommonFields( AutofillMetrics::FALSE_NEGATIVE_MISMATCH); base::UmaHistogramSparse( type_specific_histogram, - GetFieldTypeGroupMetric(actual_type, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH)); + GetFieldTypeGroupPredictionQualityMetric( + actual_type, AutofillMetrics::FALSE_NEGATIVE_MISMATCH)); base::UmaHistogramSparse( type_specific_histogram, - GetFieldTypeGroupMetric(predicted_type, - AutofillMetrics::FALSE_POSITIVE_MISMATCH)); + GetFieldTypeGroupPredictionQualityMetric( + predicted_type, AutofillMetrics::FALSE_POSITIVE_MISMATCH)); } // Logs field type prediction quality metrics. The primary histogram name is @@ -689,6 +712,121 @@ void AutofillMetrics::LogSaveCardRequestExpirationDateReasonMetric( reason); } +// static +void AutofillMetrics::LogSaveCardPromptOfferMetric( + SaveCardPromptOfferMetric metric, + bool is_uploading, + bool is_reshow, + AutofillClient::SaveCreditCardOptions options, + int previous_save_credit_card_prompt_user_decision, + security_state::SecurityLevel security_level, + AutofillSyncSigninState sync_state) { + DCHECK_LT(metric, NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + std::string base_histogram_name = "Autofill.SaveCreditCardPromptOffer"; + std::string destination = is_uploading ? ".Upload" : ".Local"; + std::string show = is_reshow ? ".Reshows" : ".FirstShow"; + std::string metric_with_destination_and_show = + base_histogram_name + destination + show; + base::UmaHistogramEnumeration(metric_with_destination_and_show, metric, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + + base::UmaHistogramEnumeration( + metric_with_destination_and_show + GetMetricsSyncStateSuffix(sync_state), + metric, NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + + if (options.should_request_name_from_user) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".RequestingCardholderName", metric, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + } + if (options.should_request_expiration_date_from_user) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".RequestingExpirationDate", metric, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + } + if (options.has_non_focusable_field) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".FromNonFocusableForm", metric, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + } + if (options.from_dynamic_change_form) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".FromDynamicChangeForm", metric, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + } + + base::UmaHistogramEnumeration( + metric_with_destination_and_show + + PreviousSaveCreditCardPromptUserDecisionToString( + previous_save_credit_card_prompt_user_decision), + metric, NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + + if (security_level != security_state::SecurityLevel::SECURITY_LEVEL_COUNT) { + base::UmaHistogramEnumeration( + security_state::GetSecurityLevelHistogramName( + base_histogram_name + destination, security_level), + metric, NUM_SAVE_CARD_PROMPT_OFFER_METRICS); + } +} + +// static +void AutofillMetrics::LogSaveCardPromptResultMetric( + SaveCardPromptResultMetric metric, + bool is_uploading, + bool is_reshow, + AutofillClient::SaveCreditCardOptions options, + int previous_save_credit_card_prompt_user_decision, + security_state::SecurityLevel security_level, + AutofillSyncSigninState sync_state) { + DCHECK_LT(metric, NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + std::string base_histogram_name = "Autofill.SaveCreditCardPromptResult"; + std::string destination = is_uploading ? ".Upload" : ".Local"; + std::string show = is_reshow ? ".Reshows" : ".FirstShow"; + std::string metric_with_destination_and_show = + base_histogram_name + destination + show; + + base::UmaHistogramEnumeration(metric_with_destination_and_show, metric, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + + base::UmaHistogramEnumeration( + metric_with_destination_and_show + GetMetricsSyncStateSuffix(sync_state), + metric, NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + + if (options.should_request_name_from_user) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".RequestingCardholderName", metric, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + } + if (options.should_request_expiration_date_from_user) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".RequestingExpirationDate", metric, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + } + if (options.has_non_focusable_field) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".FromNonFocusableForm", metric, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + } + if (options.from_dynamic_change_form) { + base::UmaHistogramEnumeration( + metric_with_destination_and_show + ".FromDynamicChangeForm", metric, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + } + + base::UmaHistogramEnumeration( + metric_with_destination_and_show + + PreviousSaveCreditCardPromptUserDecisionToString( + previous_save_credit_card_prompt_user_decision), + metric, NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + + if (security_level != security_state::SecurityLevel::SECURITY_LEVEL_COUNT) { + base::UmaHistogramEnumeration( + security_state::GetSecurityLevelHistogramName( + base_histogram_name + destination, security_level), + metric, NUM_SAVE_CARD_PROMPT_RESULT_METRICS); + } +} + // static void AutofillMetrics::LogSaveCardPromptMetric( SaveCardPromptMetric metric, @@ -760,6 +898,12 @@ void AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( metric, NUM_SAVE_CARD_PROMPT_METRICS); } +// static +void AutofillMetrics::LogCreditCardUploadLegalMessageLinkClicked() { + base::RecordAction(base::UserMetricsAction( + "Autofill_CreditCardUpload_LegalMessageLinkClicked")); +} + // static void AutofillMetrics::LogCreditCardUploadFeedbackMetric( CreditCardUploadFeedbackMetric metric) { @@ -1262,6 +1406,49 @@ void AutofillMetrics::LogOverallPredictionQualityMetrics( true /*log_rationalization_metrics*/); } +void AutofillMetrics::LogEditedAutofilledFieldAtSubmission( + FormInteractionsUkmLogger* form_interactions_ukm_logger, + const FormStructure& form, + const AutofillField& field) { + const std::string aggregate_histogram = + "Autofill.EditedAutofilledFieldAtSubmission.Aggregate"; + const std::string type_specific_histogram = + "Autofill.EditedAutofilledFieldAtSubmission.ByFieldType"; + + AutofilledFieldUserEditingStatusMetric editing_metric = + field.previously_autofilled() + ? AutofilledFieldUserEditingStatusMetric::AUTOFILLED_FIELD_WAS_EDITED + : AutofilledFieldUserEditingStatusMetric:: + AUTOFILLED_FIELD_WAS_NOT_EDITED; + + // Record the aggregated UMA statistics. + base::UmaHistogramEnumeration(aggregate_histogram, editing_metric); + + // Record the type specific UMA statistics. + base::UmaHistogramSparse(type_specific_histogram, + GetFieldTypeUserEditStatusMetric( + field.Type().GetStorableType(), editing_metric)); + + // Record the UMA statistics spliced by the autocomplete attribute value. + FormType form_type = + FormTypes::FieldTypeGroupToFormType(field.Type().group()); + if (form_type == ADDRESS_FORM || form_type == CREDIT_CARD_FORM) { + bool autocomplete_off = field.autocomplete_attribute == "off"; + const std::string autocomplete_histogram = base::StrCat( + {"Autofill.Autocomplete.", autocomplete_off ? "Off" : "NotOff", + ".EditedAutofilledFieldAtSubmission.", + form_type == ADDRESS_FORM ? "Address" : "CreditCard"}); + base::UmaHistogramEnumeration(autocomplete_histogram, editing_metric); + } + + // If the field was edited, record the event to UKM. + if (editing_metric == + AutofilledFieldUserEditingStatusMetric::AUTOFILLED_FIELD_WAS_EDITED) { + form_interactions_ukm_logger->LogEditedAutofilledFieldAtSubmission(form, + field); + } +} + // static void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) { DCHECK_LT(metric, NUM_SERVER_QUERY_METRICS); @@ -1481,7 +1668,9 @@ void AutofillMetrics::LogStoredCreditCardMetrics( const std::vector>& server_cards, base::TimeDelta disused_data_threshold) { size_t num_local_cards = 0; + size_t num_local_cards_with_nickname = 0; size_t num_masked_cards = 0; + size_t num_masked_cards_with_nickname = 0; size_t num_unmasked_cards = 0; size_t num_disused_local_cards = 0; size_t num_disused_masked_cards = 0; @@ -1513,6 +1702,8 @@ void AutofillMetrics::LogStoredCreditCardMetrics( days_since_last_use); num_local_cards += 1; num_disused_local_cards += disused_delta; + if (card->HasValidNickname()) + num_local_cards_with_nickname += 1; break; case CreditCard::MASKED_SERVER_CARD: UMA_HISTOGRAM_COUNTS_1000( @@ -1523,6 +1714,8 @@ void AutofillMetrics::LogStoredCreditCardMetrics( days_since_last_use); num_masked_cards += 1; num_disused_masked_cards += disused_delta; + if (card->HasValidNickname()) + num_masked_cards_with_nickname += 1; break; case CreditCard::FULL_SERVER_CARD: UMA_HISTOGRAM_COUNTS_1000( @@ -1549,10 +1742,15 @@ void AutofillMetrics::LogStoredCreditCardMetrics( UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount", num_cards); UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount.Local", num_local_cards); + UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount.Local.WithNickname", + num_local_cards_with_nickname); UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount.Server", num_server_cards); UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount.Server.Masked", num_masked_cards); + UMA_HISTOGRAM_COUNTS_1000( + "Autofill.StoredCreditCardCount.Server.Masked.WithNickname", + num_masked_cards_with_nickname); UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredCreditCardCount.Server.Unmasked", num_unmasked_cards); @@ -1953,6 +2151,19 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion( .Record(ukm_recorder_); } +void AutofillMetrics::FormInteractionsUkmLogger:: + LogEditedAutofilledFieldAtSubmission(const FormStructure& form, + const AutofillField& field) { + if (!CanLog()) + return; + + ukm::builders::Autofill_EditedAutofilledFieldAtSubmission(source_id_) + .SetFieldSignature(HashFieldSignature(field.GetFieldSignature())) + .SetFormSignature(HashFormSignature(form.form_signature())) + .SetOverallType(static_cast(field.Type().GetStorableType())) + .Record(ukm_recorder_); +} + void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange( const FormStructure& form, const AutofillField& field) { @@ -2211,4 +2422,10 @@ void AutofillMetrics:: "Autofill.AddressProfileImportCountrySpecificFieldRequirements", metric); } +// static +void AutofillMetrics::LogAddressFormImportStatustMetric( + AutofillMetrics::AddressProfileImportStatusMetric metric) { + base::UmaHistogramEnumeration("Autofill.AddressProfileImportStatus", metric); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index 124cac7c810..68dcd8aebd3 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -135,14 +135,14 @@ class AutofillMetrics { enum InfoBarMetric { INFOBAR_SHOWN = 0, // We showed an infobar, e.g. prompting to save credit - // card info. - INFOBAR_ACCEPTED, // The user explicitly accepted the infobar. - INFOBAR_DENIED, // The user explicitly denied the infobar. - INFOBAR_IGNORED, // The user completely ignored the infobar (logged on - // tab close). + // card info. + INFOBAR_ACCEPTED, // The user explicitly accepted the infobar. + INFOBAR_DENIED, // The user explicitly denied the infobar. + INFOBAR_IGNORED, // The user completely ignored the infobar (logged on + // tab close). INFOBAR_NOT_SHOWN_INVALID_LEGAL_MESSAGE, // We didn't show the infobar - // because the provided legal - // message was invalid. + // because the provided legal + // message was invalid. NUM_INFO_BAR_METRICS, }; @@ -233,6 +233,30 @@ class AutofillMetrics { kMaxValue = kExpirationDatePresentButExpired, }; + // Metrics to track event when the save card prompt is offered. + enum SaveCardPromptOfferMetric { + // The prompt is actually shown. + SAVE_CARD_PROMPT_SHOWN, + // The prompt is not shown because the prompt has been declined by the user + // too many times. + SAVE_CARD_PROMPT_NOT_SHOWN_MAX_STRIKES_REACHED, + NUM_SAVE_CARD_PROMPT_OFFER_METRICS, + }; + + enum SaveCardPromptResultMetric { + // The user explicitly accepted the prompt by clicking the ok button. + SAVE_CARD_PROMPT_ACCEPTED, + // The user explicitly cancelled the prompt by clicking the cancel button. + SAVE_CARD_PROMPT_CANCELLED, + // The user explicitly closed the prompt with the close button or ESC. + SAVE_CARD_PROMPT_CLOSED, + // The user did not interact with the prompt. + SAVE_CARD_PROMPT_NOT_INTERACTED, + // The prompt lost focus and was deactivated. + SAVE_CARD_PROMPT_LOST_FOCUS, + NUM_SAVE_CARD_PROMPT_RESULT_METRICS, + }; + // Metrics to measure user interaction with the save credit card prompt. // // SAVE_CARD_PROMPT_DISMISS_FOCUS is not stored explicitly, but can be @@ -244,7 +268,7 @@ class AutofillMetrics { // location bar icon being clicked while bubble is hidden (reshows). SAVE_CARD_PROMPT_SHOW_REQUESTED, // The prompt was shown successfully. - SAVE_CARD_PROMPT_SHOWN, + SAVE_CARD_PROMPT_SHOWN_DEPRECATED, // The prompt was not shown because the legal message was invalid. SAVE_CARD_PROMPT_END_INVALID_LEGAL_MESSAGE, // The user explicitly accepted the prompt. @@ -966,6 +990,21 @@ class AutofillMetrics { kMaxValue = LINE1_ZIP_STATE_CITY_REQUIREMENT_VIOLATED, }; + // To record if the value in an autofilled field was edited by the user. + enum class AutofilledFieldUserEditingStatusMetric { + AUTOFILLED_FIELD_WAS_EDITED = 0, + AUTOFILLED_FIELD_WAS_NOT_EDITED = 1, + kMaxValue = AUTOFILLED_FIELD_WAS_NOT_EDITED, + }; + + // Represent the overall status of a profile import. + enum class AddressProfileImportStatusMetric { + NO_IMPORT = 0, + REGULAR_IMPORT = 1, + SECTION_UNION_IMPORT = 2, + kMaxValue = SECTION_UNION_IMPORT, + }; + // Utility to log URL keyed form interaction events. class FormInteractionsUkmLogger { public: @@ -999,6 +1038,8 @@ class AutofillMetrics { const AutofillField& field); void LogTextFieldDidChange(const FormStructure& form, const AutofillField& field); + void LogEditedAutofilledFieldAtSubmission(const FormStructure& form, + const AutofillField& field); void LogFieldFillStatus(const FormStructure& form, const AutofillField& field, QualityMetricType metric_type); @@ -1107,6 +1148,22 @@ class AutofillMetrics { static void LogCreditCardFillingInfoBarMetric(InfoBarMetric metric); static void LogSaveCardRequestExpirationDateReasonMetric( SaveCardRequestExpirationDateReasonMetric metric); + static void LogSaveCardPromptOfferMetric( + SaveCardPromptOfferMetric metric, + bool is_uploading, + bool is_reshow, + AutofillClient::SaveCreditCardOptions options, + int previous_save_credit_card_prompt_user_decision, + security_state::SecurityLevel security_level, + AutofillSyncSigninState sync_state); + static void LogSaveCardPromptResultMetric( + SaveCardPromptResultMetric metric, + bool is_uploading, + bool is_reshow, + AutofillClient::SaveCreditCardOptions options, + int previous_save_credit_card_prompt_user_decision, + security_state::SecurityLevel security_level, + AutofillSyncSigninState sync_state); static void LogSaveCardPromptMetric( SaveCardPromptMetric metric, bool is_uploading, @@ -1119,6 +1176,7 @@ class AutofillMetrics { SaveCardPromptMetric metric, bool is_uploading, security_state::SecurityLevel security_level); + static void LogCreditCardUploadLegalMessageLinkClicked(); static void LogCreditCardUploadFeedbackMetric( CreditCardUploadFeedbackMetric metric); static void LogManageCardsPromptMetric(ManageCardsPromptMetric metric, @@ -1534,6 +1592,15 @@ class AutofillMetrics { bool is_city_missing, bool is_line1_missing); + // Records if an autofilled field of a specific type was edited by the user. + static void LogEditedAutofilledFieldAtSubmission( + FormInteractionsUkmLogger* form_interactions_ukm_logger, + const FormStructure& form, + const AutofillField& field); + + static void LogAddressFormImportStatustMetric( + AddressProfileImportStatusMetric metric); + static const char* GetMetricsSyncStateSuffix( AutofillSyncSigninState sync_state); @@ -1545,6 +1612,11 @@ class AutofillMetrics { DISALLOW_IMPLICIT_CONSTRUCTORS(AutofillMetrics); }; -} // namespace autofill +#if defined(UNIT_TEST) +int GetFieldTypeUserEditStatusMetric( + ServerFieldType server_type, + AutofillMetrics::AutofilledFieldUserEditingStatusMetric metric); +#endif +} // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_METRICS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index 5fad6416589..f5c0ffec08d 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -51,6 +51,7 @@ #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/renderer_id.h" #include "components/autofill/core/common/signatures.h" #include "components/prefs/pref_service.h" #include "components/sync/driver/test_sync_service.h" @@ -100,6 +101,8 @@ using UkmFormSubmittedType = ukm::builders::Autofill_FormSubmitted; using UkmFieldTypeValidationType = ukm::builders::Autofill_FieldTypeValidation; using UkmFieldFillStatusType = ukm::builders::Autofill_FieldFillStatus; using UkmFormEventType = ukm::builders::Autofill_FormEvent; +using UkmEditedAutofilledFieldAtSubmission = + ukm::builders::Autofill_EditedAutofilledFieldAtSubmission; using ExpectedUkmMetricsRecord = std::vector>; using ExpectedUkmMetrics = std::vector; @@ -117,6 +120,13 @@ FieldSignature Collapse(FieldSignature sig) { return FieldSignature(sig.value() % 1021); } +// Returns numbers which are distinct from each other within the scope of one +// test. +FormRendererId MakeFormRendererId() { + static uint32_t counter = 10; + return FormRendererId(counter++); +} + struct AddressProfileImportRequirementExpectations { AddressImportRequirements requirement; bool fulfilled; @@ -295,8 +305,9 @@ class MockAutofillClient : public TestAutofillClient { } // namespace // This is defined in the autofill_metrics.cc implementation file. -int GetFieldTypeGroupMetric(ServerFieldType field_type, - AutofillMetrics::FieldTypeQualityMetric metric); +int GetFieldTypeGroupPredictionQualityMetric( + ServerFieldType field_type, + AutofillMetrics::FieldTypeQualityMetric metric); class AutofillMetricsTest : public testing::Test { public: @@ -615,6 +626,7 @@ INSTANTIATE_TEST_SUITE_P(AutofillMetricsTest, TEST_F(AutofillMetricsTest, QualityMetrics) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -681,48 +693,50 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE, 2); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::TRUE_POSITIVE), + 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE), 1); // Mismatch: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_NUMBER, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); // False Positive Unknown: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_NUMBER, - AutofillMetrics::FALSE_POSITIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), 1); // False Positive Empty: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, - AutofillMetrics::FALSE_POSITIVE_EMPTY), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY), 1); // Sanity Check: @@ -744,33 +758,34 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE, 2); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE), 1); // Mismatch: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FIRST, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); // False Positive Unknown: @@ -778,16 +793,16 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, - AutofillMetrics::FALSE_POSITIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), 1); // False Positive Empty: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FIRST, - AutofillMetrics::FALSE_POSITIVE_EMPTY), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY), 1); // Sanity Check: @@ -796,6 +811,253 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { } } +// Test that the ProfileImportStatus logs a no import. +TEST_F(AutofillMetricsTest, ProfileImportStatus_NoImport) { + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.url = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + std::vector heuristic_types, server_types; + FormFieldData field; + + test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", + &field); + form.fields.push_back(field); + heuristic_types.push_back(NAME_FULL); + server_types.push_back(NAME_FULL); + + test::CreateTestFormField("Address", "home_line_one", + "3734 Elvis Presley Blvd.", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_LINE1); + server_types.push_back(ADDRESS_HOME_LINE1); + + test::CreateTestFormField("City", "city", "New York", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_CITY); + server_types.push_back(ADDRESS_HOME_CITY); + + test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + + test::CreateTestFormField("State", "state", "InvalidState", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_STATE); + server_types.push_back(ADDRESS_HOME_STATE); + + test::CreateTestFormField("ZIP", "zip", "00000000000000000", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_ZIP); + server_types.push_back(ADDRESS_HOME_ZIP); + + test::CreateTestFormField("Country", "country", "NoACountry", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_COUNTRY); + server_types.push_back(ADDRESS_HOME_COUNTRY); + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + std::string guid(kTestGuid); + autofill_manager_->FillOrPreviewForm( + AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), + autofill_manager_->MakeFrontendIDForTest(std::string(), guid)); + + // Simulate form submission. + base::HistogramTester histogram_tester; + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + std::string histogram = "Autofill.AddressProfileImportStatus"; + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0); + histogram_tester.ExpectBucketCount( + histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, + 1); + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, + 0); +} + +// Test that the ProfileImportStatus logs a regular import. +TEST_F(AutofillMetricsTest, ProfileImportStatus_RegularImport) { + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.url = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + std::vector heuristic_types, server_types; + FormFieldData field; + + test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", + &field); + form.fields.push_back(field); + heuristic_types.push_back(NAME_FULL); + server_types.push_back(NAME_FULL); + + test::CreateTestFormField("Address", "home_line_one", + "3734 Elvis Presley Blvd.", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_LINE1); + server_types.push_back(ADDRESS_HOME_LINE1); + + test::CreateTestFormField("City", "city", "New York", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_CITY); + server_types.push_back(ADDRESS_HOME_CITY); + + test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + + test::CreateTestFormField("State", "state", "CA", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_STATE); + server_types.push_back(ADDRESS_HOME_STATE); + + test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_ZIP); + server_types.push_back(ADDRESS_HOME_ZIP); + + test::CreateTestFormField("Country", "country", "USA", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_COUNTRY); + server_types.push_back(ADDRESS_HOME_COUNTRY); + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + std::string guid(kTestGuid); + autofill_manager_->FillOrPreviewForm( + AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), + autofill_manager_->MakeFrontendIDForTest(std::string(), guid)); + + // Simulate form submission. + base::HistogramTester histogram_tester; + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + std::string histogram = "Autofill.AddressProfileImportStatus"; + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 1); + histogram_tester.ExpectBucketCount( + histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, + 0); + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, + 0); +} + +// Test that the ProfileImportStatus logs a section union mport. +TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) { + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.url = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + std::vector heuristic_types, server_types; + FormFieldData field; + + test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", + &field); + form.fields.push_back(field); + heuristic_types.push_back(NAME_FULL); + server_types.push_back(NAME_FULL); + + test::CreateTestFormField("Address", "home_line_one", + "3734 Elvis Presley Blvd.", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_LINE1); + server_types.push_back(ADDRESS_HOME_LINE1); + + test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_ZIP); + server_types.push_back(ADDRESS_HOME_ZIP); + + test::CreateTestFormField("Country", "country", "USA", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_COUNTRY); + server_types.push_back(ADDRESS_HOME_COUNTRY); + + test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + + test::CreateTestFormField("City", "city", "New York", "text", &field); + // Assign a specific section. + field.autocomplete_attribute = "section-billing locality"; + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_CITY); + server_types.push_back(ADDRESS_HOME_CITY); + + test::CreateTestFormField("State", "state", "CA", "text", &field); + // Make the state a different section than the city. + field.autocomplete_attribute = "section-shipping address-level1"; + form.fields.push_back(field); + heuristic_types.push_back(ADDRESS_HOME_STATE); + server_types.push_back(ADDRESS_HOME_STATE); + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + std::string guid(kTestGuid); + autofill_manager_->FillOrPreviewForm( + AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), + autofill_manager_->MakeFrontendIDForTest(std::string(), guid)); + + base::HistogramTester histogram_tester; + std::string histogram = "Autofill.AddressProfileImportStatus"; + + // Disable the union import feature. + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillProfileImportFromUnifiedSection); + + // Simulate form submission. + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0); + histogram_tester.ExpectBucketCount( + histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, + 1); + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, + 0); + + // Enable the union import feature. + scoped_feature_list_.Reset(); + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillProfileImportFromUnifiedSection); + // Simulate form submission. + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0); + histogram_tester.ExpectBucketCount( + histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, + 1); + histogram_tester.ExpectBucketCount( + histogram, + AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, + 1); +} + // Test that the ProfileImportRequirements are all counted as fulfilled for a // 'perfect' profile import. TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilled) { @@ -1427,6 +1689,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_LoggedCorrecltyForRationalizationOk) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -1507,6 +1770,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_LoggedCorrecltyForRationalizationGood) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -1574,6 +1838,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -1716,6 +1981,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -1765,7 +2031,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { ASSERT_TRUE(response.SerializeToString(&response_string)); FormStructure::ParseQueryResponse( - response_string, forms, + response_string, forms, test::GetEncodedSignatures(forms), autofill_manager_->form_interactions_ukm_logger_for_test()); ASSERT_EQ(test_ukm_recorder_ @@ -1823,6 +2089,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -1880,7 +2147,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { ASSERT_TRUE(response.SerializeToString(&response_string)); FormStructure::ParseQueryResponse( - response_string, forms, + response_string, forms, test::GetEncodedSignatures(forms), autofill_manager_->form_interactions_ukm_logger_for_test()); ASSERT_EQ(test_ukm_recorder_ @@ -1960,6 +2227,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_LoggedCorrecltyForRationalizationBad) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2026,6 +2294,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_LoggedCorrecltyForOnlyFillWhenFocusedField) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2115,16 +2384,18 @@ TEST_F(AutofillMetricsTest, AutofillMetrics::TRUE_POSITIVE, 4); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::TRUE_POSITIVE), + 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_LINE1, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE), 2); // TRUE_NEGATIVE_EMPTY histogram_tester.ExpectBucketCount(aggregate_histogram, @@ -2134,8 +2405,8 @@ TEST_F(AutofillMetricsTest, aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); // Sanity Check: histogram_tester.ExpectTotalCount(aggregate_histogram, 6); @@ -2156,16 +2427,18 @@ TEST_F(AutofillMetricsTest, AutofillMetrics::TRUE_POSITIVE, 4); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::TRUE_POSITIVE), + 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_LINE1, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE), 2); // TRUE_NEGATIVE_EMPTY histogram_tester.ExpectBucketCount(aggregate_histogram, @@ -2175,8 +2448,9 @@ TEST_F(AutofillMetricsTest, aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_CITY_AND_NUMBER, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_CITY_AND_NUMBER, + AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); // Sanity Check: histogram_tester.ExpectTotalCount(aggregate_histogram, 6); @@ -2327,6 +2601,7 @@ TEST_P(QualityMetricsTest, Classification) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2466,11 +2741,11 @@ TEST_P(QualityMetricsTest, Classification) { aggregate_expected_count); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(actual_field_type, metric), + GetFieldTypeGroupPredictionQualityMetric(actual_field_type, metric), expected_count_for_actual_type); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(predicted_type, metric), + GetFieldTypeGroupPredictionQualityMetric(predicted_type, metric), expected_count_for_predicted_type); } } @@ -2502,6 +2777,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { base::HistogramTester histogram_tester; FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2543,6 +2819,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2612,47 +2889,49 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE, 2); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::TRUE_POSITIVE), + 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE), 1); // Mismatch: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_NUMBER, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); // False Positives: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, - AutofillMetrics::FALSE_POSITIVE_EMPTY), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY), 1); histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_NUMBER, - AutofillMetrics::FALSE_POSITIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), 1); // Sanity Check: @@ -2674,33 +2953,34 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_COUNTRY, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE, 2); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER, - AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE), 1); // Mismatch: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FIRST, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); // False Positives: @@ -2708,15 +2988,15 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FIRST, - AutofillMetrics::FALSE_POSITIVE_EMPTY), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY), 1); histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, - AutofillMetrics::FALSE_POSITIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), 1); // Sanity Check: @@ -2729,6 +3009,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { // on autocomplete attributes present on the fields. TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("MyForm"); form.url = GURL("http://myform.com/form.html"); form.action = GURL("http://myform.com/submit.html"); @@ -2761,7 +3042,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { TestFormStructure* form_structure_ptr = form_structure.get(); form_structure->DetermineHeuristicTypes(); ASSERT_TRUE(autofill_manager_->mutable_form_structures_for_test() - ->emplace(form_structure_ptr->form_signature(), + ->emplace(form_structure_ptr->unique_renderer_id(), std::move(form_structure)) .second); @@ -2785,12 +3066,9 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { std::string encoded_response_string; base::Base64Encode(response_string, &encoded_response_string); - std::vector signatures; - signatures.push_back(form_structure_ptr->FormSignatureAsStr()); - base::HistogramTester histogram_tester; - autofill_manager_->OnLoadedServerPredictionsForTest(encoded_response_string, - signatures); + autofill_manager_->OnLoadedServerPredictionsForTest( + encoded_response_string, test::GetEncodedSignatures(*form_structure_ptr)); // Verify that FormStructure::ParseQueryResponse was called (here and below). histogram_tester.ExpectBucketCount("Autofill.ServerQueryResponse", @@ -2819,27 +3097,29 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_ZIP, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::TRUE_POSITIVE, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_LAST, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_LAST, AutofillMetrics::TRUE_POSITIVE), + 1); // Mismatch: histogram_tester.ExpectBucketCount( aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FIRST, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_MIDDLE, - AutofillMetrics::FALSE_POSITIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + NAME_MIDDLE, AutofillMetrics::FALSE_POSITIVE_MISMATCH), 1); // Sanity check. @@ -2852,6 +3132,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2911,6 +3192,7 @@ TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { // seen/parsed, and the time it is submitted. FormData form; FormFieldData field; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -2948,7 +3230,7 @@ TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { SCOPED_TRACE("ADDRESS_HOME_COUNTRY"); histogram_tester.ExpectBucketCount( histogram_name, - GetFieldTypeGroupMetric( + GetFieldTypeGroupPredictionQualityMetric( ADDRESS_HOME_COUNTRY, source == "Overall" ? AutofillMetrics::TRUE_POSITIVE : AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), @@ -2961,8 +3243,8 @@ TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { SCOPED_TRACE("ADDRESS_HOME_ZIP"); histogram_tester.ExpectBucketCount( histogram_name, - GetFieldTypeGroupMetric(ADDRESS_HOME_ZIP, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_ZIP, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); } @@ -2971,10 +3253,10 @@ TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { SCOPED_TRACE("PHONE_HOME_WHOLE_NUMBER"); histogram_tester.ExpectBucketCount( histogram_name, - GetFieldTypeGroupMetric(PHONE_HOME_WHOLE_NUMBER, - source == "Server" - ? AutofillMetrics::FALSE_NEGATIVE_UNKNOWN - : AutofillMetrics::TRUE_POSITIVE), + GetFieldTypeGroupPredictionQualityMetric( + PHONE_HOME_WHOLE_NUMBER, + source == "Server" ? AutofillMetrics::FALSE_NEGATIVE_UNKNOWN + : AutofillMetrics::TRUE_POSITIVE), 1); } @@ -2988,6 +3270,7 @@ TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3052,8 +3335,8 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_STATE, - AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_STATE, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), 1); // Match: histogram_tester.ExpectBucketCount(aggregate_histogram, @@ -3061,23 +3344,25 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { source == "Heuristic" ? 2 : 1); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TRUE_POSITIVE), 1); + GetFieldTypeGroupPredictionQualityMetric( + NAME_FULL, AutofillMetrics::TRUE_POSITIVE), + 1); // Mismatch: histogram_tester.ExpectBucketCount(aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, source == "Heuristic" ? 1 : 2); histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(EMAIL_ADDRESS, - AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); // Source dependent: histogram_tester.ExpectBucketCount( by_field_type_histogram, - GetFieldTypeGroupMetric(ADDRESS_HOME_CITY, - source == "Heuristic" - ? AutofillMetrics::TRUE_POSITIVE - : AutofillMetrics::FALSE_NEGATIVE_MISMATCH), + GetFieldTypeGroupPredictionQualityMetric( + ADDRESS_HOME_CITY, source == "Heuristic" + ? AutofillMetrics::TRUE_POSITIVE + : AutofillMetrics::FALSE_NEGATIVE_MISMATCH), 1); } } @@ -3087,6 +3372,7 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) { // Construct a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3123,6 +3409,7 @@ TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { features.InitAndEnableFeature(kAutofillEnforceMinRequiredFieldsForHeuristics); // Construct a non-fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3150,11 +3437,150 @@ TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { "Autofill.StoredProfileCountAtAutofillableFormSubmission", 0); } +// Verify that when submitting an autofillable form, the proper tppe of +// the edited fields is correctly logged to UKM. +TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUkmLogging) { + // Construct a fillable form. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.url = GURL("https://example.test/form.html"); + form.action = GURL("https://example.test/submit.html"); + form.main_frame_origin = + url::Origin::Create(GURL("https://example.test/form.html")); + + std::vector heuristic_types, server_types; + + FormFieldData field; + test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley", + "text", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(NAME_FULL); + server_types.push_back(NAME_FULL); + + test::CreateTestFormField("Autofill Failed", "autofillfailed", + "buddy@gmail.com", "text", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(EMAIL_ADDRESS); + server_types.push_back(EMAIL_ADDRESS); + + test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + + // Verify that there are no counts before form submission. + + EXPECT_EQ(0U, test_ukm_recorder_->entries_count()); + + base::HistogramTester histogram_tester; + // Simulate text input in the first and second fields. + autofill_manager_->OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), + TimeTicks()); + + // Simulate form submission. + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + ExpectedUkmMetricsRecord name_field_ukm_record{ + {UkmEditedAutofilledFieldAtSubmission::kFieldSignatureName, + Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, + {UkmEditedAutofilledFieldAtSubmission::kFormSignatureName, + Collapse(CalculateFormSignature(form)).value()}, + {UkmEditedAutofilledFieldAtSubmission::kOverallTypeName, + static_cast(NAME_FULL)}}; + + VerifyUkm(test_ukm_recorder_, form, + UkmEditedAutofilledFieldAtSubmission::kEntryName, + {name_field_ukm_record}); +} + +// Verify that when submitting an autofillable form, the proper tppe of +// the edited fields is correctly logged to UMA. +TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUmaLogging) { + // Construct a fillable form. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.url = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + form.main_frame_origin = + url::Origin::Create(GURL("http://example_root.com/form.html")); + + std::vector heuristic_types, server_types; + + FormFieldData field; + test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley", + "text", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(NAME_FULL); + server_types.push_back(NAME_FULL); + + test::CreateTestFormField("Autofill Failed", "autofillfailed", + "buddy@gmail.com", "text", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(EMAIL_ADDRESS); + server_types.push_back(EMAIL_ADDRESS); + + test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field); + field.is_autofilled = true; + form.fields.push_back(field); + heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + + base::HistogramTester histogram_tester; + // Simulate text input in the first and second fields. + autofill_manager_->OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), + TimeTicks()); + autofill_manager_->OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), + TimeTicks()); + + // Simulate form submission. + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + // The |NAME_FULL| field was edited (bucket 112). + histogram_tester.ExpectBucketCount( + "Autofill.EditedAutofilledFieldAtSubmission.ByFieldType", 112, 1); + + // The |NAME_FULL| field was edited (bucket 144). + histogram_tester.ExpectBucketCount( + "Autofill.EditedAutofilledFieldAtSubmission.ByFieldType", 144, 1); + + // The |PHONE_HOME_CITY_AND_NUMBER| field was not edited (bucket 209). + histogram_tester.ExpectBucketCount( + "Autofill.EditedAutofilledFieldAtSubmission.ByFieldType", 209, 1); + + // The aggregated histogram should have two counts on edited fields. + histogram_tester.ExpectBucketCount( + "Autofill.EditedAutofilledFieldAtSubmission.Aggregate", 0, 2); + + // The aggregated histogram should have one count on accepted fields. + histogram_tester.ExpectBucketCount( + "Autofill.EditedAutofilledFieldAtSubmission.Aggregate", 1, 1); + + // The autocomplete!=off histogram should have one count on accepted fields. + histogram_tester.ExpectBucketCount( + "Autofill.Autocomplete.NotOff.EditedAutofilledFieldAtSubmission.Address", + 1, 1); + + // The autocomplete!=off histogram should have no count on accepted fields. + histogram_tester.ExpectTotalCount( + "Autofill.Autocomplete.Off.EditedAutofilledFieldAtSubmission.Address", 0); +} + // Verify that when submitting an autofillable form, the proper number of edited // fields is logged. TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) { // Construct a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3209,6 +3635,7 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) { TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) { // Construct a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3258,6 +3685,7 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) { TEST_F(AutofillMetricsTest, DeveloperEngagement) { // Start with a non-fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3362,6 +3790,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogFillableFormParsedWithoutTypeHints) { // Start with a non-fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3408,6 +3837,7 @@ TEST_F(AutofillMetricsTest, TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogFillableFormParsedWithTypeHints) { FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3465,6 +3895,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { // Disabled. {}); FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3634,6 +4065,67 @@ TEST_F(AutofillMetricsTest, LogStoredCreditCardMetrics) { "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 200, 3); } +TEST_F(AutofillMetricsTest, LogStoredCreditCardWithNicknameMetrics) { + std::vector> local_cards; + std::vector> server_cards; + local_cards.reserve(2); + server_cards.reserve(4); + + // Create cards with and without nickname of each record type: 1 of each for + // local, 2 of each for masked. + const std::vector record_types{ + CreditCard::LOCAL_CARD, CreditCard::MASKED_SERVER_CARD}; + int num_cards_of_type = 0; + for (auto record_type : record_types) { + num_cards_of_type += 1; + for (int i = 0; i < num_cards_of_type; ++i) { + // Create a card with a nickname. + CreditCard card_with_nickname = test::GetRandomCreditCard(record_type); + card_with_nickname.SetNickname(ASCIIToUTF16("Valid nickname")); + + // Create a card that doesn't have a nickname. + CreditCard card_without_nickname = test::GetRandomCreditCard(record_type); + // Set nickname to empty. + card_without_nickname.SetNickname(ASCIIToUTF16("")); + + // Add the cards to the personal data manager in the appropriate way. + auto& repo = + (record_type == CreditCard::LOCAL_CARD) ? local_cards : server_cards; + repo.push_back( + std::make_unique(std::move(card_with_nickname))); + repo.push_back( + std::make_unique(std::move(card_without_nickname))); + } + } + + // Log the stored credit card metrics for the cards configured above. + base::HistogramTester histogram_tester; + AutofillMetrics::LogStoredCreditCardMetrics(local_cards, server_cards, + base::TimeDelta::FromDays(180)); + + // Validate the count metrics. + histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount", 1); + histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Local", 1); + histogram_tester.ExpectTotalCount( + "Autofill.StoredCreditCardCount.Local.WithNickname", 1); + histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Server", 1); + histogram_tester.ExpectTotalCount( + "Autofill.StoredCreditCardCount.Server.Masked", 1); + histogram_tester.ExpectTotalCount( + "Autofill.StoredCreditCardCount.Server.Masked.WithNickname", 1); + histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount", 6, 1); + histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Local", 2, + 1); + histogram_tester.ExpectBucketCount( + "Autofill.StoredCreditCardCount.Local.WithNickname", 1, 1); + histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Server", 4, + 1); + histogram_tester.ExpectBucketCount( + "Autofill.StoredCreditCardCount.Server.Masked", 4, 1); + histogram_tester.ExpectBucketCount( + "Autofill.StoredCreditCardCount.Server.Masked.WithNickname", 2, 1); +} + // Test that we correctly log when Profile Autofill is enabled at startup. TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtStartup) { base::HistogramTester histogram_tester; @@ -3698,6 +4190,7 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtStartup) { TEST_F(AutofillMetricsTest, AddressSuggestionsCount) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3770,6 +4263,7 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) { TEST_P(AutofillMetricsCompanyTest, CompanyNameSuggestions) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3814,6 +4308,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -3999,6 +4494,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { // Test that the UPI Checkout flow form submit is correctly logged TEST_F(AutofillMetricsTest, UpiVpaUkmTest) { FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4030,6 +4526,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4171,6 +4668,7 @@ TEST_F(AutofillMetricsTest, PolledCreditCardSuggestions_DebounceLogs) { // Set up the form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://www.foo.com/"); @@ -4231,6 +4729,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { // Set up the form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4250,6 +4749,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { { // Simulate having seen this insecure form on page load. + form.unique_renderer_id = MakeFormRendererId(); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); form.main_frame_origin = @@ -4269,6 +4769,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { { // Simulate having seen this secure form on page load. autofill_manager_->Reset(); + form.unique_renderer_id = MakeFormRendererId(); form.url = GURL("https://example.com/form.html"); form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = @@ -4292,6 +4793,7 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) { // Set up the form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4349,6 +4851,7 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) { TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4381,6 +4884,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) { TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4437,6 +4941,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) { TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4495,6 +5000,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) { TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4577,6 +5083,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) { true /* include_full_server_credit_card */); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4658,6 +5165,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) { true /* include_full_server_credit_card */); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4910,6 +5418,7 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration) { false /* include_full_server_credit_card */); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -4979,6 +5488,7 @@ TEST_F(AutofillMetricsTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5021,6 +5531,7 @@ TEST_P(AutofillMetricsIFrameTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5065,6 +5576,7 @@ TEST_P(AutofillMetricsIFrameTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5110,6 +5622,7 @@ TEST_P(AutofillMetricsIFrameTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5156,6 +5669,7 @@ TEST_P(AutofillMetricsIFrameTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5202,6 +5716,7 @@ TEST_P(AutofillMetricsIFrameTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5261,6 +5776,7 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5302,6 +5818,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { true /* include_full_server_credit_card */); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -5796,6 +6313,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { true /* include_full_server_credit_card */); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6456,6 +6974,7 @@ TEST_P(AutofillMetricsServerNicknameTest, LogServerNicknameSelectionDuration) { TEST_F(AutofillMetricsTest, MixedParsedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6499,6 +7018,7 @@ TEST_F(AutofillMetricsTest, MixedParsedFormEvents) { TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6541,6 +7061,7 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6617,6 +7138,7 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6710,6 +7232,7 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6821,6 +7344,7 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -6940,6 +7464,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7152,6 +7677,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { RecreateProfile(/*is_server=*/false); // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7336,6 +7862,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7447,6 +7974,7 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) { TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7556,6 +8084,7 @@ TEST_F(AutofillMetricsTest, DaysSinceLastUse_Profile) { TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Start with a form with insufficiently many fields. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7859,6 +8388,7 @@ TEST_F( AutofillMetricsTest, AutofillFormSubmittedState_DontCountUnfilledFieldsWithOnlyFillWhenFocused) { FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -7994,6 +8524,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_UnknownForm) { TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -8020,6 +8551,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("https://example.com/form.html"); form.action = GURL("https://example.com/submit.html"); @@ -8189,6 +8721,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -8443,6 +8976,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -8460,6 +8994,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Fill additional form. FormData second_form = form; + second_form.unique_renderer_id = MakeFormRendererId(); test::CreateTestFormField("Second Phone", "second_phone", "", "text", &field); second_form.fields.push_back(field); @@ -8864,6 +9399,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -8990,6 +9526,7 @@ class AutofillMetricsParseQueryResponseTest : public testing::Test { public: void SetUp() override { FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.url = GURL("http://foo.com"); form.main_frame_origin = url::Origin::Create(GURL("http://foo_root.com")); FormFieldData field; @@ -9043,7 +9580,8 @@ TEST_F(AutofillMetricsParseQueryResponseTest, ServerHasData) { ASSERT_TRUE(response.SerializeToString(&response_string)); base::HistogramTester histogram_tester; - FormStructure::ParseQueryResponse(response_string, forms_, nullptr); + FormStructure::ParseQueryResponse( + response_string, forms_, test::GetEncodedSignatures(forms_), nullptr); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), ElementsAre(Bucket(true, 2))); @@ -9062,7 +9600,8 @@ TEST_F(AutofillMetricsParseQueryResponseTest, OneFormNoServerData) { ASSERT_TRUE(response.SerializeToString(&response_string)); base::HistogramTester histogram_tester; - FormStructure::ParseQueryResponse(response_string, forms_, nullptr); + FormStructure::ParseQueryResponse( + response_string, forms_, test::GetEncodedSignatures(forms_), nullptr); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), ElementsAre(Bucket(false, 1), Bucket(true, 1))); @@ -9080,7 +9619,8 @@ TEST_F(AutofillMetricsParseQueryResponseTest, AllFormsNoServerData) { ASSERT_TRUE(response.SerializeToString(&response_string)); base::HistogramTester histogram_tester; - FormStructure::ParseQueryResponse(response_string, forms_, nullptr); + FormStructure::ParseQueryResponse( + response_string, forms_, test::GetEncodedSignatures(forms_), nullptr); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), ElementsAre(Bucket(false, 2))); @@ -9099,7 +9639,8 @@ TEST_F(AutofillMetricsParseQueryResponseTest, PartialNoServerData) { ASSERT_TRUE(response.SerializeToString(&response_string)); base::HistogramTester histogram_tester; - FormStructure::ParseQueryResponse(response_string, forms_, nullptr); + FormStructure::ParseQueryResponse( + response_string, forms_, test::GetEncodedSignatures(forms_), nullptr); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), ElementsAre(Bucket(true, 2))); @@ -9114,6 +9655,7 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -9172,6 +9714,7 @@ TEST_F(AutofillMetricsTest, // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("https://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -9296,6 +9839,7 @@ TEST_F(AutofillMetricsTest, DISABLED_AutofillSuggestionShownTest) { false /* include_masked_server_credit_card */, false /* include_full_server_credit_card */); FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example_cc.com/form.html"); form.action = GURL("http://example_cc.com/submit.html"); @@ -9335,6 +9879,7 @@ TEST_F(AutofillMetricsTest, DynamicFormMetrics) { // Set up our form data. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -9434,10 +9979,10 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( AutofillMetrics::USER_DID_AUTOFILL_ONCE, UNKNOWN_FORM_TYPE, - security_state::SecurityLevel::EV_SECURE); - histogram_tester.ExpectBucketCount( - "Autofill.UserHappiness.Unknown.EV_SECURE", - AutofillMetrics::USER_DID_AUTOFILL_ONCE, 1); + security_state::SecurityLevel::SECURE); + histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Unknown.SECURE", + AutofillMetrics::USER_DID_AUTOFILL_ONCE, + 1); } { @@ -9457,6 +10002,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -9718,11 +10264,11 @@ TEST_F(AutofillMetricsTest, LogSaveCardPromptMetricBySecurityLevel) { { base::HistogramTester histogram_tester; AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, /*is_uploading=*/true, - security_state::SecurityLevel::SECURE); + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, + /*is_uploading=*/true, security_state::SecurityLevel::SECURE); histogram_tester.ExpectBucketCount( "Autofill.SaveCreditCardPrompt.Upload.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1); + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); } { @@ -9749,9 +10295,9 @@ TEST_F(AutofillMetricsTest, LogSaveCardPromptMetricBySecurityLevel) { base::HistogramTester histogram_tester; AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, - /*is_uploading=*/false, security_state::SecurityLevel::EV_SECURE); + /*is_uploading=*/false, security_state::SecurityLevel::SECURE); histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Local.EV_SECURE", + "Autofill.SaveCreditCardPrompt.Local.SECURE", AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); } @@ -9779,22 +10325,23 @@ TEST_F(AutofillMetricsTest, /*is_uploading=*/true, /*is_reshow=*/false, AutofillClient::SaveCreditCardOptions(), /*previous_save_credit_card_prompt_user_decision=*/1, - security_state::SecurityLevel::EV_SECURE, SyncSigninState::kSignedOut); + security_state::SecurityLevel::SECURE, SyncSigninState::kSignedOut); histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.EV_SECURE", + "Autofill.SaveCreditCardPrompt.Upload.SECURE", AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); } { base::HistogramTester histogram_tester; AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, /*is_uploading=*/false, + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, + /*is_uploading=*/false, /*is_reshow=*/true, AutofillClient::SaveCreditCardOptions(), /*previous_save_credit_card_prompt_user_decision=*/0, security_state::SecurityLevel::SECURE, SyncSigninState::kSignedOut); histogram_tester.ExpectBucketCount( "Autofill.SaveCreditCardPrompt.Local.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1); + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); } } @@ -9963,15 +10510,17 @@ TEST_F(AutofillMetricsTest, LogSaveCardPromptMetric_BySyncState) { /*is_uploading=*/true, /*is_reshow=*/false, AutofillClient::SaveCreditCardOptions(), /*previous_save_credit_card_prompt_user_decision=*/1, - security_state::SecurityLevel::EV_SECURE, SyncSigninState::kSignedIn); + security_state::SecurityLevel::SECURE, SyncSigninState::kSignedIn); histogram_tester.ExpectBucketCount( "Autofill.SaveCreditCardPrompt.Upload.FirstShow.SignedIn", AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); } + { base::HistogramTester histogram_tester; AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, /*is_uploading=*/false, + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, + /*is_uploading=*/false, /*is_reshow=*/true, AutofillClient::SaveCreditCardOptions(), /*previous_save_credit_card_prompt_user_decision=*/0, security_state::SecurityLevel::SECURE, @@ -9979,7 +10528,7 @@ TEST_F(AutofillMetricsTest, LogSaveCardPromptMetric_BySyncState) { histogram_tester.ExpectBucketCount( "Autofill.SaveCreditCardPrompt.Local.Reshows." "SignedInAndSyncFeatureEnabled", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1); + AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); } } @@ -10026,6 +10575,7 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { // Load a fillable form. FormData form; + form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); @@ -10136,6 +10686,10 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { "Autofill.KeyMetrics.FillingCorrectness.Address", 1, 1); histogram_tester.ExpectBucketCount( "Autofill.KeyMetrics.FillingAssistance.Address", 1, 1); + histogram_tester.ExpectBucketCount( + "Autofill.Autocomplete.NotOff.FillingAcceptance.Address", 1, 1); + histogram_tester.ExpectTotalCount( + "Autofill.Autocomplete.Off.FillingAcceptance.Address", 0); } else { histogram_tester.ExpectTotalCount( "Autofill.KeyMetrics.FillingReadiness.Address", 0); @@ -10145,6 +10699,10 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { "Autofill.KeyMetrics.FillingCorrectness.Address", 0); histogram_tester.ExpectTotalCount( "Autofill.KeyMetrics.FillingAssistance.Address", 0); + histogram_tester.ExpectTotalCount( + "Autofill.Autocomplete.NotOff.FillingAcceptance.Address", 0); + histogram_tester.ExpectTotalCount( + "Autofill.Autocomplete.Off.FillingAcceptance.Address", 0); } if (user_accepted_suggestion) { histogram_tester.ExpectBucketCount( @@ -10172,6 +10730,7 @@ void AutofillMetricsKeyMetricsTest::SetUp() { RecreateProfile(/*is_server=*/false); // Load a fillable form. + form_.unique_renderer_id = MakeFormRendererId(); form_.name = ASCIIToUTF16("TestForm"); form_.url = GURL("http://example.com/form.html"); form_.action = GURL("http://example.com/submit.html"); @@ -10373,4 +10932,18 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) { "Autofill.KeyMetrics.FormSubmission.Autofilled.Address", 0, 1); } +TEST_F(AutofillMetricsTest, GetFieldTypeUserEditStatusMetric) { + // The id of ADDRESS_HOME_COUNTRY is 36 = 0b10'0100. + ServerFieldType server_type = ADDRESS_HOME_COUNTRY; + // The id of AUTOFILL_FIELD_WAS_NOT_EDITED is 1. + AutofillMetrics::AutofilledFieldUserEditingStatusMetric metric = + AutofillMetrics::AutofilledFieldUserEditingStatusMetric:: + AUTOFILLED_FIELD_WAS_NOT_EDITED; + + int expected_result = 0b10'0100'0001; + int actual_result = + autofill::GetFieldTypeUserEditStatusMetric(server_type, metric); + EXPECT_EQ(expected_result, actual_result); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_provider.cc b/chromium/components/autofill/core/browser/autofill_provider.cc index 9fc3b8c0f43..8cea5630a05 100644 --- a/chromium/components/autofill/core/browser/autofill_provider.cc +++ b/chromium/components/autofill/core/browser/autofill_provider.cc @@ -19,4 +19,10 @@ void AutofillProvider::SendFormDataToRenderer(AutofillHandlerProxy* handler, requestId, AutofillDriver::FORM_DATA_ACTION_FILL, formData); } -} // namespace autofil +void AutofillProvider::RendererShouldAcceptDataListSuggestion( + AutofillHandlerProxy* handler, + const base::string16& value) { + handler->driver()->RendererShouldAcceptDataListSuggestion(value); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_provider.h b/chromium/components/autofill/core/browser/autofill_provider.h index 09f87a90ab9..6eae4082e4b 100644 --- a/chromium/components/autofill/core/browser/autofill_provider.h +++ b/chromium/components/autofill/core/browser/autofill_provider.h @@ -67,11 +67,18 @@ class AutofillProvider { const std::vector& forms, const base::TimeTicks timestamp) = 0; + virtual void OnHidePopup(AutofillHandlerProxy* handler) = 0; + virtual void Reset(AutofillHandlerProxy* handler) = 0; void SendFormDataToRenderer(AutofillHandlerProxy* handler, int requestId, const FormData& formData); + + // Notifies the renderer should accept the datalist suggestion given by + // |value| and fill the associated input field. + void RendererShouldAcceptDataListSuggestion(AutofillHandlerProxy* handler, + const base::string16& value); }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index bf871432b31..82b3b06f3b4 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -22,8 +22,10 @@ #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_prefs.h" +#include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/renderer_id.h" #include "components/os_crypt/os_crypt_mocker.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" @@ -50,6 +52,16 @@ std::string GetRandomCardNumber() { return value; } +FormRendererId MakeFormRendererId() { + static uint32_t counter = 10; + return FormRendererId(counter++); +} + +FieldRendererId MakeFieldRendererId() { + static uint32_t counter = 10; + return FieldRendererId(counter++); +} + } // namespace std::unique_ptr PrefServiceForTesting() { @@ -74,6 +86,7 @@ void CreateTestFormField(const char* label, const char* value, const char* type, FormFieldData* field) { + field->unique_renderer_id = MakeFieldRendererId(); field->label = ASCIIToUTF16(label); field->name = ASCIIToUTF16(name); field->value = ASCIIToUTF16(value); @@ -108,6 +121,27 @@ void CreateTestSelectField(const std::vector& values, CreateTestSelectField("", "", "", values, values, values.size(), field); } +void CreateTestDatalistField(const char* label, + const char* name, + const char* value, + const std::vector& values, + const std::vector& labels, + FormFieldData* field) { + // Fill the base attributes. + CreateTestFormField(label, name, value, "text", field); + + std::vector values16(values.size()); + for (size_t i = 0; i < values.size(); ++i) + values16[i] = base::UTF8ToUTF16(values[i]); + + std::vector label16(labels.size()); + for (size_t i = 0; i < labels.size(); ++i) + label16[i] = base::UTF8ToUTF16(labels[i]); + + field->datalist_values = values16; + field->datalist_labels = label16; +} + void CreateTestAddressFormData(FormData* form, const char* unique_id) { std::vector types; CreateTestAddressFormData(form, &types, unique_id); @@ -116,6 +150,7 @@ void CreateTestAddressFormData(FormData* form, const char* unique_id) { void CreateTestAddressFormData(FormData* form, std::vector* types, const char* unique_id) { + form->unique_renderer_id = MakeFormRendererId(); form->name = ASCIIToUTF16("MyForm") + ASCIIToUTF16(unique_id ? unique_id : ""); form->button_titles = { @@ -192,6 +227,7 @@ void CreateTestAddressFormData(FormData* form, void CreateTestPersonalInformationFormData(FormData* form, const char* unique_id) { + form->unique_renderer_id = MakeFormRendererId(); form->name = ASCIIToUTF16("MyForm") + ASCIIToUTF16(unique_id ? unique_id : ""); form->url = GURL("http://myform.com/form.html"); @@ -216,6 +252,7 @@ void CreateTestCreditCardFormData(FormData* form, bool use_month_type, bool split_names, const char* unique_id) { + form->unique_renderer_id = MakeFormRendererId(); form->name = ASCIIToUTF16("MyForm") + ASCIIToUTF16(unique_id ? unique_id : ""); if (is_https) { @@ -796,5 +833,28 @@ std::string TenYearsFromNow() { return base::NumberToString(now.year + 10); } +FormAndFieldSignatures GetEncodedSignatures(const FormStructure& form) { + FormAndFieldSignatures signatures; + signatures.emplace_back(form.form_signature(), + std::vector{}); + for (const auto& field : form) { + if (form.ShouldSkipFieldVisibleForTesting(*field)) + continue; + signatures.back().second.push_back(field->GetFieldSignature()); + } + return signatures; +} + +FormAndFieldSignatures GetEncodedSignatures( + const std::vector& forms) { + FormAndFieldSignatures all_signatures; + for (const FormStructure* form : forms) { + FormAndFieldSignatures form_signatures = GetEncodedSignatures(*form); + std::move(form_signatures.begin(), form_signatures.end(), + std::back_inserter(all_signatures)); + } + return all_signatures; +} + } // namespace test } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h index 900e51fcdb7..f51ea475c9f 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_test_utils.h @@ -14,6 +14,7 @@ #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" #include "components/autofill/core/browser/proto/server.pb.h" @@ -64,6 +65,14 @@ void CreateTestSelectField(const char* label, void CreateTestSelectField(const std::vector& values, FormFieldData* field); +// Provides a quick way to populate a datalist field. +void CreateTestDatalistField(const char* label, + const char* name, + const char* value, + const std::vector& values, + const std::vector& labels, + FormFieldData* field); + // Populates |form| with data corresponding to a simple address form. // Note that this actually appends fields to the form data, which can be useful // for building up more complex test forms. Another version of the function is @@ -274,6 +283,16 @@ void FillQueryField(AutofillPageQueryRequest_Form_Field* field, const char* name, const char* control_type); +// Creates the structure of signatures that would be encoded by +// FormStructure::EncodeUploadRequest() and FormStructure::EncodeQueryRequest() +// and consumed by FormStructure::ParseQueryResponse() and +// FormStructure::ParseApiQueryResponse(). +// +// Perhaps a neater way would be to move this to TestFormStructure. +FormAndFieldSignatures GetEncodedSignatures(const FormStructure& form); +FormAndFieldSignatures GetEncodedSignatures( + const std::vector& forms); + // 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/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index 19e9cbf009f..31428cacab9 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -10,9 +10,13 @@ namespace autofill { FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { switch (field_type) { + case NAME_HONORIFIC_PREFIX: case NAME_FIRST: case NAME_MIDDLE: case NAME_LAST: + case NAME_LAST_FIRST: + case NAME_LAST_SECOND: + case NAME_LAST_CONJUNCTION: case NAME_MIDDLE_INITIAL: case NAME_FULL: case NAME_SUFFIX: @@ -139,6 +143,7 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, HtmlFieldMode field_mode) { switch (field_type) { case HTML_TYPE_NAME: + case HTML_TYPE_HONORIFIC_PREFIX: case HTML_TYPE_GIVEN_NAME: case HTML_TYPE_ADDITIONAL_NAME: case HTML_TYPE_ADDITIONAL_NAME_INITIAL: @@ -318,6 +323,9 @@ ServerFieldType AutofillType::GetStorableType() const { case HTML_TYPE_NAME: return NAME_FULL; + case HTML_TYPE_HONORIFIC_PREFIX: + return NAME_HONORIFIC_PREFIX; + case HTML_TYPE_GIVEN_NAME: return NAME_FIRST; @@ -456,6 +464,8 @@ std::string AutofillType::ToString() const { break; case HTML_TYPE_NAME: return "HTML_TYPE_NAME"; + case HTML_TYPE_HONORIFIC_PREFIX: + return "HTML_TYPE_HONORIFIC_PREFIX"; case HTML_TYPE_GIVEN_NAME: return "HTML_TYPE_GIVEN_NAME"; case HTML_TYPE_ADDITIONAL_NAME: @@ -558,12 +568,20 @@ std::string AutofillType::ServerFieldTypeToString(ServerFieldType type) { return "UNKNOWN_TYPE"; case EMPTY_TYPE: return "EMPTY_TYPE"; + case NAME_HONORIFIC_PREFIX: + return "NAME_HONORIFIC_PREFIX"; case NAME_FIRST: return "NAME_FIRST"; case NAME_MIDDLE: return "NAME_MIDDLE"; case NAME_LAST: return "NAME_LAST"; + case NAME_LAST_FIRST: + return "NAME_LAST_FIRST"; + case NAME_LAST_CONJUNCTION: + return "NAME_LAST_CONJUNCTION"; + case NAME_LAST_SECOND: + return "NAME_LAST_SECOND"; case NAME_MIDDLE_INITIAL: return "NAME_MIDDLE_INITIAL"; case NAME_FULL: diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.cc b/chromium/components/autofill/core/browser/data_model/credit_card.cc index 4b42be5216b..8e2fffe9ddb 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc @@ -760,9 +760,16 @@ void CreditCard::SetExpirationDateFromString(const base::string16& text) { const std::pair CreditCard::LabelPieces() const { base::string16 label; - // No CC number, return name only. - if (number().empty()) + if (number().empty()) { + // No CC number, if valid nickname is present, return nickname only. + // Otherwise, return cardholder name only. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableCardNicknameManagement) && + HasValidNickname()) { + return std::make_pair(nickname_, base::string16()); + } return std::make_pair(name_on_card_, base::string16()); + } base::string16 obfuscated_cc_number = CardIdentifierStringForAutofillDisplay(); @@ -817,11 +824,12 @@ base::string16 CreditCard::NetworkAndLastFourDigits() const { : network + ASCIIToUTF16(" ") + obfuscated_string; } -base::string16 CreditCard::CardIdentifierStringForAutofillDisplay() const { +base::string16 CreditCard::CardIdentifierStringForAutofillDisplay( + base::string16 customized_nickname) const { if (base::FeatureList::IsEnabled( features::kAutofillEnableSurfacingServerCardNickname) && - HasValidNickname()) { - return NicknameAndLastFourDigits(); + (HasValidNickname() || !customized_nickname.empty())) { + return NicknameAndLastFourDigits(customized_nickname); } // Return a Google-specific string for Google-issued cards. if (base::FeatureList::IsEnabled(features::kAutofillEnableGoogleIssuedCard) && @@ -832,10 +840,11 @@ base::string16 CreditCard::CardIdentifierStringForAutofillDisplay() const { } base::string16 CreditCard::CardIdentifierStringAndDescriptiveExpiration( - const std::string& app_locale) const { + const std::string& app_locale, + base::string16 customized_nickname) const { return l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_TWO_LINE_LABEL_FROM_NAME, - CardIdentifierStringForAutofillDisplay(), + CardIdentifierStringForAutofillDisplay(customized_nickname), GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale)); } @@ -897,13 +906,17 @@ bool CreditCard::HasValidNickname() const { return false; } // Must not contain digits. - for (char c : nickname_) { + for (base::char16 c : nickname_) { if (base::IsAsciiDigit(c)) return false; } return true; } +base::string16 CreditCard::NicknameAndLastFourDigitsForTesting() const { + return NicknameAndLastFourDigits(); +} + base::string16 CreditCard::Expiration2DigitYearAsString() const { return data_util::Expiration2DigitYearAsString(expiration_year_); } @@ -954,15 +967,17 @@ base::string16 CreditCard::NetworkForFill() const { return ::autofill::NetworkForFill(network_); } -base::string16 CreditCard::NicknameAndLastFourDigits() const { +base::string16 CreditCard::NicknameAndLastFourDigits( + base::string16 customized_nickname) const { // Should call HasValidNickname() to check valid nickname before calling this. - DCHECK(HasValidNickname()); + DCHECK(HasValidNickname() || !customized_nickname.empty()); const base::string16 digits = LastFourDigits(); // If digits are empty, return nickname. if (digits.empty()) - return nickname_; + return customized_nickname.empty() ? nickname_ : customized_nickname; - return nickname_ + ASCIIToUTF16(" ") + + return (customized_nickname.empty() ? nickname_ : customized_nickname) + + ASCIIToUTF16(" ") + internal::GetObfuscatedStringForCardDigits(digits); } diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.h b/chromium/components/autofill/core/browser/data_model/credit_card.h index 555dc938a99..e74ced42b14 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.h +++ b/chromium/components/autofill/core/browser/data_model/credit_card.h @@ -260,14 +260,16 @@ class CreditCard : public AutofillDataModel { // available and valid; otherwise, formatted as 'IssuerNetwork - ****2345'. // Google-issued cards have their own specific identifier, instead of // displaying the issuer network name. - base::string16 CardIdentifierStringForAutofillDisplay() const; + base::string16 CardIdentifierStringForAutofillDisplay( + base::string16 customized_nickname = base::string16()) const; // A label for this card formatted as 'Nickname - ****2345, expires on MM/YY' // if nickname experiment is turned on and nickname is available; otherwise, // formatted as 'IssuerNetwork - ****2345, expires on MM/YY'. // This label is used as a second line label when the cardholder // name/expiration date field is selected. base::string16 CardIdentifierStringAndDescriptiveExpiration( - const std::string& app_locale) const; + const std::string& app_locale, + base::string16 customized_nickname = base::string16()) const; // A label for this card formatted as 'Expires on MM/YY'. // This label is used as a second line label when the autofill dropdown // uses a two line layout and the credit card number is selected. @@ -292,10 +294,12 @@ class CreditCard : public AutofillDataModel { // Returns whether the card has a valid nickname. bool HasValidNickname() const; + // Should be used ONLY by tests. + base::string16 NicknameAndLastFourDigitsForTesting() const; + private: FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationDateFromString); FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationYearFromString); - FRIEND_TEST_ALL_PREFIXES(CreditCardTest, NicknameAndLastFourDigitsStrings); base::string16 Expiration2DigitYearAsString() const; @@ -312,7 +316,8 @@ class CreditCard : public AutofillDataModel { // A label for this card formatted as 'Nickname - ****2345'. Always call // HasValidNickname() before calling this. - base::string16 NicknameAndLastFourDigits() const; + base::string16 NicknameAndLastFourDigits( + base::string16 customized_nickname = base::string16()) const; // Sets the name_on_card_ value based on the saved name parts. void SetNameOnCardFromSeparateParts(); diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc index 01c06a494c8..56a5c11ec45 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc @@ -61,8 +61,11 @@ const char* const kInvalidNumbers[] = { }; const char* const kValidNicknames[] = { - "Grocery Card", "Two percent Cashback", + "Grocery Card", + "Two percent Cashback", "Mastercard \xF0\x9F\x92\xB3", /* Nickname with UTF-8 hex encoded emoji */ + "\u0634\u063a\u0645\u0688", /* arbitrary Arabic script in unicode */ + "\u0434\u0444\u0431\u044A", /* arbitrary Cyrillic script in unicode */ }; const char* const kInvalidNicknames[] = { @@ -98,9 +101,11 @@ TEST(CreditCardTest, GetObfuscatedStringForCardDigits) { TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { base::test::ScopedFeatureList scoped_feature_list; base::string16 valid_nickname = ASCIIToUTF16("My Visa Card"); - // Enable the flag. - scoped_feature_list.InitAndEnableFeature( - features::kAutofillEnableSurfacingServerCardNickname); + // Enable the flags. + scoped_feature_list.InitWithFeatures( + /*enable_features=*/{features::kAutofillEnableSurfacingServerCardNickname, + features::kAutofillEnableCardNicknameManagement}, + /*disable_features=*/{}); // Case 0: empty credit card. CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com/"); @@ -126,6 +131,16 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { base::string16 obfuscated1 = credit_card1.NetworkAndLastFourDigits(); EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated1); + // Case 1.1: No credit card number, but has nickname. + CreditCard credit_card11(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card11, "John Dillinger", "", "01", "2010", + "1"); + credit_card11.SetNickname(valid_nickname); + base::string16 summary11 = credit_card11.Label(); + EXPECT_EQ(valid_nickname, summary11); + base::string16 obfuscated11 = credit_card11.NetworkAndLastFourDigits(); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated11); + // Case 2: No month. CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card2, "John Dillinger", @@ -193,10 +208,12 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { summary6); // Case 7: Have everything including nickname but flag is off. - // Reset and disable the feature flag. + // Reset and disable the nickname feature flags. scoped_feature_list.Reset(); - scoped_feature_list.InitAndDisableFeature( - features::kAutofillEnableSurfacingServerCardNickname); + scoped_feature_list.InitWithFeatures( + /*enable_features=*/{}, /*disable_features=*/{ + features::kAutofillEnableSurfacingServerCardNickname, + features::kAutofillEnableCardNicknameManagement}); CreditCard credit_card7(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card7, "John Dillinger", "5105 1051 0510 5100" /* Mastercard */, "01", "2010", @@ -206,6 +223,16 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + test::ObfuscatedCardDigitsAsUTF8("5100") + ", 01/2010"), summary7); + + // Case 8: No credit card number, has valid nickname, but flag is off. + CreditCard credit_card8(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card8, "John Dillinger", "", "01", "2010", + "1"); + credit_card8.SetNickname(valid_nickname); + base::string16 summary8 = credit_card11.Label(); + EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary8); + base::string16 obfuscated8 = credit_card8.NetworkAndLastFourDigits(); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated8); } TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { @@ -216,7 +243,7 @@ TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2020", "1"); credit_card1.SetNickname(valid_nickname); - EXPECT_EQ(valid_nickname, credit_card1.NicknameAndLastFourDigits()); + EXPECT_EQ(valid_nickname, credit_card1.NicknameAndLastFourDigitsForTesting()); // Case 2: Have everything. CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/"); @@ -226,7 +253,7 @@ TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { EXPECT_EQ( valid_nickname + UTF8ToUTF16(std::string(" ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - credit_card2.NicknameAndLastFourDigits()); + credit_card2.NicknameAndLastFourDigitsForTesting()); } TEST(CreditCardTest, CardIdentifierStringsForAutofillDisplay) { diff --git a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc index 67391ee4b1f..518d6a7949d 100644 --- a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc +++ b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc @@ -12,6 +12,8 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "third_party/icu/source/i18n/unicode/dtfmtsym.h" @@ -141,6 +143,18 @@ bool SetExpirationYear(int value, int* expiration_year) { return true; } +base::string16 FindPossiblePhoneCountryCode(const base::string16& text) { + base::string16 candidate; + if (text.find(base::ASCIIToUTF16("00")) != base::string16::npos || + text.find('+') != base::string16::npos) { + if (MatchesPattern(text, base::ASCIIToUTF16(kAugmentedPhoneCountryCodeRe), + &candidate, 1)) + return candidate; + } + + return base::string16(); +} + } // namespace data_util } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/data_model_utils.h b/chromium/components/autofill/core/browser/data_model/data_model_utils.h index 59a75c3ce54..f1324537936 100644 --- a/chromium/components/autofill/core/browser/data_model/data_model_utils.h +++ b/chromium/components/autofill/core/browser/data_model/data_model_utils.h @@ -47,6 +47,10 @@ bool SetExpirationMonth(int value, int* expiration_month); // made to |*expiration_year|. bool SetExpirationYear(int value, int* expiration_year); +// Finds possible country code in |text| by fetching the first sub-group when +// matched with |kAugmentedPhoneCountryCodeRe| regex. +base::string16 FindPossiblePhoneCountryCode(const base::string16& text); + } // namespace data_util } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/phone_number.cc b/chromium/components/autofill/core/browser/data_model/phone_number.cc index 4327531281e..96a2b0b62f1 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number.cc +++ b/chromium/components/autofill/core/browser/data_model/phone_number.cc @@ -13,9 +13,11 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" +#include "components/autofill/core/browser/data_model/data_model_utils.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/geo/phone_number_i18n.h" +#include "components/autofill/core/common/autofill_features.h" namespace autofill { namespace { @@ -134,6 +136,18 @@ void PhoneNumber::GetMatchingTypes(const base::string16& text, matching_types->insert(PHONE_HOME_WHOLE_NUMBER); } } + + // |PHONE_HOME_COUNTRY_CODE| is added to the set of the |matching_types| when + // the digits extracted from the |stripped_text| match the |country_code|. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableAugmentedPhoneCountryCode)) { + base::string16 candidate = + data_util::FindPossiblePhoneCountryCode(stripped_text); + base::string16 country_code = + GetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE), app_locale); + if (candidate.size() > 0 && candidate == country_code) + matching_types->insert(PHONE_HOME_COUNTRY_CODE); + } } // Normalize phones if |type| is a whole number: diff --git a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc index 29456dcf536..2a28625d1d9 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc @@ -6,10 +6,12 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/geo/phone_number_i18n.h" +#include "components/autofill/core/common/autofill_features.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -289,4 +291,78 @@ TEST(PhoneNumberTest, InternationalPhoneHomeCityAndNumber_DE) { phone_number.GetInfo(PHONE_HOME_CITY_AND_NUMBER, "en-US")); } +// Tests whether the |PHONE_HOME_COUNTRY_CODE| is added to the set of matching +// types. +TEST(PhoneNumberTest, CountryCodeInMatchingTypes) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + AutofillProfile profile; + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + // Set the phone number such that country_code == 1, city_code = 650, + // number = 2345678. + base::string16 phone(ASCIIToUTF16("1 [650] 234-5678")); + PhoneNumber phone_number(&profile); + phone_number.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), phone, "US"); + + std::vector test_cases = {"+1", "1", "(+1) United States", + "US (+1)"}; + + for (size_t i = 0; i < test_cases.size(); i++) { + SCOPED_TRACE(testing::Message() << "i(US) = " << i); + + ServerFieldTypeSet matching_types; + phone_number.GetMatchingTypes(ASCIIToUTF16(test_cases[i]), "US", + &matching_types); + + EXPECT_THAT(matching_types, testing::ElementsAre(PHONE_HOME_COUNTRY_CODE)); + } + + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + base::string16 de_phone(ASCIIToUTF16("+49 0151 6679586")); + PhoneNumber phone_number_de(&profile); + phone_number_de.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), de_phone, + "DE"); + + test_cases = {"49", "+49", "(+49) DE", "(0049) DE", "0049"}; + for (size_t i = 0; i < test_cases.size(); i++) { + SCOPED_TRACE(testing::Message() << "i(DE) = " << i); + + ServerFieldTypeSet matching_types; + phone_number_de.GetMatchingTypes(ASCIIToUTF16(test_cases[i]), "DE", + &matching_types); + + EXPECT_THAT(matching_types, testing::ElementsAre(PHONE_HOME_COUNTRY_CODE)); + } +} + +// Tests that the |PHONE_HOME_COUNTRY_CODE| should not be added to the set of +// matching types. +TEST(PhoneNumberTest, CountryCodeNotInMatchingTypes) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + AutofillProfile profile; + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + // Set phone number so country_code == 1, city_code = 650, number = 2345678. + base::string16 phone(ASCIIToUTF16("1 [650] 234-5678")); + PhoneNumber phone_number(&profile); + phone_number.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), phone, "US"); + + std::vector test_cases = { + "01", "+16502", "11", "211", "0001", "++1", "+1abc2", "001abc2", "01"}; + + for (size_t i = 0; i < test_cases.size(); i++) { + SCOPED_TRACE(testing::Message() << "i = " << i); + + ServerFieldTypeSet matching_types; + phone_number.GetMatchingTypes(ASCIIToUTF16(test_cases[i]), "US", + &matching_types); + + EXPECT_THAT(matching_types, testing::IsEmpty()); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/test_data_creator.cc b/chromium/components/autofill/core/browser/data_model/test_data_creator.cc index 41bdcd75669..edb610199dc 100644 --- a/chromium/components/autofill/core/browser/data_model/test_data_creator.cc +++ b/chromium/components/autofill/core/browser/data_model/test_data_creator.cc @@ -6,6 +6,7 @@ #include +#include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" diff --git a/chromium/components/autofill/core/browser/field_filler.cc b/chromium/components/autofill/core/browser/field_filler.cc index 34766959c5e..16ada9dbb6c 100644 --- a/chromium/components/autofill/core/browser/field_filler.cc +++ b/chromium/components/autofill/core/browser/field_filler.cc @@ -705,6 +705,36 @@ base::string16 RemoveWhitespace(const base::string16& value) { return stripped_value; } +// Finds the best suitable option in the |field| that corresponds to the +// |country_code|. +// If the exact match is not found, extracts the digits (ignoring leading '00' +// or '+') from each option and compares them with the |country_code|. +void FillPhoneCountryCodeSelectControl(const base::string16& country_code, + FormFieldData* field, + std::string* failure_to_fill) { + if (country_code.empty()) + return; + + // Find the option that exactly matches the |country_code|. + if (SetSelectControlValue(country_code, field, /*best_match_index=*/nullptr, + failure_to_fill)) + return; + + for (size_t i = 0; i < field->option_contents.size(); i++) { + base::string16 cc_candidate_in_value = + data_util::FindPossiblePhoneCountryCode( + RemoveWhitespace(field->option_values[i])); + base::string16 cc_candidate_in_content = + data_util::FindPossiblePhoneCountryCode( + RemoveWhitespace(field->option_contents[i])); + if (cc_candidate_in_value == country_code || + cc_candidate_in_content == country_code) { + field->value = field->option_values[i]; + return; + } + } +} + } // namespace FieldFiller::FieldFiller(const std::string& app_locale, @@ -748,7 +778,16 @@ bool FieldFiller::FillFormField(const AutofillField& field, } if (type.group() == PHONE_HOME) { - FillPhoneNumberField(field, value, field_data); + // If the |field_data| is a selection box and having the type + // |PHONE_HOME_COUNTRY_CODE|, call |FillPhoneCountryCodeSelectControl|. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableAugmentedPhoneCountryCode) && + field_data->form_control_type == "select-one" && + type.GetStorableType() == PHONE_HOME_COUNTRY_CODE) { + FillPhoneCountryCodeSelectControl(value, field_data, failure_to_fill); + } else { + FillPhoneNumberField(field, value, field_data); + } return true; } if (field_data->form_control_type == "select-one") { diff --git a/chromium/components/autofill/core/browser/field_filler_unittest.cc b/chromium/components/autofill/core/browser/field_filler_unittest.cc index f5c2e8f6b08..02f1c95c11f 100644 --- a/chromium/components/autofill/core/browser/field_filler_unittest.cc +++ b/chromium/components/autofill/core/browser/field_filler_unittest.cc @@ -1495,4 +1495,132 @@ INSTANTIATE_TEST_SUITE_P( FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 3, "Quebec", "", false})); +// Tests that the correct option is chosen in the selection box when one of the +// options exactly matches the phone country code. +TEST_F(AutofillFieldFillerTest, + FillSelectControlPhoneCountryCodeWithExactMatch) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = {"91", "1", "20", "49"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+15145554578")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("1"), field.value); +} + +// Tests that the correct option is chosen in the selection box when the options +// are preceded by a plus sign and the field is of |PHONE_HOME_COUNTRY_CODE| +// type. +TEST_F(AutofillFieldFillerTest, + FillSelectControlPhoneCountryCodePrecededByPlus) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = {"+91", "+1", "+20", "+49"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+918890888888")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("+91"), field.value); +} + +// Tests that the correct option is chosen in the selection box when the options +// are preceded by a '00' and the field is of |PHONE_HOME_COUNTRY_CODE| +// type. +TEST_F(AutofillFieldFillerTest, + FillSelectControlPhoneCountryCodePrecededByDoubleZeros) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = {"0091", "001", "0020", "0049"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+918890888888")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("0091"), field.value); +} + +// Tests that the correct option is chosen in the selection box when the options +// are composed of the country code and the country name. +TEST_F(AutofillFieldFillerTest, FillSelectControlAugmentedPhoneCountryCode) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = { + "Please select an option", "+91 (India)", "+1 (United States)", + "+20 (Egypt)", "+49 (Germany)"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("+49 (Germany)"), field.value); +} + +// Tests that the correct option is chosen in the selection box when the options +// are composed of the country code having whitespace and the country name. +TEST_F(AutofillFieldFillerTest, + FillSelectControlAugmentedPhoneCountryCodeWithWhiteSpaces) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = { + "Please select an option", "(00 91) India", "(00 1) United States", + "(00 20) Egypt", "(00 49) Germany"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("(00 49) Germany"), field.value); +} + +// Tests that the correct option is chosen in the selection box when the options +// are composed of the country code that is preceded by '00' and the country +// name. +TEST_F(AutofillFieldFillerTest, + FillSelectControlAugmentedPhoneCountryCodeHavingDoubleZeros) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + std::vector kPhoneCountryCode = { + "Please select an option", "(0091) India", "(001) United States", + "(0020) Egypt", "(0049) Germany"}; + AutofillField field; + test::CreateTestSelectField(kPhoneCountryCode, &field); + field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE); + + AutofillProfile address; + address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("(0049) Germany"), field.value); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/field_types.cc b/chromium/components/autofill/core/browser/field_types.cc index d734b327ab2..d5f7ede4a7d 100644 --- a/chromium/components/autofill/core/browser/field_types.cc +++ b/chromium/components/autofill/core/browser/field_types.cc @@ -11,9 +11,13 @@ namespace autofill { bool IsFillableFieldType(ServerFieldType field_type) { switch (field_type) { + case NAME_HONORIFIC_PREFIX: case NAME_FIRST: case NAME_MIDDLE: case NAME_LAST: + case NAME_LAST_FIRST: + case NAME_LAST_CONJUNCTION: + case NAME_LAST_SECOND: case NAME_MIDDLE_INITIAL: case NAME_FULL: case NAME_SUFFIX: diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index b8964f24f76..24e11f2b469 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -206,9 +206,17 @@ enum ServerFieldType { // Currently not used by Chrome. ADDRESS_HOME_OTHER_SUBUNIT = 106, + // Types to represent the structure of a Hispanic/Latinx last name. + NAME_LAST_FIRST = 107, + NAME_LAST_CONJUNCTION = 108, + NAME_LAST_SECOND = 109, + + // Type to catch name additions like "Mr.", "Ms." or "Dr.". + NAME_HONORIFIC_PREFIX = 110, + // No new types can be added without a corresponding change to the Autofill // server. - MAX_VALID_FIELD_TYPE = 107, + MAX_VALID_FIELD_TYPE = 111, }; // The list of all HTML autocomplete field type hints supported by Chrome. @@ -219,6 +227,7 @@ enum HtmlFieldType { // Name types. HTML_TYPE_NAME, + HTML_TYPE_HONORIFIC_PREFIX, HTML_TYPE_GIVEN_NAME, HTML_TYPE_ADDITIONAL_NAME, HTML_TYPE_FAMILY_NAME, diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc index b243b7aa61f..1009591614b 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.cc +++ b/chromium/components/autofill/core/browser/form_data_importer.cc @@ -466,6 +466,27 @@ bool FormDataImporter::ImportAddressProfiles(const FormStructure& form) { // And close the div of the section import log. import_log_buffer << CTag{"div"}; } + // TODO(crbug.com/1097125): Remove feature test. + // Run the import on the union of the section if the import was not + // successful and if there is more than one section. + if (num_saved_profiles > 0) { + AutofillMetrics::LogAddressFormImportStatustMetric( + AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT); + } else if (base::FeatureList::IsEnabled( + features::kAutofillProfileImportFromUnifiedSection) && + sections.size() > 1) { + // Try to import by combining all sections. + if (ImportAddressProfileForSection(form, "", &import_log_buffer)) { + num_saved_profiles++; + AutofillMetrics::LogAddressFormImportStatustMetric( + AutofillMetrics::AddressProfileImportStatusMetric:: + SECTION_UNION_IMPORT); + } + } + if (num_saved_profiles == 0) { + AutofillMetrics::LogAddressFormImportStatustMetric( + AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT); + } } import_log_buffer << LogMessage::kImportAddressProfileFromFormNumberOfImports << num_saved_profiles << CTag{}; @@ -509,7 +530,8 @@ bool FormDataImporter::ImportAddressProfileForSection( // Go through each |form| field and attempt to constitute a valid profile. for (const auto& field : form) { // Reject fields that are not within the specified |section|. - if (field->section != section) + // If section is empty, use all fields. + if (field->section != section && !section.empty()) continue; base::string16 value; @@ -518,7 +540,12 @@ bool FormDataImporter::ImportAddressProfileForSection( // If we don't know the type of the field, or the user hasn't entered any // information into the field, or the field is non-focusable (hidden), then // skip it. - if (!field->IsFieldFillable() || !field->is_focusable || value.empty()) + // TODO(crbug.com/1101280): Remove |skip_unfocussable_field| + bool skip_unfocussable_field = + !field->is_focusable && + !base::FeatureList::IsEnabled( + features::kAutofillProfileImportFromUnfocusableFields); + if (!field->IsFieldFillable() || skip_unfocussable_field || value.empty()) continue; AutofillType field_type = field->Type(); @@ -679,17 +706,12 @@ bool FormDataImporter::ImportCreditCard( } } - // If editable expiration date experiment is enabled, the card with invalid - // expiration date can be uploaded. However, the card with invalid card number - // must be ignored. + // Cards with invalid expiration dates can be uploaded due to the existence of + // the expiration date fix flow. However, cards with invalid card numbers must + // still be ignored. if (!candidate_credit_card.HasValidCardNumber()) { return false; } - if (!candidate_credit_card.HasValidExpirationDate() && - !base::FeatureList::IsEnabled( - features::kAutofillUpstreamEditableExpirationDate)) { - return false; - } // Can import one valid card per form. Start by treating it as NEW_CARD, but // overwrite this type if we discover it is already a local or server card. @@ -711,6 +733,15 @@ bool FormDataImporter::ImportCreditCard( // already a local card. imported_credit_card_record_type_ = ImportedCreditCardRecordType::LOCAL_CARD; + + // If the card is a local card and it has a nickname stored in the local + // database, copy the nickname to the |candidate_credit_card| so that the + // nickname also shows in the Upstream bubble. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableSurfacingServerCardNickname)) { + candidate_credit_card.SetNickname(card_copy.nickname()); + } + // If we should not return the local card, return that we merged it, // without setting |imported_credit_card|. if (!should_return_local_card) diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h index d255cbd8625..38d0b12fd9a 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.h +++ b/chromium/components/autofill/core/browser/form_data_importer.h @@ -114,7 +114,8 @@ class FormDataImporter { bool ImportAddressProfiles(const FormStructure& form); // Helper method for ImportAddressProfiles which only considers the fields for - // a specified |section|. + // a specified |section|. If |section| is the empty string, the import is + // performed on the union of all sections. bool ImportAddressProfileForSection(const FormStructure& form, const std::string& section, LogBuffer* import_log_buffer); diff --git a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc index ca166dfaea8..1487f0ff2e2 100644 --- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc @@ -306,7 +306,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -319,6 +319,60 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles) { EXPECT_EQ(0, expected.Compare(*results[0])); } +TEST_F(FormDataImporterTest, ImportAddressProfileFromUnifiedSection) { + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("First name:", "first_name", "George", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name:", "last_name", "Washington", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Email:", "email", "theprez@gmail.com", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "California", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); + form.fields.push_back(field); + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + + // Assign the address field another section than the other fields. + form_structure.field(3)->section = "another_section"; + + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndDisableFeature( + features::kAutofillProfileImportFromUnifiedSection); + + // Without the feature, the import is expected to fail. + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); + + // After enabled the feature, the import is expected to succeed. + scoped_feature.Reset(); + scoped_feature.InitAndEnableFeature( + features::kAutofillProfileImportFromUnifiedSection); + + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&expected, "George", nullptr, "Washington", + "theprez@gmail.com", nullptr, "21 Laussat St", nullptr, + "San Francisco", "California", "94102", nullptr, + nullptr); + const std::vector& results2 = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results2.size()); + EXPECT_EQ(0, expected.Compare(*results2[0])); +} + TEST_F(FormDataImporterTest, ImportAddressProfiles_BadEmail) { FormData form; form.url = GURL("https://wwww.foo.com"); @@ -343,7 +397,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_BadEmail) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); } @@ -374,7 +428,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoEmails) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); } @@ -405,7 +459,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoDifferentEmails) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); } @@ -427,7 +481,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_NotEnoughFilledFields) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size()); @@ -455,7 +509,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressUSA) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); } @@ -482,7 +536,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGB) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); } @@ -504,7 +558,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGI) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); } @@ -544,7 +598,7 @@ TEST_F(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", nullptr, @@ -556,6 +610,64 @@ TEST_F(FormDataImporterTest, EXPECT_EQ(0, expected.Compare(*results[0])); } +TEST_F(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + + test::CreateTestFormField("First name:", "first_name", "George", "text", + &field); + form.fields.push_back(field); + + test::CreateTestFormField("Last name:", "last_name", "Washington", "text", + &field); + form.fields.push_back(field); + + test::CreateTestFormField("Phone:", "phone", "1234554321", "text", &field); + form.fields.push_back(field); + + test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text", + &field); + form.fields.push_back(field); + + test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); + // Set this field to be unfocusable. + field.is_focusable = false; + form.fields.push_back(field); + + test::CreateTestFormField("State:", "state", "California", "text", &field); + form.fields.push_back(field); + + test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + + // Verify the status quo that the form is not imported with the unfocusable + // fields. + // TODO(crbug.com/1101280): Remove once feature is launched. + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillProfileImportFromUnfocusableFields); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); + + // Activate the feature and test again. + scoped_feature_list_.Reset(); + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillProfileImportFromUnfocusableFields); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&expected, "George", nullptr, "Washington", nullptr, + nullptr, "21 Laussat St", nullptr, "San Francisco", + "California", "94102", nullptr, "1234554321"); + const std::vector& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + EXPECT_EQ(0, expected.Compare(*results[0])); +} + TEST_F(FormDataImporterTest, ImportAddressProfiles_MultilineAddress) { FormData form; form.url = GURL("https://wwww.foo.com"); @@ -583,7 +695,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MultilineAddress) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -623,7 +735,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure1); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -659,7 +771,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure2); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); AutofillProfile expected2(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected2, "John", nullptr, "Adams", "second@gmail.com", @@ -716,7 +828,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoValidProfilesSameForm) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -790,7 +902,7 @@ TEST_F(FormDataImporterTest, // Still able to do the import. FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -872,7 +984,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_ThreeValidProfilesSameForm) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // Only two are saved. AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -926,7 +1038,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure1); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -972,7 +1084,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure2); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector& results2 = personal_data_manager_->GetProfiles(); @@ -1009,7 +1121,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure1); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", nullptr, @@ -1045,7 +1157,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure2); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector& results2 = personal_data_manager_->GetProfiles(); @@ -1089,7 +1201,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure1); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -1127,7 +1239,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure2); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector& results2 = personal_data_manager_->GetProfiles(); @@ -1163,7 +1275,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_InsufficientAddress) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure1); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure1); // Since no refresh is expected, reload the data from the database to make // sure no changes were written out. @@ -1219,7 +1331,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // Expect that no new profile is saved. const std::vector& results = @@ -1237,7 +1349,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure2(form); form_structure2.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure2); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); // Expect that no new profile is saved. const std::vector& results2 = @@ -1276,7 +1388,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_UnrecognizedCountry) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); // Since no refresh is expected, reload the data from the database to make // sure no changes were written out. @@ -1320,7 +1432,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_LocalizedCountryName) { // the page language is not set. FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size()); @@ -1334,7 +1446,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_LocalizedCountryName) { // enabled. scoped_feature_list_.InitAndDisableFeature( features::kAutofillUsePageLanguageToTranslateCountryNames); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); // There should be no imported address profile. ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); @@ -1345,7 +1457,7 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_LocalizedCountryName) { scoped_feature_list_.Reset(); scoped_feature_list_.InitAndEnableFeature( features::kAutofillUsePageLanguageToTranslateCountryNames); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // There should be one imported address profile. ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); @@ -1392,7 +1504,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/true, form_structure); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", @@ -1439,7 +1551,7 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - ImportAddressProfiles(/*extraction_success=*/false, form_structure); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); // Since no refresh is expected, reload the data from the database to make // sure no changes were written out. @@ -1507,39 +1619,10 @@ TEST_F(FormDataImporterTest, ImportCreditCard_InvalidCardNumber) { ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size()); } -// Tests that an invalid credit card expiration is not extracted when the -// expiration date fix flow experiment is disabled. -TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate) { - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillUpstreamEditableExpirationDate); - FormData form; - form.url = GURL("https://wwww.foo.com"); - - AddFullCreditCardForm(&form, "Smalls Biggie", "4111-1111-1111-1111", "0", - "2999"); - - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - std::unique_ptr imported_credit_card; - base::HistogramTester histogram_tester; - EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); - ASSERT_FALSE(imported_credit_card); - histogram_tester.ExpectUniqueSample("Autofill.SubmittedCardState", - AutofillMetrics::HAS_CARD_NUMBER_ONLY, 1); - - // Since no refresh is expected, reload the data from the database to make - // sure no changes were written out. - ResetPersonalDataManager(USER_MODE_NORMAL); - - ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size()); -} - -// Tests that an empty credit card expiration is extracted when editable -// expiration date experiment on. +// Tests that a credit card with an empty expiration can be extracted due to the +// expiration date fix flow. TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate_EditableExpirationExpOn) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); FormData form; form.url = GURL("https://wwww.foo.com"); @@ -1555,12 +1638,10 @@ TEST_F(FormDataImporterTest, AutofillMetrics::HAS_CARD_NUMBER_ONLY, 1); } -// Tests that an expired credit card is extracted when editable expiration date -// experiment on. +// Tests that an expired credit card can be extracted due to the expiration date +// fix flow. TEST_F(FormDataImporterTest, ImportCreditCard_ExpiredExpiryDate_EditableExpirationExpOn) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); FormData form; form.url = GURL("https://wwww.foo.com"); @@ -2455,41 +2536,10 @@ TEST_F(FormDataImporterTest, FormDataImporter::ImportedCreditCardRecordType::NO_CARD); } -// Ensures that |imported_credit_card_record_type_| is set correctly. -TEST_F( - FormDataImporterTest, - ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard_EditableExpDateOff) { - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // Simulate a form submission with an expired credit card. - FormData form; - form.url = GURL("https://wwww.foo.com"); - - AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01", - "1999"); - - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - std::unique_ptr imported_credit_card; - base::Optional imported_upi_id; - EXPECT_FALSE(form_data_importer_->ImportFormData( - form_structure, /*profile_autofill_enabled=*/true, - /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card, - &imported_upi_id)); - ASSERT_FALSE(imported_credit_card); - // |imported_credit_card_record_type_| should be NO_CARD because no valid card - // was successfully imported from the form. - ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ == - FormDataImporter::ImportedCreditCardRecordType::NO_CARD); -} - // Ensures that |imported_credit_card_record_type_| is set correctly. TEST_F( FormDataImporterTest, ImportFormData_ImportCreditCardRecordType_NewCard_ExpiredCard_WithExpDateFixFlow) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // Simulate a form submission with an expired credit card. FormData form; form.url = GURL("https://wwww.foo.com"); @@ -3110,8 +3160,6 @@ TEST_F(FormDataImporterTest, // server card and user submitted an invalid expiration date month. TEST_F(FormDataImporterTest, Metrics_SubmittedServerCardExpirationStatus_EmptyExpirationMonth) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); EnableWalletCardImport(); std::vector server_cards; @@ -3160,8 +3208,6 @@ TEST_F(FormDataImporterTest, // server card and user submitted an invalid expiration date year. TEST_F(FormDataImporterTest, Metrics_SubmittedServerCardExpirationStatus_EmptyExpirationYear) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); EnableWalletCardImport(); std::vector server_cards; @@ -3211,8 +3257,6 @@ TEST_F(FormDataImporterTest, TEST_F( FormDataImporterTest, Metrics_SubmittedDifferentServerCardExpirationStatus_EmptyExpirationYear) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); EnableWalletCardImport(); std::vector server_cards; diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc index 36e09498599..3495f488b98 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc @@ -20,6 +20,7 @@ #include "components/autofill/core/browser/field_filler.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_regex_constants.h" #include "components/autofill/core/common/autofill_regexes.h" @@ -212,7 +213,7 @@ std::unique_ptr CreditCardField::Parse(AutofillScanner* scanner, continue; } - if (credit_card_field->ParseExpirationDate(scanner)) { + if (credit_card_field->ParseExpirationDate(scanner, log_manager)) { nb_unknown_fields = 0; continue; } @@ -308,7 +309,8 @@ bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) { } // static -bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner) { +bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner, + LogManager* log_manager) { if (scanner->IsEnd()) return false; @@ -317,18 +319,48 @@ bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner) { MATCH_SELECT | MATCH_SEARCH)) return false; + // Filter out days - elements for date entries would have + // numbers 1 to 9 as well in them, which we can filter on. + const base::string16 kSingleDigitDateRe = base::ASCIIToUTF16("\\b[1-9]\\b"); + for (const auto& value : field->option_contents) { + if (MatchesPattern(value, kSingleDigitDateRe)) { + return false; + } + } + + // Another way to eliminate days - filter out 'day' fields. + if (FormField::ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDayRe), + MATCH_DEFAULT | MATCH_SELECT, nullptr, + {log_manager, "kDayRe"})) { + return false; + } + + // Filter out birth years - a website would not offer 1999 as a credit card + // expiration year, but show it in the context of a birth year selector. + const base::string16 kBirthYearRe = base::ASCIIToUTF16("(1999|99)"); + for (const auto& value : field->option_contents) { + if (MatchesPattern(value, kBirthYearRe)) { + return false; + } + } + const base::Time time_now = AutofillClock::Now(); base::Time::Exploded time_exploded; time_now.UTCExplode(&time_exploded); const int kYearsToMatch = 3; - std::vector years_to_check; + std::vector years_to_check_4_digit; + std::vector years_to_check_2_digit; for (int year = time_exploded.year; year < time_exploded.year + kYearsToMatch; ++year) { - years_to_check.push_back(base::NumberToString16(year)); + years_to_check_4_digit.push_back(base::NumberToString16(year)); + years_to_check_2_digit.push_back(base::NumberToString16(year).substr(2)); } - return (FindConsecutiveStrings(years_to_check, field->option_values) || - FindConsecutiveStrings(years_to_check, field->option_contents)); + return ( + FindConsecutiveStrings(years_to_check_4_digit, field->option_values) || + FindConsecutiveStrings(years_to_check_4_digit, field->option_contents) || + FindConsecutiveStrings(years_to_check_2_digit, field->option_values) || + FindConsecutiveStrings(years_to_check_2_digit, field->option_contents)); } // static @@ -359,20 +391,25 @@ bool CreditCardField::IsGiftCardField(AutofillScanner* scanner, if (scanner->IsEnd()) return false; + const int kMatchFieldTypes = + MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE | MATCH_SEARCH; size_t saved_cursor = scanner->SaveCursor(); - if (ParseField(scanner, base::UTF8ToUTF16(kDebitCardRe), nullptr, - {log_manager, "kDebitCardRe"})) { + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitCardRe), + kMatchFieldTypes, nullptr, + {log_manager, "kDebitCardRe"})) { scanner->RewindTo(saved_cursor); return false; } - if (ParseField(scanner, base::UTF8ToUTF16(kDebitGiftCardRe), nullptr, - {log_manager, "kDebitGiftCardRe"})) { + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitGiftCardRe), + kMatchFieldTypes, nullptr, + {log_manager, "kDebitGiftCardRe"})) { scanner->RewindTo(saved_cursor); return false; } - return ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr, - {log_manager, "kGiftCardRe"}); + return ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kGiftCardRe), + kMatchFieldTypes, nullptr, + {log_manager, "kGiftCardRe"}); } CreditCardField::CreditCardField(LogManager* log_manager) @@ -427,7 +464,8 @@ void CreditCardField::AddClassifications( } } -bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner) { +bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, + LogManager* log_manager) { if (!expiration_date_ && base::LowerCaseEqualsASCII( scanner->Cursor()->form_control_type, "month")) { expiration_date_ = scanner->Cursor(); @@ -447,7 +485,7 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner) { if (LikelyCardMonthSelectField(scanner)) { expiration_month_ = scanner->Cursor(); scanner->Advance(); - if (LikelyCardYearSelectField(scanner)) { + if (LikelyCardYearSelectField(scanner, log_manager)) { expiration_year_ = scanner->Cursor(); scanner->Advance(); return true; diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h index 9db7aec100f..abd4eb9a961 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h @@ -38,8 +38,10 @@ class CreditCardField : public FormField { // Returns true if |scanner| points to a field that looks like a year // field that contains credit // card type options. @@ -54,7 +56,7 @@ class CreditCardField : public FormField { // Parses the expiration month/year/date fields. Returns true if it finds // something new. - bool ParseExpirationDate(AutofillScanner* scanner); + bool ParseExpirationDate(AutofillScanner* scanner, LogManager* log_manager); // For the combined expiration field we return |exp_year_type_|; otherwise if // |expiration_year_| is having year with |max_length| of 2-digits we return diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc index 865e1893f21..e876c72ec64 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc @@ -9,9 +9,11 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -152,6 +154,72 @@ TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) { field_candidates_map_[ASCIIToUTF16("year3")].BestHeuristicType()); } +TEST_F(CreditCardFieldTest, ParseMinimumCreditCardWithExpiryDateOptions) { + FormFieldData cc_number_field; + FormFieldData month_field; + FormFieldData year_field; + + cc_number_field.form_control_type = "text"; + cc_number_field.label = ASCIIToUTF16("Card Number"); + cc_number_field.name = ASCIIToUTF16("card_number"); + list_.push_back( + std::make_unique(cc_number_field, ASCIIToUTF16("number"))); + + // For month field, set the label and name to something which won't match + // any regex, so we can test matching of the options themselves. + month_field.form_control_type = "select-one"; + month_field.label = ASCIIToUTF16("Random label"); + month_field.name = ASCIIToUTF16("Random name"); + const std::vector kMonths{"MM", "01", "02", "03", "04", + "05", "06", "07", "08", "09", + "10", "11", "12"}; + for (auto month : kMonths) { + month_field.option_contents.push_back(base::UTF8ToUTF16(month)); + month_field.option_values.push_back(base::UTF8ToUTF16(month)); + } + list_.push_back( + std::make_unique(month_field, ASCIIToUTF16("month"))); + + // For year, keep the label and name to something which doesn't match regex + // so we can test matching of the options themselves. + year_field.form_control_type = "select-one"; + year_field.label = ASCIIToUTF16("Random label"); + year_field.name = ASCIIToUTF16("Random name"); + year_field.max_length = 2; + year_field.option_contents.push_back(base::ASCIIToUTF16("YY")); + year_field.option_values.push_back(base::ASCIIToUTF16("YY")); + + const base::Time time_now = AutofillClock::Now(); + base::Time::Exploded time_exploded; + time_now.UTCExplode(&time_exploded); + const int kYearsToAdd = 10; + + for (auto year = time_exploded.year; year < time_exploded.year + kYearsToAdd; + year++) { + year_field.option_contents.push_back( + base::NumberToString16(year).substr(2)); + year_field.option_values.push_back(base::NumberToString16(year).substr(2)); + } + list_.push_back( + std::make_unique(year_field, ASCIIToUTF16("year"))); + + Parse(); + ASSERT_NE(nullptr, field_.get()); + AddClassifications(); + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != + field_candidates_map_.end()); + EXPECT_EQ(CREDIT_CARD_NUMBER, + field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != + field_candidates_map_.end()); + EXPECT_EQ(CREDIT_CARD_EXP_MONTH, + field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != + field_candidates_map_.end()); + EXPECT_EQ(CREDIT_CARD_EXP_2_DIGIT_YEAR, + field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); +} + TEST_F(CreditCardFieldTest, ParseFullCreditCard) { FormFieldData field; field.form_control_type = "text"; diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc index a067e9ca0ba..d7079bfd751 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc @@ -17,11 +17,33 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/common/autofill_regexes.h" namespace autofill { namespace { +// Minimum limit on the number of the options of the select field for +// determining the field to be of |PHONE_HOME_COUNTRY_CODE| type. +constexpr int kMinSelectOptionsForCountryCode = 5; + +// Maximum limit on the number of the options of the select field for +// determining the field to be of |PHONE_HOME_COUNTRY_CODE| type. +// Currently, there are approximately 250 countries that have been assigned a +// phone country code, therefore, 275 is taken as the upper bound. +constexpr int kMaxSelectOptionsForCountryCode = 275; + +// Minimum percentage of options in select field that should look like a +// country code in order to classify the field as a |PHONE_HOME_COUNTRY_CODE|. +constexpr int kMinCandidatePercentageForCountryCode = 90; + +// If a field that appears to be the + // phone country code by looking at its option contents. + // "Augmented" refers to the fact that we are looking for select options that + // contain not only a country code but also further text like "Germany (+49)". + static bool LikelyAugmentedPhoneCountryCode(AutofillScanner* scanner, + AutofillField** match); + // FIELD_PHONE is always present; holds suffix if prefix is present. // The rest could be NULL. AutofillField* parsed_phone_fields_[FIELD_MAX]; diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc index 22754b699a2..f5a2d7cb76e 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc @@ -13,8 +13,10 @@ #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -59,6 +61,22 @@ class PhoneFieldTest : public testing::Test { EXPECT_EQ(expected_type, it->second.BestHeuristicType()) << name; } + // Populates a select |field| with the |label|, the |name| and the |contents|. + void CreateTestSelectField(const char* label, + const char* name, + const std::vector& contents, + FormFieldData* field) { + field->label = ASCIIToUTF16(label); + field->name = ASCIIToUTF16(name); + field->form_control_type = "select-one"; + + std::vector contents16; + for (auto* const element : contents) + contents16.push_back(base::UTF8ToUTF16(element)); + + field->option_contents = contents16; + } + std::vector> list_; std::unique_ptr field_; FieldCandidatesMap field_candidates_map_; @@ -335,4 +353,317 @@ TEST_F(PhoneFieldTest, CountryCodeIsSelectElement) { CheckField("phoneNumber", PHONE_HOME_NUMBER); } +// Tests if the country code, city code and phone number fields are correctly +// classified by the heuristic when the phone code field is a select element +// consisting of valid options. +TEST_F(PhoneFieldTest, CountryCodeWithOptions) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + + // Options consisting of the country code followed by the country names. + std::vector augmented_field_options_list = { + "(+91) India", "(+49) Germany", "(+1) United States", "(+20) Egypt", + "(+1242) Bahamas", "(+593) Ecuador", "(+7) Russia"}; + CreateTestSelectField("PC", "PC", augmented_field_options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("countryCode"))); + + field.label = ASCIIToUTF16("Phone City Code"); + field.name = ASCIIToUTF16("areacode"); + field.form_control_type = "text"; + field.max_length = 3; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("cityCode"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 0; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_NE(nullptr, field_.get()); + field_->AddClassificationsForTesting(&field_candidates_map_); + CheckField("countryCode", PHONE_HOME_COUNTRY_CODE); + CheckField("cityCode", PHONE_HOME_CITY_CODE); + CheckField("phoneNumber", PHONE_HOME_NUMBER); +} + +// Tests if the country code field is correctly classified by the heuristic when +// the phone code is a select element and consists of valid options. +TEST_F(PhoneFieldTest, IsPhoneCountryCodeField) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + std::vector> augmented_field_options_list = { + // Options with the country name followed by the country code in brackets. + {"India(+91) ", "Germany(+49)", "United States(+1)", "Egypt(+20)", + "Bahamas(+1242)", "Ecuador(+593)", "Russia(+7)"}, + + // Options consisting of the country code totaling more than 20. + {"+91", "+49", "+1", "+20", "+1242", "+593", "+7", + "+1441", "+211", "+212", "+30", "+31", "+32", "+33", + "+34", "+51", "52", "+673", "+674", "+81", "+82"}, + + // Options consisting of the country code totaling more than 20 with an + // additional placeholder option. + {"+91", "+49", + "+1", "+20", + "+1242", "+593", + "+7", "+1441", + "+211", "+212", + "+30", "+31", + "+32", "+33", + "+34", "+51", + "52", "+673", + "+674", "+81", + "+82", "Please select an option"}, + + // Options with the country name followed by the country code in brackets + // along with a placeholder option. + {"Please select an option", "(+91) India", "(+49) Germany", + "(+1) United States", "(+20) Egypt", "(+1242) Bahamas", "(+593) Ecuador", + "(+7) Russia"}, + + // Options with the phone country code followed by the country + // abbreviation. + {"91 IN", "49 DE", "1 US", "20 E", "1242 B", "593 EQ", "7 R"}, + + // Options with the phone country code that are preceded by '00' and + // followed by the country abbreviation. + {"(0091) IN", "(0049) DE", "(001) US", "(0020) E", "(001242) B", + "(00593) EQ", "(007) R"}, + + // Options with the phone country code that are preceded by '00' and + // followed by the country abbreviation with single space in between. + {"(00 91) IN", "(00 49) DE", "(00 1) US", "(00 20) E", "(00 1242) B", + "(00 593) EQ", "(00 7) R"}, + + // Options with the phone country code preceded by '00' with multiple + // spaces in between to align them. + {"00 91", "00 49", "00 1", "00 20", "001242", "00 593", "00 7"}, + + // Options with the phone country code preceded by '00'. + {"0091", "0049", "001", "0020", "001242", "00593", "007"}}; + + for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + const auto& options_list = augmented_field_options_list[i]; + CreateTestSelectField("PC", "PC", options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("countryCode"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 14; + field.form_control_type = "text"; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_NE(nullptr, field_.get()); + field_->AddClassificationsForTesting(&field_candidates_map_); + CheckField("countryCode", PHONE_HOME_COUNTRY_CODE); + } +} // namespace autofill + +// Tests that the month field is not classified as |PHONE_HOME_COUNTRY_CODE|. +TEST_F(PhoneFieldTest, IsMonthField) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + std::vector> augmented_field_options_list = { + // Month options in numeric. + {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"}, + + // Month options in numeric followed by the respective text. + {"(01) Jan", "(02) Feb", "(03) March", "(04) April", "(05) May", + "(06) June", "(07) July", "(08) August", "(09) Sept", "(10) Oct", + "(11) Nov", "(12) Dec"}}; + + for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + const auto& options_list = augmented_field_options_list[i]; + CreateTestSelectField("Month", "Month", options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("months"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 14; + field.form_control_type = "text"; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_EQ(nullptr, field_.get()); + } +} + +// Tests that the day field is not classified as |PHONE_HOME_COUNTRY_CODE|. +TEST_F(PhoneFieldTest, IsDayField) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + std::vector> augmented_field_options_list = { + // Numeric day options. + {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "21", "22", "23", "24", "25", "26", "27", "28", "29", "30"}, + + // Numeric day options with a select option placeholder. + {"Please select an option", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30"}}; + + for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + const auto& options_list = augmented_field_options_list[i]; + CreateTestSelectField("Field", "Field", options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("day"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 14; + field.form_control_type = "text"; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_EQ(nullptr, field_.get()); + } +} + +// Tests that the field is not classified as |PHONE_HOME_COUNTRY_CODE|. +TEST_F(PhoneFieldTest, IsYearField) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + std::vector> augmented_field_options_list = { + // Numeric four digit year options. + {"1990", "1991", "1992", "1993", "1994", "1995", "1996", + "1997", "1998", "1999", "2000", "2001", "2002", "2003", + "2004", "2005", "2006", "2007", "2008", "2009", "2010"}, + + // Numeric four digit year options less than 10 in total. + {"1990", "1991", "1992", "1993", "1994"}, + + // Numeric four digit year options in decreasing order. + {"2025", "2024", "2023", "2022", "2021", "2020"}, + + // Numeric two digit year options. + {"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "00", "01", + "02", "03", "04", "05", "06"}, + + // Numeric two digit year options along with an additional placeholder + // option. + {"Please select an option", "90", "91", "92", "93", "94", "95", "96", + "97", "98", "99", "00", "01", "02", "03", "04", "05", "06"}, + + // Numeric two digit year options along with an additional placeholder + // option less than 10 in total. + {"Please select an option", "90", "91", "92", "93", "94"}}; + + for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + const auto& options_list = augmented_field_options_list[i]; + CreateTestSelectField("Field", "Field", options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("year"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 14; + field.form_control_type = "text"; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_EQ(nullptr, field_.get()); + } +} + +// Tests that the timezone field is not classified as |PHONE_HOME_COUNTRY_CODE|. +TEST_F(PhoneFieldTest, IsTimeZoneField) { + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableAugmentedPhoneCountryCode); + + FormFieldData field; + std::vector> augmented_field_options_list = { + // Time Zone options. + {"Yemen (UTC+03:00)", "Uruguay (UTC−03:00)", "UAE (UTC+04:00)", + "Uganda (UTC+03:00)", "Turkey (UTC+03:00)", "Taiwan (UTC+08:00)", + "Sweden (UTC+01:00)"}, + + // Time Zone options with a placeholder select element. + {"Please select an option", "Yemen (UTC+03:00)", "Uruguay (UTC−03:00)", + "UAE (UTC+04:00)", "Uganda (UTC+03:00)", "Turkey (UTC+03:00)", + "Taiwan (UTC+08:00)", "Sweden (UTC+01:00)"}}; + + for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + const auto& options_list = augmented_field_options_list[i]; + CreateTestSelectField("Time Zone", "TimeZone", options_list, &field); + list_.push_back( + std::make_unique(field, ASCIIToUTF16("timeZone"))); + + field.label = ASCIIToUTF16("Phone Number"); + field.name = ASCIIToUTF16("phonenumber"); + field.max_length = 14; + field.form_control_type = "text"; + list_.push_back( + std::make_unique(field, ASCIIToUTF16("phoneNumber"))); + + AutofillScanner scanner(list_); + field_ = Parse(&scanner); + ASSERT_EQ(nullptr, field_.get()); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index 8001381efa8..66e954488ec 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -61,7 +61,6 @@ namespace autofill { -using mojom::ButtonTitleType; using mojom::SubmissionIndicatorEvent; namespace { @@ -169,6 +168,9 @@ HtmlFieldType FieldTypeFromAutocompleteAttributeValue( if (autocomplete_attribute_value == "name") return HTML_TYPE_NAME; + if (autocomplete_attribute_value == "honorific-prefix") + return HTML_TYPE_HONORIFIC_PREFIX; + if (autocomplete_attribute_value == "given-name" || autocomplete_attribute_value == "given_name" || autocomplete_attribute_value == "first-name" || @@ -359,32 +361,6 @@ HtmlFieldType FieldTypeFromAutocompleteAttributeValue( return HTML_TYPE_UNRECOGNIZED; } -// Helper function for explicit conversion between |ButtonTitleType| defined in -// "autofill_types.mojom.h" and "server.proto". -AutofillUploadContents_ButtonTitle_ButtonTitleType ToServerButtonTitleType( - ButtonTitleType input) { - switch (input) { - case ButtonTitleType::NONE: - return AutofillUploadContents::ButtonTitle::NONE; - case ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE: - return AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_SUBMIT_TYPE; - case ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE: - return AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_BUTTON_TYPE; - case ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE: - return AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_SUBMIT_TYPE; - case ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE: - return AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_BUTTON_TYPE; - case ButtonTitleType::HYPERLINK: - return AutofillUploadContents::ButtonTitle::HYPERLINK; - case ButtonTitleType::DIV: - return AutofillUploadContents::ButtonTitle::DIV; - case ButtonTitleType::SPAN: - return AutofillUploadContents::ButtonTitle::SPAN; - } - NOTREACHED(); - return AutofillUploadContents::ButtonTitle::NONE; -} - std::ostream& operator<<( std::ostream& out, const autofill::AutofillQueryResponseContents& response) { @@ -394,6 +370,22 @@ std::ostream& operator<<( return out; } +std::ostream& operator<<(std::ostream& out, + const autofill::AutofillQueryResponse& response) { + for (const auto& form : response.form_suggestions()) { + out << "\nForm"; + for (const auto& field : form.field_suggestions()) { + out << "\n Field\n signature: " << field.field_signature(); + if (field.has_primary_type_prediction()) + out << "\n primary_type_prediction: " + << field.primary_type_prediction(); + for (const auto& prediction : field.predictions()) + out << "\n prediction: " << prediction.type(); + } + } + return out; +} + // Returns true iff all form fields autofill types are in |contained_types|. bool AllTypesCaptured(const FormStructure& form, const ServerFieldTypeSet& contained_types) { @@ -467,11 +459,21 @@ void PopulateRandomizedFormMetadata(const RandomizedEncoder& encoder, RandomizedEncoder::FORM_NAME, form.name_attribute(), metadata->mutable_name()); } + + for (const ButtonTitleInfo& e : form.button_titles()) { + auto* button_title = metadata->add_button_title(); + DCHECK(!e.first.empty()); + EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature, + RandomizedEncoder::FORM_BUTTON_TITLES, e.first, + button_title->mutable_title()); + button_title->set_type(static_cast(e.second)); + } auto full_source_url = form.full_source_url().spec(); if (encoder.AnonymousUrlCollectionIsEnabled() && !full_source_url.empty()) { EncodeRandomizedValue(encoder, form_signature, kNullFieldSignature, RandomizedEncoder::FORM_URL, full_source_url, metadata->mutable_url()); + metadata->set_checksum_for_url(StrToHash32Bit(full_source_url)); } } @@ -677,8 +679,11 @@ bool FormStructure::EncodeUploadRequest( bool form_was_autofilled, const std::string& login_form_signature, bool observed_submission, - AutofillUploadContents* upload) const { + AutofillUploadContents* upload, + FormAndFieldSignatures* encoded_signatures) const { DCHECK(AllTypesCaptured(*this, available_field_types)); + encoded_signatures->clear(); + upload->set_submission(observed_submission); upload->set_client_version(kClientVersion); upload->set_form_signature(form_signature().value()); @@ -712,7 +717,7 @@ bool FormStructure::EncodeUploadRequest( for (const ButtonTitleInfo& e : button_titles_) { auto* button_title = upload->add_button_title(); button_title->set_title(base::UTF16ToUTF8(e.first)); - button_title->set_type(ToServerButtonTitleType(e.second)); + button_title->set_type(static_cast(e.second)); } } @@ -725,36 +730,38 @@ bool FormStructure::EncodeUploadRequest( if (IsMalformed()) return false; // Malformed form, skip it. - EncodeFormForUpload(upload); + EncodeFormForUpload(upload, encoded_signatures); return true; } // static bool FormStructure::EncodeQueryRequest( const std::vector& forms, - std::vector* encoded_signatures, - AutofillQueryContents* query) { + AutofillQueryContents* query, + FormAndFieldSignatures* encoded_signatures) { DCHECK(encoded_signatures); encoded_signatures->clear(); encoded_signatures->reserve(forms.size()); query->set_client_version(kClientVersion); - // Some badly formatted web sites repeat forms - detect that and encode only - // one form as returned data would be the same for all the repeated forms. - std::set processed_forms; + // If a page contains repeated forms, detect that and encode only one form as + // the returned data would be the same for all the repeated forms. + // TODO(crbug/1064709#c11): the statement is not entirely correct because + // (1) distinct forms can have identical form signatures because we truncate + // (large) numbers in the form signature calculation while these are + // considered for field signatures; (2) for dynamic forms we will hold on to + // the original form signature. + std::set processed_forms; for (const auto* form : forms) { - std::string signature(form->FormSignatureAsStr()); - if (processed_forms.find(signature) != processed_forms.end()) + if (processed_forms.find(form->form_signature()) != processed_forms.end()) continue; - processed_forms.insert(signature); + processed_forms.insert(form->form_signature()); UMA_HISTOGRAM_COUNTS_1000("Autofill.FieldCount", form->field_count()); if (form->IsMalformed()) continue; - form->EncodeFormForQuery(query->add_form()); - - encoded_signatures->push_back(signature); + form->EncodeFormForQuery(query->add_form(), encoded_signatures); } return !encoded_signatures->empty(); @@ -764,6 +771,7 @@ bool FormStructure::EncodeQueryRequest( void FormStructure::ParseQueryResponse( std::string payload, const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { AutofillMetrics::LogServerQueryMetric( AutofillMetrics::QUERY_RESPONSE_RECEIVED); @@ -775,12 +783,14 @@ void FormStructure::ParseQueryResponse( VLOG(1) << "Autofill query response was successfully parsed:\n" << response; - ProcessQueryResponse(response, forms, form_interactions_ukm_logger); + ProcessQueryResponse(response, forms, encoded_signatures, + form_interactions_ukm_logger); } void FormStructure::ParseApiQueryResponse( base::StringPiece payload, const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { AutofillMetrics::LogServerQueryMetric( AutofillMetrics::QUERY_RESPONSE_RECEIVED); @@ -796,38 +806,56 @@ void FormStructure::ParseApiQueryResponse( if (!response.ParseFromString(decoded_payload)) return; - // TODO(vincb): Make an ostream overloaded function for this. - VLOG(1) << "Autofill query response from API was successfully parsed"; + VLOG(1) << "Autofill query response from API was successfully parsed: " + << response; ProcessQueryResponse(CreateLegacyResponseFromApiResponse(response), forms, - form_interactions_ukm_logger); + encoded_signatures, form_interactions_ukm_logger); } // static void FormStructure::ProcessQueryResponse( const AutofillQueryResponseContents& response, const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED); bool heuristics_detected_fillable_field = false; bool query_response_overrode_heuristics = false; + // Align the server response to the |encoded_signatures|. + auto field_types = [&response, &encoded_signatures] { + std::map, + ::autofill::AutofillQueryResponseContents_Field> + field_types; + auto current_field = response.field().begin(); + for (const auto& form_and_fields : encoded_signatures) { + FormSignature form = form_and_fields.first; + for (const auto& field : form_and_fields.second) { + // In some cases *successful* response does not return all the + // fields. + if (current_field == response.field().end()) + return field_types; + field_types[std::make_pair(form, field)] = *current_field++; + } + } + return field_types; + }(); + // Copy the field types into the actual form. - auto current_field = response.field().begin(); for (FormStructure* form : forms) { bool query_response_has_no_server_data = true; for (auto& field : form->fields_) { - if (form->ShouldSkipField(*field)) + auto it = field_types.find( + std::make_pair(form->form_signature(), field->GetFieldSignature())); + if (it == field_types.end()) continue; - // In some cases *successful* response does not return all the fields. - // Quit the update of the types then. - if (current_field == response.field().end()) - break; + const auto& current_field = it->second; - ServerFieldType field_type = static_cast( - current_field->overall_type_prediction()); + ServerFieldType field_type = + static_cast(current_field.overall_type_prediction()); query_response_has_no_server_data &= field_type == NO_SERVER_DATA; ServerFieldType heuristic_type = field->heuristic_type(); @@ -837,23 +865,21 @@ void FormStructure::ProcessQueryResponse( field->set_server_type(field_type); std::vector server_predictions; - if (current_field->predictions_size() == 0) { + if (current_field.predictions_size() == 0) { AutofillQueryResponseContents::Field::FieldPrediction field_prediction; field_prediction.set_type(field_type); server_predictions.push_back(field_prediction); } else { - server_predictions.assign(current_field->predictions().begin(), - current_field->predictions().end()); + server_predictions.assign(current_field.predictions().begin(), + current_field.predictions().end()); } field->set_server_predictions(std::move(server_predictions)); if (heuristic_type != field->Type().GetStorableType()) query_response_overrode_heuristics = true; - if (current_field->has_password_requirements()) - field->SetPasswordRequirements(current_field->password_requirements()); - - ++current_field; + if (current_field.has_password_requirements()) + field->SetPasswordRequirements(current_field.password_requirements()); } AutofillMetrics::LogServerResponseHasDataForForm( @@ -885,18 +911,12 @@ std::vector FormStructure::GetFieldTypePredictions( forms.reserve(form_structures.size()); for (const FormStructure* form_structure : form_structures) { FormDataPredictions form; - form.data.name = form_structure->form_name_; - form.data.url = form_structure->source_url_; - form.data.action = form_structure->target_url_; - form.data.main_frame_origin = form_structure->main_frame_origin_; - form.data.is_form_tag = form_structure->is_form_tag_; - form.data.is_formless_checkout = form_structure->is_formless_checkout_; + form.data = form_structure->ToFormData(); form.signature = form_structure->FormSignatureAsStr(); for (const auto& field : form_structure->fields_) { - form.data.fields.push_back(FormFieldData(*field)); - FormFieldDataPredictions annotated_field; + annotated_field.field = *field; annotated_field.signature = field->FieldSignatureAsStr(); annotated_field.heuristic_type = AutofillType(field->heuristic_type()).ToString(); @@ -1041,44 +1061,68 @@ void FormStructure::RetrieveFromCache( const FormStructure& cached_form, const bool should_keep_cached_value, const bool only_server_and_autofill_state) { - // Map from field signatures to cached fields. - std::map cached_fields; + // TODO(crbug/1101631) Clean up once the experiment is over. + const bool kUseRendererIds = base::FeatureList::IsEnabled( + features::kAutofillRetrieveFromCacheWithRendererIds); + std::map cached_fields_by_name; + std::map cached_fields_by_id; for (size_t i = 0; i < cached_form.field_count(); ++i) { auto* const field = cached_form.field(i); - cached_fields[field->unique_name()] = field; + if (kUseRendererIds) + cached_fields_by_id[field->unique_renderer_id] = field; + else + cached_fields_by_name[field->unique_name()] = field; } for (auto& field : *this) { - const auto& cached_field = cached_fields.find(field->unique_name()); - if (cached_field != cached_fields.end()) { + const AutofillField* cached_field = nullptr; + if (kUseRendererIds) { + const auto& it = cached_fields_by_id.find(field->unique_renderer_id); + if (it != cached_fields_by_id.end()) + cached_field = it->second; + } else { + const auto& it = cached_fields_by_name.find(field->unique_name()); + if (it != cached_fields_by_name.end()) + cached_field = it->second; + } + + if (cached_field) { if (!only_server_and_autofill_state) { // Transfer attributes of the cached AutofillField to the newly created // AutofillField. - field->set_heuristic_type(cached_field->second->heuristic_type()); - field->SetHtmlType(cached_field->second->html_type(), - cached_field->second->html_mode()); - field->section = cached_field->second->section; + field->set_heuristic_type(cached_field->heuristic_type()); + field->SetHtmlType(cached_field->html_type(), + cached_field->html_mode()); + field->section = cached_field->section; field->set_only_fill_when_focused( - cached_field->second->only_fill_when_focused()); + cached_field->only_fill_when_focused()); } if (should_keep_cached_value) { - field->is_autofilled = cached_field->second->is_autofilled; + field->is_autofilled = cached_field->is_autofilled; } if (field->form_control_type != "select-one") { bool is_credit_card_field = - AutofillType(cached_field->second->Type().GetStorableType()) - .group() == CREDIT_CARD; - if (should_keep_cached_value && is_credit_card_field) { - field->value = cached_field->second->value; + AutofillType(cached_field->Type().GetStorableType()).group() == + CREDIT_CARD; + if (should_keep_cached_value && + (is_credit_card_field || + base::FeatureList::IsEnabled( + features::kAutofillKeepInitialFormValuesInCache))) { + field->value = cached_field->value; value_from_dynamic_change_form_ = true; - } else if (field->value == cached_field->second->value) { + } else if (field->value == cached_field->value && + (!base::FeatureList::IsEnabled( + features:: + kAutofillImportPrefilledCountryAndStateValues) || + (field->server_type() != ADDRESS_HOME_COUNTRY && + field->server_type() != ADDRESS_HOME_STATE))) { + // TODO(crbug.com/1100231): Remove feature check once launched. // From the perspective of learning user data, text fields containing // default values are equivalent to empty fields. field->value = base::string16(); } } - field->set_server_type(cached_field->second->server_type()); - field->set_previously_autofilled( - cached_field->second->previously_autofilled()); + field->set_server_type(cached_field->server_type()); + field->set_previously_autofilled(cached_field->previously_autofilled()); } } @@ -1155,6 +1199,15 @@ void FormStructure::LogQualityMetrics( did_autofill_some_possible_fields = true; else if (!field->only_fill_when_focused()) did_autofill_all_possible_fields = false; + + // If the form was submitted, record if field types have been filled and + // subsequently edited by the user. + if (observed_submission) { + if (field->is_autofilled || field->previously_autofilled()) { + AutofillMetrics::LogEditedAutofilledFieldAtSubmission( + form_interactions_ukm_logger, *this, *field); + } + } } AutofillMetrics::LogNumberOfEditedAutofilledFields( @@ -1390,11 +1443,16 @@ size_t FormStructure::active_field_count() const { FormData FormStructure::ToFormData() const { FormData data; + data.id_attribute = id_attribute_; + data.name_attribute = name_attribute_; data.name = form_name_; + data.button_titles = button_titles_; data.url = source_url_; data.full_url = full_source_url_; data.action = target_url_; data.main_frame_origin = main_frame_origin_; + data.is_form_tag = is_form_tag_; + data.is_formless_checkout = is_formless_checkout_; data.unique_renderer_id = unique_renderer_id_; for (size_t i = 0; i < fields_.size(); ++i) { @@ -1404,23 +1462,6 @@ FormData FormStructure::ToFormData() const { return data; } -bool FormStructure::operator==(const FormData& form) const { - // TODO(jhawkins): Is this enough to differentiate a form? - if (form_name_ == form.name && source_url_ == form.url && - target_url_ == form.action) { - return true; - } - - // TODO(jhawkins): Compare field names, IDs and labels once we have labels - // set up. - - return false; -} - -bool FormStructure::operator!=(const FormData& form) const { - return !operator==(form); -} - FormStructure::SectionedFieldsIndexes::SectionedFieldsIndexes() {} FormStructure::SectionedFieldsIndexes::~SectionedFieldsIndexes() {} @@ -1866,10 +1907,13 @@ void FormStructure::RationalizeFieldTypePredictions() { } void FormStructure::EncodeFormForQuery( - AutofillQueryContents::Form* query_form) const { + AutofillQueryContents::Form* query_form, + FormAndFieldSignatures* encoded_signatures) const { DCHECK(!IsMalformed()); query_form->set_signature(form_signature().value()); + encoded_signatures->emplace_back(); + encoded_signatures->back().first = form_signature(); if (is_rich_query_enabled_) { EncodeFormMetadataForQuery(*this, query_form->mutable_form_metadata()); @@ -1880,8 +1924,8 @@ void FormStructure::EncodeFormForQuery( continue; AutofillQueryContents::Form::Field* added_field = query_form->add_field(); - added_field->set_signature(field->GetFieldSignature().value()); + encoded_signatures->back().second.push_back(field->GetFieldSignature()); if (is_rich_query_enabled_) { EncodeFieldMetadataForQuery(*field, @@ -1897,9 +1941,14 @@ void FormStructure::EncodeFormForQuery( } } -void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const { +void FormStructure::EncodeFormForUpload( + AutofillUploadContents* upload, + FormAndFieldSignatures* encoded_signatures) const { DCHECK(!IsMalformed()); + encoded_signatures->emplace_back(); + encoded_signatures->back().first = form_signature(); + if (randomized_encoder_) { PopulateRandomizedFormMetadata(*randomized_encoder_, *this, upload->mutable_randomized_form_metadata()); @@ -1915,6 +1964,7 @@ void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const { continue; auto* added_field = upload->add_field(); + encoded_signatures->back().second.push_back(field->GetFieldSignature()); for (const auto& field_type : field->possible_types()) { added_field->add_autofill_type(field_type); @@ -2280,6 +2330,45 @@ void FormStructure::RationalizeTypeRelationships() { } } +std::ostream& operator<<(std::ostream& buffer, const FormStructure& form) { + buffer << "\nForm signature: " + << base::StrCat({base::NumberToString(form.form_signature().value()), + " - ", + base::NumberToString( + HashFormSignature(form.form_signature()))}); + buffer << "\n Form name: " << form.form_name(); + buffer << "\n Unique renderer Id: " << form.unique_renderer_id().value(); + buffer << "\n Target URL:" << form.target_url(); + for (size_t i = 0; i < form.field_count(); ++i) { + buffer << "\n Field " << i << ": "; + const AutofillField* field = form.field(i); + buffer << "\n Signature: " + << base::StrCat( + {base::NumberToString(field->GetFieldSignature().value()), + " - ", + base::NumberToString( + HashFieldSignature(field->GetFieldSignature()))}); + buffer << "\n Name: " << field->parseable_name(); + + auto type = field->Type().ToString(); + auto heuristic_type = AutofillType(field->heuristic_type()).ToString(); + auto server_type = AutofillType(field->server_type()).ToString(); + + buffer << "\n Type: " + << base::StrCat({type, " (heuristic: ", heuristic_type, + ", server: ", server_type, ")"}); + buffer << "\n Section: " << field->section; + + constexpr size_t kMaxLabelSize = 100; + const base::string16 truncated_label = + field->label.substr(0, std::min(field->label.length(), kMaxLabelSize)); + buffer << "\n Label: " << truncated_label; + + buffer << "\n Is empty: " << (field->IsEmpty() ? "Yes" : "No"); + } + return buffer; +} + LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) { buffer << Tag{"div"} << Attrib{"class", "form"}; buffer << Tag{"table"}; @@ -2320,6 +2409,8 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) { const base::string16 truncated_label = field->label.substr(0, std::min(field->label.length(), kMaxLabelSize)); buffer << Tr{} << "Label:" << truncated_label; + + buffer << Tr{} << "Is empty:" << (field->IsEmpty() ? "Yes" : "No"); buffer << CTag{"table"}; buffer << CTag{"td"}; buffer << CTag{"tr"}; diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index 9890750a9f8..7e0ceef0fe8 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -49,6 +49,11 @@ enum class PasswordAttribute { kPasswordAttributesCount }; +// The structure of forms and fields, represented by their signatures, on a +// page. These are sequence containers to reflect their order in the DOM. +using FormAndFieldSignatures = + std::vector>>; + struct FormData; struct FormDataPredictions; @@ -65,43 +70,41 @@ class FormStructure { // types. void DetermineHeuristicTypes(LogManager* log_manager = nullptr); - // Encodes the proto |upload| request from this FormStructure. + // Encodes the proto |upload| request from this FormStructure, and stores + // the (single) FormSignature and the signatures of the fields to be uploaded + // in |encoded_signatures|. // In some cases, a |login_form_signature| is included as part of the upload. // This field is empty when sending upload requests for non-login forms. bool EncodeUploadRequest(const ServerFieldTypeSet& available_field_types, bool form_was_autofilled, const std::string& login_form_signature, bool observed_submission, - autofill::AutofillUploadContents* upload) const; - - // Encodes the proto |query| request for the set of |forms| that are valid - // (see implementation for details on which forms are not included in the - // query). The form signatures used in the Query request are output in - // |encoded_signatures|. All valid fields are encoded in |query|. + autofill::AutofillUploadContents* upload, + FormAndFieldSignatures* encoded_signatures) const; + + // Encodes the proto |query| request for the list of |forms| and their fields + // that are valid. The queried FormSignatures and FieldSignatures are stored + // in |encoded_signatures| in the same order as in |query|. In case multiple + // FormStructures have the same FormSignature, only the first one is included + // in |query| and |encoded_signatures|. static bool EncodeQueryRequest(const std::vector& forms, - std::vector* encoded_signatures, - autofill::AutofillQueryContents* query); + autofill::AutofillQueryContents* query, + FormAndFieldSignatures* encoded_signatures); // Parses response as AutofillQueryResponseContents proto and calls // ProcessQueryResponse. - static void ParseQueryResponse(std::string response, - const std::vector& forms, - AutofillMetrics::FormInteractionsUkmLogger*); + static void ParseQueryResponse( + std::string response, + const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, + AutofillMetrics::FormInteractionsUkmLogger*); static void ParseApiQueryResponse( base::StringPiece payload, const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, AutofillMetrics::FormInteractionsUkmLogger*); - // Parses the field types from the server query response. |forms| must be the - // same as the one passed to EncodeQueryRequest when constructing the query. - // |form_interactions_ukm_logger| is used to provide logs to UKM and can be - // null in tests. - static void ProcessQueryResponse( - const AutofillQueryResponseContents& response, - const std::vector& forms, - AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger); - // Returns predictions using the details from the given |form_structures| and // their fields' predicted types. static std::vector GetFieldTypePredictions( @@ -241,6 +244,8 @@ class FormStructure { const url::Origin& main_frame_origin() const { return main_frame_origin_; } + const ButtonTitleList& button_titles() const { return button_titles_; } + bool has_author_specified_types() const { return has_author_specified_types_; } @@ -268,7 +273,11 @@ class FormStructure { bool all_fields_are_passwords() const { return all_fields_are_passwords_; } - FormSignature form_signature() const { return form_signature_; } + const FormSignature form_signature() const { return form_signature_; } + + void set_form_signature(FormSignature signature) { + form_signature_ = signature; + } // Returns a FormData containing the data this form structure knows about. FormData ToFormData() const; @@ -349,8 +358,6 @@ class FormStructure { void set_submission_source(mojom::SubmissionSource submission_source) { submission_source_ = submission_source; } - bool operator==(const FormData& form) const; - bool operator!=(const FormData& form) const; // Returns an identifier that is used by the refill logic. Takes the first non // empty of these or returns an empty string: @@ -380,6 +387,20 @@ class FormStructure { FormRendererId unique_renderer_id() const { return unique_renderer_id_; } + bool ShouldSkipFieldVisibleForTesting(const FormFieldData& field) const { + return ShouldSkipField(field); + } + + static void ProcessQueryResponseForTesting( + const AutofillQueryResponseContents& response, + const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, + AutofillMetrics::FormInteractionsUkmLogger* + form_interactions_ukm_logger) { + ProcessQueryResponse(response, forms, encoded_signatures, + form_interactions_ukm_logger); + } + private: friend class AutofillMergeTest; friend class FormStructureTest; @@ -434,6 +455,16 @@ class FormStructure { size_t current_section_ptr = 0; }; + // Parses the field types from the server query response. |forms| must be the + // same as the one passed to EncodeQueryRequest when constructing the query. + // |form_interactions_ukm_logger| is used to provide logs to UKM and can be + // null in tests. + static void ProcessQueryResponse( + const AutofillQueryResponseContents& response, + const std::vector& forms, + const FormAndFieldSignatures& encoded_signatures, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger); + FormStructure(FormSignature form_signature, const std::vector& field_signatures); @@ -498,12 +529,11 @@ class FormStructure { // when it considers necessary. void RationalizeFieldTypePredictions(); - // Encodes information about this form and its fields into |query_form|. - void EncodeFormForQuery( - autofill::AutofillQueryContents::Form* query_form) const; + void EncodeFormForQuery(autofill::AutofillQueryContents::Form* query_form, + FormAndFieldSignatures* encoded_signatures) const; - // Encodes information about this form and its fields into |upload|. - void EncodeFormForUpload(autofill::AutofillUploadContents* upload) const; + void EncodeFormForUpload(autofill::AutofillUploadContents* upload, + FormAndFieldSignatures* encoded_signatures) const; // Returns true if the form has no fields, or too many. bool IsMalformed() const; @@ -663,6 +693,8 @@ class FormStructure { }; LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form); +std::ostream& operator<<(std::ostream& buffer, const FormStructure& form); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_H_ diff --git a/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc b/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc index b684f2f6ed2..8e3870a008c 100644 --- a/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc +++ b/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc @@ -7,6 +7,7 @@ #include #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" @@ -35,7 +36,8 @@ void AddField(const std::string& label, // forms vectors, so it can be changed if needed. DEFINE_BINARY_PROTO_FUZZER(const AutofillQueryResponseContents& response) { std::vector forms; - FormStructure::ProcessQueryResponse(response, forms, nullptr); + FormStructure::ProcessQueryResponseForTesting( + response, forms, test::GetEncodedSignatures(forms), nullptr); FormData form_data; AddField("username", "username", "text", &form_data); @@ -43,7 +45,8 @@ DEFINE_BINARY_PROTO_FUZZER(const AutofillQueryResponseContents& response) { FormStructure form(form_data); forms.push_back(&form); - FormStructure::ProcessQueryResponse(response, forms, nullptr); + FormStructure::ProcessQueryResponseForTesting( + response, forms, test::GetEncodedSignatures(forms), nullptr); } } // namespace diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index 0e23ab24aea..f5e7a3ae4bb 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -39,7 +39,6 @@ namespace autofill { using features::kAutofillEnforceMinRequiredFieldsForHeuristics; using features::kAutofillEnforceMinRequiredFieldsForQuery; using features::kAutofillEnforceMinRequiredFieldsForUpload; -using mojom::ButtonTitleType; using mojom::SubmissionIndicatorEvent; using mojom::SubmissionSource; @@ -2349,7 +2348,18 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + + FormAndFieldSignatures expected_signatures; + expected_signatures.push_back( + {form_structure.form_signature(), + { + form_structure.field(0)->GetFieldSignature(), + form_structure.field(1)->GetFieldSignature(), + form_structure.field(2)->GetFieldSignature(), + form_structure.field(3)->GetFieldSignature(), + form_structure.field(4)->GetFieldSignature() + // field 5 is checkable, and hence skipped. + }}); // Prepare the expected proto string. AutofillQueryContents query; @@ -2371,13 +2381,11 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - const std::string kSignature1 = form_structure.FormSignatureAsStr(); - AutofillQueryContents encoded_query; - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); - ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kSignature1, encoded_signatures[0]); + FormAndFieldSignatures encoded_signatures; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); + EXPECT_EQ(encoded_signatures, expected_signatures); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -2388,11 +2396,13 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { FormStructure form_structure2(form); forms.push_back(&form_structure2); + FormAndFieldSignatures expected_signatures2 = expected_signatures; + AutofillQueryContents encoded_query2; - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query2)); - ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kSignature1, encoded_signatures[0]); + FormAndFieldSignatures encoded_signatures2; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query2, + &encoded_signatures2)); + EXPECT_EQ(encoded_signatures2, expected_signatures2); encoded_query2.SerializeToString(&encoded_query_string); EXPECT_EQ(expected_query_string, encoded_query_string); @@ -2407,6 +2417,21 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { FormStructure form_structure3(form); forms.push_back(&form_structure3); + FormAndFieldSignatures expected_signatures3 = expected_signatures2; + expected_signatures3.push_back( + {form_structure3.form_signature(), + {form_structure3.field(0)->GetFieldSignature(), + form_structure3.field(1)->GetFieldSignature(), + form_structure3.field(2)->GetFieldSignature(), + form_structure3.field(3)->GetFieldSignature(), + form_structure3.field(4)->GetFieldSignature(), + // field 5 is checkable, and hence skipped. + form_structure3.field(6)->GetFieldSignature(), + form_structure3.field(7)->GetFieldSignature(), + form_structure3.field(8)->GetFieldSignature(), + form_structure3.field(9)->GetFieldSignature(), + form_structure3.field(10)->GetFieldSignature()}}); + // Add the second form to the expected proto. query_form = query.add_form(); query_form->set_signature(form_structure3.form_signature().value()); @@ -2429,16 +2454,31 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { ASSERT_TRUE(query.SerializeToString(&expected_query_string)); AutofillQueryContents encoded_query3; - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query3)); - ASSERT_EQ(2U, encoded_signatures.size()); - EXPECT_EQ(kSignature1, encoded_signatures[0]); - const std::string kSignature2 = form_structure3.FormSignatureAsStr(); - EXPECT_EQ(kSignature2, encoded_signatures[1]); + FormAndFieldSignatures encoded_signatures3; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query3, + &encoded_signatures3)); + EXPECT_EQ(encoded_signatures3, expected_signatures3); encoded_query3.SerializeToString(&encoded_query_string); EXPECT_EQ(expected_query_string, encoded_query_string); + // |form_structures4| will have the same signature as |form_structure3|. + form.fields.back().name = ASCIIToUTF16("address123456789"); + + FormStructure form_structure4(form); + forms.push_back(&form_structure4); + + FormAndFieldSignatures expected_signatures4 = expected_signatures3; + + AutofillQueryContents encoded_query4; + FormAndFieldSignatures encoded_signatures4; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query4, + &encoded_signatures4)); + EXPECT_EQ(encoded_signatures4, expected_signatures4); + + encoded_query4.SerializeToString(&encoded_query_string); + EXPECT_EQ(expected_query_string, encoded_query_string); + FormData malformed_form(form); // Add 300 address fields - the form is not valid anymore, but previous ones // are. The result should be the same as in previous test. @@ -2450,22 +2490,25 @@ TEST_F(FormStructureTest, EncodeQueryRequest) { FormStructure malformed_form_structure(malformed_form); forms.push_back(&malformed_form_structure); - AutofillQueryContents encoded_query4; - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query4)); - ASSERT_EQ(2U, encoded_signatures.size()); - EXPECT_EQ(kSignature1, encoded_signatures[0]); - EXPECT_EQ(kSignature2, encoded_signatures[1]); - encoded_query4.SerializeToString(&encoded_query_string); + FormAndFieldSignatures expected_signatures5 = expected_signatures4; + + AutofillQueryContents encoded_query5; + FormAndFieldSignatures encoded_signatures5; + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query5, + &encoded_signatures5)); + EXPECT_EQ(encoded_signatures5, expected_signatures5); + + encoded_query5.SerializeToString(&encoded_query_string); EXPECT_EQ(expected_query_string, encoded_query_string); // Check that we fail if there are only bad form(s). std::vector bad_forms; bad_forms.push_back(&malformed_form_structure); - AutofillQueryContents encoded_query5; - EXPECT_FALSE(FormStructure::EncodeQueryRequest(bad_forms, &encoded_signatures, - &encoded_query5)); + AutofillQueryContents encoded_query6; + FormAndFieldSignatures encoded_signatures6; + EXPECT_FALSE(FormStructure::EncodeQueryRequest(bad_forms, &encoded_query6, + &encoded_signatures6)); } TEST_F(FormStructureTest, EncodeUploadRequest_SubmissionIndicatorEvents_Match) { @@ -2507,44 +2550,43 @@ TEST_F(FormStructureTest, EncodeUploadRequest_SubmissionIndicatorEvents_Match) { } TEST_F(FormStructureTest, ButtonTitleType_Match) { - // Statically assert that the mojo ButtonTitleType enum matches the - // corresponding entries the in proto AutofillUploadContents::ButtonTitle - // ButtonTitleType enum. - static_assert(AutofillUploadContents::ButtonTitle::NONE == - static_cast(ButtonTitleType::NONE), - "NONE enumerator does not match!"); + // Statically assert that the mojom::ButtonTitleType enum matches the + // corresponding entries in the proto - ButtonTitleType enum. + static_assert( + ButtonTitleType::NONE == static_cast(mojom::ButtonTitleType::NONE), + "NONE enumerator does not match!"); static_assert( - AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_SUBMIT_TYPE == - static_cast(ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE), + ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE == + static_cast(mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE), "BUTTON_ELEMENT_SUBMIT_TYPE enumerator does not match!"); static_assert( - AutofillUploadContents::ButtonTitle::BUTTON_ELEMENT_BUTTON_TYPE == - static_cast(ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE), + ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE == + static_cast(mojom::ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE), "BUTTON_ELEMENT_BUTTON_TYPE enumerator does not match!"); static_assert( - AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_SUBMIT_TYPE == - static_cast(ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE), + ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE == + static_cast(mojom::ButtonTitleType::INPUT_ELEMENT_SUBMIT_TYPE), "INPUT_ELEMENT_SUBMIT_TYPE enumerator does not match!"); static_assert( - AutofillUploadContents::ButtonTitle::INPUT_ELEMENT_BUTTON_TYPE == - static_cast(ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE), + ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE == + static_cast(mojom::ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE), "INPUT_ELEMENT_BUTTON_TYPE enumerator does not match!"); - static_assert(AutofillUploadContents::ButtonTitle::HYPERLINK == - static_cast(ButtonTitleType::HYPERLINK), + static_assert(ButtonTitleType::HYPERLINK == + static_cast(mojom::ButtonTitleType::HYPERLINK), "HYPERLINK enumerator does not match!"); - static_assert(AutofillUploadContents::ButtonTitle::DIV == - static_cast(ButtonTitleType::DIV), - "DIV enumerator does not match!"); + static_assert( + ButtonTitleType::DIV == static_cast(mojom::ButtonTitleType::DIV), + "DIV enumerator does not match!"); - static_assert(AutofillUploadContents::ButtonTitle::SPAN == - static_cast(ButtonTitleType::SPAN), - "SPAN enumerator does not match!"); + static_assert( + ButtonTitleType::SPAN == static_cast(mojom::ButtonTitleType::SPAN), + "SPAN enumerator does not match!"); } TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { @@ -2669,10 +2711,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { //////////////// std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + available_field_types, false, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -2684,7 +2728,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload2)); + available_field_types, true, std::string(), true, &encoded_upload2, + &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -2737,7 +2782,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3)); + available_field_types, false, std::string(), true, &encoded_upload3, + &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -2862,10 +2908,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithNonMatchingValidities) { //////////////// std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + available_field_types, false, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -2995,10 +3043,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMultipleValidities) { //////////////// std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + available_field_types, false, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3081,6 +3131,18 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { possible_field_types_validities[i]); } + FormAndFieldSignatures expected_signatures; + expected_signatures.push_back( + {form_structure->form_signature(), + { + form_structure->field(0)->GetFieldSignature(), + form_structure->field(1)->GetFieldSignature(), + form_structure->field(2)->GetFieldSignature(), + form_structure->field(3)->GetFieldSignature(), + form_structure->field(4)->GetFieldSignature() + // Field 5 is checkable and hence skipped. + }}); + ServerFieldTypeSet available_field_types; available_field_types.insert(NAME_FIRST); available_field_types.insert(NAME_LAST); @@ -3119,10 +3181,13 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + available_field_types, false, std::string(), true, &encoded_upload, + &signatures)); + EXPECT_EQ(signatures, expected_signatures); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3134,7 +3199,9 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload2)); + available_field_types, true, std::string(), true, &encoded_upload2, + &signatures)); + EXPECT_EQ(signatures, expected_signatures); encoded_upload2.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -3166,6 +3233,13 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { possible_field_types_validities[i]); } + expected_signatures[0].first = form_structure->form_signature(); + // Field 5 is checkable and hence skipped. + expected_signatures[0].second.push_back( + form_structure->field(6)->GetFieldSignature()); + expected_signatures[0].second.push_back( + form_structure->field(7)->GetFieldSignature()); + // Adjust the expected proto string. upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); @@ -3187,7 +3261,9 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3)); + available_field_types, false, std::string(), true, &encoded_upload3, + &signatures)); + EXPECT_EQ(signatures, expected_signatures); encoded_upload3.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -3215,7 +3291,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { AutofillUploadContents encoded_upload4; EXPECT_FALSE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4)); + available_field_types, false, std::string(), true, &encoded_upload4, + &signatures)); } TEST_F(FormStructureTest, @@ -3335,10 +3412,11 @@ TEST_F(FormStructureTest, std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; - EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, true, - "42", true, &encoded_upload)); + EXPECT_TRUE(form_structure->EncodeUploadRequest( + available_field_types, true, "42", true, &encoded_upload, &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3419,8 +3497,10 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithAutocomplete) { ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); AutofillUploadContents encoded_upload; + FormAndFieldSignatures signatures; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3519,10 +3599,12 @@ TEST_F(FormStructureTest, EncodeUploadRequestWithPropertiesMask) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3601,11 +3683,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_ObservedSubmissionFalse) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( available_field_types, true, std::string(), - /* observed_submission= */ false, &encoded_upload)); + /* observed_submission= */ false, &encoded_upload, &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3677,10 +3760,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithLabels) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3757,10 +3842,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithCssClassesAndIds) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3835,10 +3922,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithFormName) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3917,10 +4006,12 @@ TEST_F(FormStructureTest, EncodeUploadRequestPartialMetadata) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -4010,10 +4101,12 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload)); + available_field_types, true, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -4086,10 +4179,12 @@ TEST_F(FormStructureTest, CheckDataPresence) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; - EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false, + std::string(), true, + &encoded_upload, &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -4119,7 +4214,8 @@ TEST_F(FormStructureTest, CheckDataPresence) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload2)); + available_field_types, false, std::string(), true, &encoded_upload2, + &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4172,7 +4268,8 @@ TEST_F(FormStructureTest, CheckDataPresence) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3)); + available_field_types, false, std::string(), true, &encoded_upload3, + &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4203,7 +4300,8 @@ TEST_F(FormStructureTest, CheckDataPresence) { AutofillUploadContents encoded_upload4; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4)); + available_field_types, false, std::string(), true, &encoded_upload4, + &signatures)); encoded_upload4.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4270,7 +4368,8 @@ TEST_F(FormStructureTest, CheckDataPresence) { AutofillUploadContents encoded_upload5; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload5)); + available_field_types, false, std::string(), true, &encoded_upload5, + &signatures)); encoded_upload5.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4371,10 +4470,12 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); + FormAndFieldSignatures signatures; AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload)); + available_field_types, false, std::string(), true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -4397,7 +4498,8 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload2)); + available_field_types, false, std::string(), true, &encoded_upload2, + &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4414,7 +4516,8 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3)); + available_field_types, false, std::string(), true, &encoded_upload3, + &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4439,7 +4542,8 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { AutofillUploadContents encoded_upload4; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4)); + available_field_types, false, std::string(), true, &encoded_upload4, + &signatures)); encoded_upload4.SerializeToString(&encoded_upload_string); EXPECT_EQ(expected_upload_string, encoded_upload_string); @@ -4466,10 +4570,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_PasswordsRevealed) { FormStructure form_structure(form); form_structure.set_passwords_were_revealed(true); AutofillUploadContents upload; + FormAndFieldSignatures signatures; EXPECT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, true /* observed_submission */, - &upload)); + &upload, &signatures)); EXPECT_EQ(true, upload.passwords_revealed()); } @@ -4488,10 +4593,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_IsFormTag) { FormStructure form_structure(form); form_structure.set_passwords_were_revealed(true); AutofillUploadContents upload; + FormAndFieldSignatures signatures; EXPECT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, - true /* observed_submission */, &upload)); + true /* observed_submission */, &upload, &signatures)); EXPECT_EQ(is_form_tag, upload.has_form_tag()); } } @@ -4518,6 +4624,9 @@ TEST_F(FormStructureTest, EncodeUploadRequest_RichMetadata) { FormData form; form.id_attribute = ASCIIToUTF16("form-id"); form.url = GURL("http://www.foo.com/"); + form.button_titles = { + std::make_pair(ASCIIToUTF16("Submit"), + mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form.full_url = GURL("http://www.foo.com/?foo=bar"); for (const auto& f : kFieldMetadata) { FormFieldData field; @@ -4540,10 +4649,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_RichMetadata) { std::make_unique(encoder)); AutofillUploadContents upload; + FormAndFieldSignatures signatures; ASSERT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, true /* observed_submission */, - &upload)); + &upload, &signatures)); const auto form_signature = form_structure.form_signature(); @@ -4571,6 +4681,18 @@ TEST_F(FormStructureTest, EncodeUploadRequest_RichMetadata) { RandomizedEncoder::FORM_URL, full_url)); ASSERT_EQ(static_cast(upload.field_size()), base::size(kFieldMetadata)); + + ASSERT_EQ(1, upload.randomized_form_metadata().button_title().size()); + EXPECT_EQ(upload.randomized_form_metadata() + .button_title()[0] + .title() + .encoded_bits(), + encoder.EncodeForTesting(form_signature, FieldSignature(), + RandomizedEncoder::FORM_BUTTON_TITLES, + form.button_titles[0].first)); + EXPECT_EQ(ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE, + upload.randomized_form_metadata().button_title()[0].type()); + for (int i = 0; i < upload.field_size(); ++i) { const auto& metadata = upload.field(i).randomized_field_metadata(); const auto& field = *form_structure.field(i); @@ -4672,7 +4794,9 @@ TEST_F(FormStructureTest, Metadata_OnlySendFullUrlWithUserConsent) { FormStructure form_structure(form); form_structure.set_randomized_encoder(RandomizedEncoder::Create(&prefs)); AutofillUploadContents upload = AutofillUploadContents(); - form_structure.EncodeUploadRequest({}, true, "", true, &upload); + FormAndFieldSignatures signatures; + form_structure.EncodeUploadRequest({}, true, "", true, &upload, + &signatures); EXPECT_EQ(has_consent, upload.randomized_form_metadata().has_url()); } @@ -4798,7 +4922,7 @@ TEST_F(FormStructureTest, SkipFieldTest) { FormStructure form_structure(form); std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; // Create the expected query and serialize it to a string. @@ -4813,12 +4937,12 @@ TEST_F(FormStructureTest, SkipFieldTest) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - const char kExpectedSignature[] = "18006745212084723782"; + const FormSignature kExpectedSignature(18006745212084723782UL); - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kExpectedSignature, encoded_signatures[0]); + EXPECT_EQ(kExpectedSignature, encoded_signatures.front().first); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -4850,7 +4974,7 @@ TEST_F(FormStructureTest, EncodeQueryRequest_WithLabels) { std::vector forms; FormStructure form_structure(form); forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; // Create the expected query and serialize it to a string. @@ -4867,8 +4991,8 @@ TEST_F(FormStructureTest, EncodeQueryRequest_WithLabels) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - EXPECT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + EXPECT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -4905,7 +5029,7 @@ TEST_F(FormStructureTest, EncodeQueryRequest_WithLongLabels) { FormStructure form_structure(form); std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; // Create the expected query and serialize it to a string. @@ -4922,8 +5046,8 @@ TEST_F(FormStructureTest, EncodeQueryRequest_WithLongLabels) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - EXPECT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + EXPECT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -4954,7 +5078,7 @@ TEST_F(FormStructureTest, EncodeQueryRequest_MissingNames) { std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; // Create the expected query and serialize it to a string. @@ -4969,12 +5093,12 @@ TEST_F(FormStructureTest, EncodeQueryRequest_MissingNames) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - const char kExpectedSignature[] = "16416961345885087496"; + const FormSignature kExpectedSignature(16416961345885087496UL); - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kExpectedSignature, encoded_signatures[0]); + EXPECT_EQ(kExpectedSignature, encoded_signatures.front().first); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -5005,7 +5129,7 @@ TEST_F(FormStructureTest, EncodeQueryRequest_DisabledMetadataTrial) { FormStructure form_structure(form); std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; // Create the expected query and serialize it to a string. @@ -5020,12 +5144,12 @@ TEST_F(FormStructureTest, EncodeQueryRequest_DisabledMetadataTrial) { std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - const char kExpectedSignature[] = "7635954436925888745"; + const FormSignature kExpectedSignature(7635954436925888745UL); - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kExpectedSignature, encoded_signatures[0]); + EXPECT_EQ(kExpectedSignature, encoded_signatures.front().first); std::string encoded_query_string; encoded_query.SerializeToString(&encoded_query_string); @@ -5073,6 +5197,73 @@ TEST_F(FormStructureTest, PossibleValues) { EXPECT_EQ(0U, form_structure2.PossibleValues(ADDRESS_BILLING_COUNTRY).size()); } +// Tests proper resolution heuristic, server and html field types when the +// server returns NO_SERVER_DATA, UNKNOWN_TYPE, and a valid type. +TEST_F(FormStructureTest, ParseQueryResponse_TooManyTypes) { + FormData form_data; + FormFieldData field; + form_data.url = GURL("http://foo.com"); + field.form_control_type = "text"; + + field.label = ASCIIToUTF16("First Name"); + field.name = ASCIIToUTF16("fname"); + form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("Last Name"); + field.name = ASCIIToUTF16("lname"); + form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("email"); + field.name = ASCIIToUTF16("email"); + field.autocomplete_attribute = "address-level2"; + form_data.fields.push_back(field); + + FormStructure form(form_data); + form.DetermineHeuristicTypes(); + + // Setup the query response. + AutofillQueryResponseContents response; + std::string response_string; + response.add_field()->set_overall_type_prediction(NAME_FIRST); + response.add_field()->set_overall_type_prediction(NAME_LAST); + response.add_field()->set_overall_type_prediction(ADDRESS_HOME_LINE1); + response.add_field()->set_overall_type_prediction(EMAIL_ADDRESS); + response.add_field()->set_overall_type_prediction(UNKNOWN_TYPE); + ASSERT_TRUE(response.SerializeToString(&response_string)); + + // Parse the response and update the field type predictions. + std::vector forms{&form}; + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); + ASSERT_EQ(form.field_count(), 3U); + + // Validate field 0. + EXPECT_EQ(NAME_FIRST, form.field(0)->heuristic_type()); + EXPECT_EQ(NAME_FIRST, form.field(0)->server_type()); + EXPECT_EQ(HTML_TYPE_UNSPECIFIED, form.field(0)->html_type()); + EXPECT_EQ(NAME_FIRST, form.field(0)->Type().GetStorableType()); + + // Validate field 1. + EXPECT_EQ(NAME_LAST, form.field(1)->heuristic_type()); + EXPECT_EQ(NAME_LAST, form.field(1)->server_type()); + EXPECT_EQ(HTML_TYPE_UNSPECIFIED, form.field(1)->html_type()); + EXPECT_EQ(NAME_LAST, form.field(1)->Type().GetStorableType()); + + // Validate field 2. Note: HTML_TYPE_ADDRESS_LEVEL2 -> City + EXPECT_EQ(EMAIL_ADDRESS, form.field(2)->heuristic_type()); + EXPECT_EQ(ADDRESS_HOME_LINE1, form.field(2)->server_type()); + EXPECT_EQ(HTML_TYPE_ADDRESS_LEVEL2, form.field(2)->html_type()); + EXPECT_EQ(ADDRESS_HOME_CITY, form.field(2)->Type().GetStorableType()); + + // Also check the extreme case of an empty form. + FormStructure empty_form{FormData()}; + std::vector empty_forms{&empty_form}; + FormStructure::ParseQueryResponse(response_string, empty_forms, + test::GetEncodedSignatures(empty_forms), + nullptr); + ASSERT_EQ(empty_form.field_count(), 0U); +} + // Tests proper resolution heuristic, server and html field types when the // server returns NO_SERVER_DATA, UNKNOWN_TYPE, and a valid type. TEST_F(FormStructureTest, ParseQueryResponse_UnknownType) { @@ -5107,7 +5298,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_UnknownType) { // Parse the response and update the field type predictions. std::vector forms{&form}; - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(form.field_count(), 3U); // Validate field 0. @@ -5184,7 +5376,8 @@ TEST_F(FormStructureTest, ParseQueryResponse) { std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_GE(forms[0]->field_count(), 2U); ASSERT_GE(forms[1]->field_count(), 2U); @@ -5268,7 +5461,8 @@ TEST_F(FormStructureTest, ParseApiQueryResponse) { base::Base64Encode(response_string, &encoded_response_string); FormStructure::ParseApiQueryResponse(std::move(encoded_response_string), - forms, nullptr); + forms, test::GetEncodedSignatures(forms), + nullptr); // Verify that the form fields are properly filled with data retrieved from // the query. @@ -5311,6 +5505,7 @@ TEST_F(FormStructureTest, ParseApiQueryResponseWhenCannotParseProtoFromString) { std::string response_string = "invalid string that cannot be parsed"; FormStructure::ParseApiQueryResponse(std::move(response_string), forms, + test::GetEncodedSignatures(forms), nullptr); // Verify that the form fields remain intact because ParseApiQueryResponse @@ -5353,7 +5548,8 @@ TEST_F(FormStructureTest, ParseApiQueryResponseWhenPayloadNotBase64) { std::string response_string; ASSERT_TRUE(api_response.SerializeToString(&response_string)); - FormStructure::ParseApiQueryResponse(response_string, forms, nullptr); + FormStructure::ParseApiQueryResponse( + response_string, forms, test::GetEncodedSignatures(forms), nullptr); // Verify that the form fields remain intact because ParseApiQueryResponse // could not parse the server's response that was badly encoded. @@ -5389,7 +5585,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_AuthorDefinedTypes) { std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_GE(forms[0]->field_count(), 2U); // Server type is parsed from the response and is the end result type. @@ -5438,7 +5635,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeLoneField) { ASSERT_TRUE(response.SerializeToString(&response_string)); // Test that the expiry month field is rationalized away. - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType()); @@ -5478,7 +5676,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeCCName) { ASSERT_TRUE(response.SerializeToString(&response_string)); // Test that the name fields are rationalized. - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(3U, forms[0]->field_count()); EXPECT_EQ(NAME_FIRST, forms[0]->field(0)->Type().GetStorableType()); @@ -5529,7 +5728,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_1) { ASSERT_TRUE(response.SerializeToString(&response_string)); // Test that the extra month field is rationalized away. - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(5U, forms[0]->field_count()); EXPECT_EQ(CREDIT_CARD_NAME_FULL, @@ -5580,7 +5780,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_2) { ASSERT_TRUE(response.SerializeToString(&response_string)); // Test that the extra month field is rationalized away. - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); EXPECT_EQ(CREDIT_CARD_NAME_FULL, @@ -5667,7 +5868,8 @@ TEST_F(FormStructureTest, RationalizePhoneNumber_RunsOncePerSection) { FormStructure form_structure(form); std::vector forms; forms.push_back(&form_structure); - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); EXPECT_FALSE(form_structure.phone_rationalized_["fullName_1-default"]); form_structure.RationalizePhoneNumbersInSection("fullName_1-default"); @@ -5719,7 +5921,8 @@ TEST_F(FormStructureTest, RationalizeRepeatedFields_OneAddress) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(3U, forms[0]->field_count()); @@ -5771,7 +5974,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_TwoAddresses) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -5829,7 +6033,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_ThreeAddresses) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(5U, forms[0]->field_count()); @@ -5895,7 +6100,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_FourAddresses) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(6U, forms[0]->field_count()); @@ -5970,7 +6176,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_OneAddressEachSection) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); // Billing ASSERT_EQ(1U, forms.size()); ASSERT_EQ(6U, forms[0]->field_count()); @@ -6112,7 +6319,8 @@ TEST_F( forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(15U, forms[0]->field_count()); @@ -6197,7 +6405,8 @@ TEST_F(FormStructureTest, ASSERT_TRUE(response.SerializeToString(&response_string)); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); // Billing ASSERT_EQ(1U, forms.size()); ASSERT_EQ(6U, forms[0]->field_count()); @@ -6288,7 +6497,8 @@ TEST_F( std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(9U, forms[0]->field_count()); @@ -6382,7 +6592,8 @@ TEST_F(FormStructureTest, ASSERT_TRUE(response.SerializeToString(&response_string)); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(10U, forms[0]->field_count()); @@ -6508,7 +6719,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_CountryStateNoHeuristics) { ASSERT_TRUE(response.SerializeToString(&response_string)); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(14U, forms[0]->field_count()); @@ -6639,7 +6851,8 @@ TEST_F(FormStructureTest, ASSERT_TRUE(response.SerializeToString(&response_string)); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(14U, forms[0]->field_count()); @@ -6713,7 +6926,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_FirstFieldRationalized) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(5U, forms[0]->field_count()); @@ -6781,7 +6995,8 @@ TEST_F(FormStructureTest, RationalizeRepreatedFields_LastFieldRationalized) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(6U, forms[0]->field_count()); @@ -6852,7 +7067,8 @@ TEST_P(ParameterizedFormStructureTest, forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -6917,7 +7133,8 @@ TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -6992,7 +7209,8 @@ TEST_P(ParameterizedFormStructureTest, WithServerDataCCFields_CVC_NoOverwrite) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -7078,7 +7296,8 @@ TEST_P(RationalizationFieldTypeFilterTest, Rationalization_Rules_Filter_Out) { forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -7138,7 +7357,8 @@ TEST_P(RationalizationFieldTypeRelationshipsTest, forms.push_back(&form_structure); // Will call RationalizeFieldTypePredictions - FormStructure::ParseQueryResponse(response_string, forms, nullptr); + FormStructure::ParseQueryResponse(response_string, forms, + test::GetEncodedSignatures(forms), nullptr); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); @@ -7168,11 +7388,11 @@ TEST_F(FormStructureTest, AllowBigForms) { std::vector forms; forms.push_back(&form_structure); - std::vector encoded_signatures; + FormAndFieldSignatures encoded_signatures; AutofillQueryContents encoded_query; - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures, - &encoded_query)); + ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, + &encoded_signatures)); EXPECT_EQ(1u, encoded_signatures.size()); } @@ -7200,12 +7420,14 @@ TEST_F(FormStructureTest, CreateForPasswordManagerUpload) { FormSignature(1234), {FieldSignature(1), FieldSignature(10), FieldSignature(100)}); AutofillUploadContents upload; + FormAndFieldSignatures signatures; EXPECT_EQ(FormSignature(1234u), form->form_signature()); ASSERT_EQ(3u, form->field_count()); ASSERT_EQ(FieldSignature(100u), form->field(2)->GetFieldSignature()); EXPECT_TRUE(form->EncodeUploadRequest( {} /* available_field_types */, false /* form_was_autofilled */, - "" /*login_form_signature*/, true /*observed_submission*/, &upload)); + "" /*login_form_signature*/, true /*observed_submission*/, &upload, + &signatures)); } // Tests if a new logical form is started with the second appearance of a field diff --git a/chromium/components/autofill/core/browser/geo/country_names.cc b/chromium/components/autofill/core/browser/geo/country_names.cc index f632ff8f6ac..a5db1d0573d 100644 --- a/chromium/components/autofill/core/browser/geo/country_names.cc +++ b/chromium/components/autofill/core/browser/geo/country_names.cc @@ -130,7 +130,8 @@ const std::string CountryNames::GetCountryCodeForLocalizedCountryName( result = country_names_for_locale.GetCountryCode(country); // Put the country names for the locale into the cache. - localized_country_names_cache_.Put(locale_name, std::move(result)); + localized_country_names_cache_.Put(locale_name, + std::move(country_names_for_locale)); return result; } diff --git a/chromium/components/autofill/core/browser/geo/country_names_unittest.cc b/chromium/components/autofill/core/browser/geo/country_names_unittest.cc index 46481c05e4b..2f971cb599c 100644 --- a/chromium/components/autofill/core/browser/geo/country_names_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/country_names_unittest.cc @@ -72,6 +72,9 @@ TEST(CountryNamesTest, GetCountryCode_EnUsFallback) { TEST(CountryNamesTest, GetCountryCodeForLocalizedCountryName) { // Initialize with the default locale. TestCountryNames names("en_US"); + EXPECT_EQ("AM", names.GetCountryCodeForLocalizedCountryName( + ASCIIToUTF16("Armenien"), "de")); + // Check that there is no cache by requesting the same result twice. EXPECT_EQ("AM", names.GetCountryCodeForLocalizedCountryName( ASCIIToUTF16("Armenien"), "de")); EXPECT_EQ("AZ", names.GetCountryCodeForLocalizedCountryName( diff --git a/chromium/components/autofill/core/browser/metrics/form_event_logger_base.cc b/chromium/components/autofill/core/browser/metrics/form_event_logger_base.cc index 3aa2b9d378a..4b42968d295 100644 --- a/chromium/components/autofill/core/browser/metrics/form_event_logger_base.cc +++ b/chromium/components/autofill/core/browser/metrics/form_event_logger_base.cc @@ -97,6 +97,8 @@ void FormEventLoggerBase::OnDidShowSuggestions( OnSuggestionsShownOnce(); } + has_logged_autocomplete_off_ |= field.autocomplete_attribute == "off"; + RecordShowSuggestions(); } @@ -272,6 +274,11 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { has_logged_suggestion_filled_); key_metrics_rows << Tr{} << "FillingAcceptance" << has_logged_suggestion_filled_; + UmaHistogramBoolean( + base::StrCat({"Autofill.Autocomplete.", + (has_logged_autocomplete_off_ ? "Off" : "NotOff"), + ".FillingAcceptance.", form_type_name_.c_str()}), + has_logged_suggestion_filled_); } if (has_logged_suggestion_filled_) { // Whether a filled form and submitted form required no fixes to filled diff --git a/chromium/components/autofill/core/browser/metrics/form_event_logger_base.h b/chromium/components/autofill/core/browser/metrics/form_event_logger_base.h index 5adfd9446d3..130c04e0e90 100644 --- a/chromium/components/autofill/core/browser/metrics/form_event_logger_base.h +++ b/chromium/components/autofill/core/browser/metrics/form_event_logger_base.h @@ -13,7 +13,6 @@ #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/common/form_field_data.h" -#include "components/autofill/core/common/signatures.h" namespace autofill { @@ -111,6 +110,7 @@ class FormEventLoggerBase { bool has_logged_user_hide_suggestions_ = false; bool has_logged_suggestions_shown_ = false; bool has_logged_suggestion_filled_ = false; + bool has_logged_autocomplete_off_ = false; bool has_logged_will_submit_ = false; bool has_logged_submitted_ = false; bool logged_suggestion_filled_was_server_data_ = false; diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc new file mode 100644 index 00000000000..127700ebd5c --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/payments/autofill_offer_manager.h" + +namespace autofill { + +AutofillOfferManager::AutofillOfferManager() = default; + +AutofillOfferManager::~AutofillOfferManager() = default; + +} // namespace autofill \ No newline at end of file diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h new file mode 100644 index 00000000000..ce93163cb9f --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h @@ -0,0 +1,42 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_MANAGER_H_ + +#include +#include + +#include "base/strings/string16.h" + +namespace autofill { + +struct AutofillOfferData { + AutofillOfferData(); + ~AutofillOfferData(); + // The description of this offer. + base::string16 description; + // The name of this offer. + base::string16 name; + // The unique server id of this offer. + std::string offer_id; + // The ids of the cards this offer can be applied to. + std::vector eligible_card_id; + // The merchant URL where this offer can be redeemed. + std::vector merchant_domain; +}; + +// Manages all Autofill related offers. One per frame; owned by the +// AutofillManager. +class AutofillOfferManager { + public: + AutofillOfferManager(); + virtual ~AutofillOfferManager(); + AutofillOfferManager(const AutofillOfferManager&) = delete; + AutofillOfferManager& operator=(const AutofillOfferManager&) = delete; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_MANAGER_H_ \ No newline at end of file diff --git a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc index 762d557835b..8fdc3912d20 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc @@ -47,7 +47,7 @@ AutofillSaveCardInfoBarDelegateMobile::AutofillSaveCardInfoBarDelegateMobile( pref_service_(pref_service), had_user_interaction_(false), issuer_icon_id_(CreditCard::IconResourceId(card.network())), - card_label_(card.NetworkAndLastFourDigits()), + card_label_(card.CardIdentifierStringForAutofillDisplay()), card_sub_label_(card.AbbreviatedExpirationDateForDisplay(false)), card_last_four_digits_(card.LastFourDigits()), cardholder_name_(card.GetRawInfo(CREDIT_CARD_NAME_FULL)), @@ -72,6 +72,15 @@ AutofillSaveCardInfoBarDelegateMobile:: } } +// static +AutofillSaveCardInfoBarDelegateMobile* +AutofillSaveCardInfoBarDelegateMobile::FromInfobarDelegate( + infobars::InfoBarDelegate* delegate) { + return delegate->GetIdentifier() == AUTOFILL_CC_INFOBAR_DELEGATE_MOBILE + ? static_cast(delegate) + : nullptr; +} + void AutofillSaveCardInfoBarDelegateMobile::OnLegalMessageLinkClicked( GURL url) { infobar()->owner()->OpenURL(url, WindowOpenDisposition::NEW_FOREGROUND_TAB); diff --git a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h index 64721013e55..f1f9011b1e2 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h +++ b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h @@ -39,6 +39,11 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { ~AutofillSaveCardInfoBarDelegateMobile() override; + // Returns |delegate| as an AutofillSaveCardInfoBarDelegateMobile, or nullptr + // if it is of another type. + static AutofillSaveCardInfoBarDelegateMobile* FromInfobarDelegate( + infobars::InfoBarDelegate* delegate); + bool upload() const { return upload_; } int issuer_icon_id() const { return issuer_icon_id_; } const base::string16& card_label() const { return card_label_; } @@ -58,7 +63,7 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { } // Called when a link in the legal message text was clicked. - void OnLegalMessageLinkClicked(GURL url); + virtual void OnLegalMessageLinkClicked(GURL url); // Google Pay branding is enabled with a flag and only for cards upstreamed // to Google. @@ -82,9 +87,9 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { // Updates and then saves the card using |cardholder_name|, // |expiration_date_month| and |expiration_date_year|, which were provided // as part of the iOS save card Infobar dialog. - bool UpdateAndAccept(base::string16 cardholder_name, - base::string16 expiration_date_month, - base::string16 expiration_date_year); + virtual bool UpdateAndAccept(base::string16 cardholder_name, + base::string16 expiration_date_month, + base::string16 expiration_date_year); #endif // defined(OS_IOS) private: diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc index 1d306acd09c..3a1c0a8f5bf 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc @@ -70,11 +70,6 @@ CreditCardAccessManager::CreditCardAccessManager( base::WaitableEvent::InitialState::NOT_SIGNALED), can_fetch_unmask_details_(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::SIGNALED) { -#if !defined(OS_IOS) - // This is to initialize StrikeDatabase is if it hasn't been already, so that - // its cache would be loaded and ready to use when the first CCAM is created. - client_->GetStrikeDatabase(); -#endif } CreditCardAccessManager::~CreditCardAccessManager() {} @@ -154,7 +149,7 @@ bool CreditCardAccessManager::GetDeletionConfirmationText( return false; if (title) - title->assign(card->NetworkAndLastFourDigits()); + title->assign(card->CardIdentifierStringForAutofillDisplay()); if (body) { body->assign(l10n_util::GetStringUTF16( IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY)); diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc index b507de35144..3c13e1d3de6 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc @@ -460,7 +460,7 @@ TEST_F(CreditCardAccessManagerTest, LocalCardGetDeletionConfirmationText) { card, &title, &body)); // |title| and |body| should be updated appropriately. - EXPECT_EQ(title, card->NetworkAndLastFourDigits()); + EXPECT_EQ(title, card->CardIdentifierStringForAutofillDisplay()); EXPECT_EQ(body, l10n_util::GetStringUTF16( IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY)); diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc index 1260d1caf9a..6400469d8c9 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc @@ -75,6 +75,9 @@ void CreditCardFIDOAuthenticator::Authenticate( requester_ = requester; form_parsed_timestamp_ = form_parsed_timestamp; + // Cancel any previous pending WebAuthn requests. + authenticator()->Cancel(); + if (card_ && IsValidRequestOptions(request_options.Clone())) { current_flow_ = AUTHENTICATION_FLOW; GetAssertion(ParseRequestOptions(std::move(request_options))); @@ -85,6 +88,9 @@ void CreditCardFIDOAuthenticator::Authenticate( void CreditCardFIDOAuthenticator::Register(std::string card_authorization_token, base::Value creation_options) { + // Cancel any previous pending WebAuthn requests. + authenticator()->Cancel(); + // If |creation_options| is set, then must enroll a new credential. Otherwise // directly send request to payments for opting in. card_authorization_token_ = card_authorization_token; @@ -105,6 +111,10 @@ void CreditCardFIDOAuthenticator::Authorize( base::Value request_options) { requester_ = requester; card_authorization_token_ = card_authorization_token; + + // Cancel any previous pending WebAuthn requests. + authenticator()->Cancel(); + if (IsValidRequestOptions(request_options)) { // If user is already opted-in, then a new card is trying to be // authorized. Otherwise, a user with a credential on file is trying to @@ -116,6 +126,9 @@ void CreditCardFIDOAuthenticator::Authorize( } void CreditCardFIDOAuthenticator::OptOut() { + // Cancel any previous pending WebAuthn requests. + authenticator()->Cancel(); + current_flow_ = OPT_OUT_FLOW; card_authorization_token_ = std::string(); OptChange(); @@ -181,6 +194,8 @@ UserOptInIntention CreditCardFIDOAuthenticator::GetUserOptInIntention( } void CreditCardFIDOAuthenticator::CancelVerification() { + authenticator()->Cancel(); + current_flow_ = NONE_FLOW; // Full card request may not exist when this function is called. The full card // request is created in OnDidGetAssertion() but the flow can be cancelled @@ -193,6 +208,10 @@ void CreditCardFIDOAuthenticator::CancelVerification() { void CreditCardFIDOAuthenticator::OnWebauthnOfferDialogRequested( std::string card_authorization_token) { card_authorization_token_ = card_authorization_token; + + // Cancel any previous pending WebAuthn requests. + authenticator()->Cancel(); + AutofillMetrics::LogWebauthnOptInPromoShown( /*is_checkout_flow=*/!card_authorization_token_.empty()); @@ -651,7 +670,7 @@ base::Value CreditCardFIDOAuthenticator::ParseAssertionResponse( response.SetKey("credential_id", BytesToBase64(assertion_response->info->raw_id)); response.SetKey("authenticator_data", - BytesToBase64(assertion_response->authenticator_data)); + BytesToBase64(assertion_response->info->authenticator_data)); response.SetKey("client_data", BytesToBase64(assertion_response->info->client_data_json)); response.SetKey("signature", BytesToBase64(assertion_response->signature)); @@ -733,6 +752,7 @@ void CreditCardFIDOAuthenticator::LogWebauthnResult( return; } + // TODO(crbug.com/949269): Add metrics for revoked pending WebAuthn requests. AutofillMetrics::WebauthnResultMetric metric; switch (status) { case AuthenticatorStatus::SUCCESS: diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc index 617c2f22a46..e9e8c203a20 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc @@ -86,9 +86,6 @@ CreditCardSaveManager::CreditCardSaveManager( payments_client_(payments_client), app_locale_(app_locale), personal_data_manager_(personal_data_manager) { - // This is to initialize StrikeDatabase is if it hasn't been already, so that - // its cache would be loaded and ready to use when the first CCSM is created. - client_->GetStrikeDatabase(); } CreditCardSaveManager::~CreditCardSaveManager() {} @@ -255,8 +252,6 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave( (should_request_expiration_date_from_user_ && personal_data_manager_->GetSyncSigninState() == AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled)) { - DCHECK(base::FeatureList::IsEnabled( - features::kAutofillUpstreamEditableExpirationDate)); LogCardUploadDecisions(upload_decision_metrics_); pending_upload_request_origin_ = url::Origin(); return; @@ -731,43 +726,39 @@ int CreditCardSaveManager::GetDetectedValues() const { detected_values |= DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT; } - if (base::FeatureList::IsEnabled( - features::kAutofillUpstreamEditableExpirationDate)) { - // If expiration date month or expiration year are missing, signal that - // expiration date will be explicitly requested in the offer-to-save bubble. - if (!upload_request_.card - .GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_) - .empty()) { - detected_values |= DetectedValue::CARD_EXPIRATION_MONTH; - } - if (!(upload_request_.card - .GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_) - .empty())) { - detected_values |= DetectedValue::CARD_EXPIRATION_YEAR; - } + // If expiration date month or expiration year are missing, signal that + // expiration date will be explicitly requested in the offer-to-save bubble. + if (!upload_request_.card + .GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_) + .empty()) { + detected_values |= DetectedValue::CARD_EXPIRATION_MONTH; + } + if (!(upload_request_.card + .GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_) + .empty())) { + detected_values |= DetectedValue::CARD_EXPIRATION_YEAR; + } - // Set |USER_PROVIDED_EXPIRATION_DATE| if expiration date is detected as - // expired or missing. - if (detected_values & DetectedValue::CARD_EXPIRATION_MONTH && - detected_values & DetectedValue::CARD_EXPIRATION_YEAR) { - int month_value = 0, year_value = 0; - bool parsable = - base::StringToInt( - upload_request_.card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), - app_locale_), - &month_value) && - base::StringToInt( - upload_request_.card.GetInfo( - AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_), - &year_value); - DCHECK(parsable); - if (!IsValidCreditCardExpirationDate(year_value, month_value, - AutofillClock::Now())) { - detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE; - } - } else { + // Set |USER_PROVIDED_EXPIRATION_DATE| if expiration date is detected as + // expired or missing. + if (detected_values & DetectedValue::CARD_EXPIRATION_MONTH && + detected_values & DetectedValue::CARD_EXPIRATION_YEAR) { + int month_value = 0, year_value = 0; + bool parsable = + base::StringToInt(upload_request_.card.GetInfo( + AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_), + &month_value) && + base::StringToInt( + upload_request_.card.GetInfo( + AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_), + &year_value); + DCHECK(parsable); + if (!IsValidCreditCardExpirationDate(year_value, month_value, + AutofillClock::Now())) { detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE; } + } else { + detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE; } // If cardholder name is conflicting/missing and the user does NOT have a diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc index 184326bc942..8798b034fb0 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc @@ -2619,9 +2619,6 @@ TEST_F( TEST_F( CreditCardSaveManagerTest, UploadCreditCard_ShouldRequestExpirationDate_ResetBetweenConsecutiveSaves) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // 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; @@ -2670,9 +2667,6 @@ TEST_F( TEST_F( CreditCardSaveManagerTest, UploadCreditCard_WalletSyncTransportEnabled_ShouldNotRequestExpirationDate) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // Wallet Sync Transport is enabled. personal_data_.SetSyncAndSignInState( AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled); @@ -2709,9 +2703,6 @@ TEST_F( TEST_F( CreditCardSaveManagerTest, UploadCreditCard_WalletSyncTransportNotEnabled_ShouldRequestExpirationDate) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // Wallet Sync Transport is not enabled. personal_data_.SetSyncAndSignInState( AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled); @@ -2750,9 +2741,6 @@ TEST_F( TEST_F( CreditCardSaveManagerTest, UploadCreditCard_DoNotRequestExpirationDateIfMissingNameAndExpirationDate) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // 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; @@ -2781,36 +2769,6 @@ TEST_F( EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); } -TEST_F(CreditCardSaveManagerTest, - UploadCreditCard_DoNotRequestExpirationDate_EditableExpDateOff) { - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillUpstreamEditableExpirationDate); - // 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(1, address_form)); - ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form); - FormSubmitted(address_form); - - // Set up our credit card form data. - FormData credit_card_form; - CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); - FormsSeen(std::vector(1, credit_card_form)); - - // Edit the data, and submit. - credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); - credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(""); - credit_card_form.fields[3].value = ASCIIToUTF16(""); - credit_card_form.fields[4].value = ASCIIToUTF16("123"); - - base::HistogramTester histogram_tester; - FormSubmitted(credit_card_form); - EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); - EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded()); -} - TEST_F(CreditCardSaveManagerTest, UploadCreditCard_RequestExpirationDateViaExpDateFixFlow) { #if defined(OS_IOS) @@ -2824,8 +2782,6 @@ TEST_F(CreditCardSaveManagerTest, } #endif - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // 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; @@ -2878,8 +2834,6 @@ TEST_F(CreditCardSaveManagerTest, } #endif - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // 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; @@ -2932,8 +2886,6 @@ TEST_F(CreditCardSaveManagerTest, } #endif - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // 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; @@ -2986,8 +2938,6 @@ TEST_F(CreditCardSaveManagerTest, } #endif - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // 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; @@ -3041,8 +2991,6 @@ TEST_F( } #endif - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillUpstreamEditableExpirationDate); // 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; diff --git a/chromium/components/autofill/core/browser/payments/legal_message_line_unittest.cc b/chromium/components/autofill/core/browser/payments/legal_message_line_unittest.cc index 257e22c22b2..1234667a475 100644 --- a/chromium/components/autofill/core/browser/payments/legal_message_line_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/legal_message_line_unittest.cc @@ -10,6 +10,7 @@ #include "base/check_op.h" #include "base/json/json_reader.h" +#include "base/logging.h" #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc index 742c4b8c80a..ec61627e313 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc @@ -41,9 +41,6 @@ LocalCardMigrationManager::LocalCardMigrationManager( payments_client_(payments_client), app_locale_(app_locale), personal_data_manager_(personal_data_manager) { - // This is to initialize StrikeDatabase is if it hasn't been already, so that - // its cache would be loaded and ready to use when the first LCMM is created. - client_->GetStrikeDatabase(); } LocalCardMigrationManager::~LocalCardMigrationManager() {} diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc index 10db6a376e2..f340bc58f33 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client.cc @@ -218,6 +218,12 @@ base::Value BuildCreditCardDictionary(const CreditCard& credit_card, SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale, "cardholder_name", card); + if (base::FeatureList::IsEnabled( + features::kAutofillEnableCardNicknameUpstream) && + credit_card.HasValidNickname()) { + card.SetKey("nickname", base::Value(credit_card.nickname())); + } + card.SetKey("encrypted_pan", base::Value("__param:" + pan_field_name)); return card; } @@ -819,6 +825,13 @@ class UploadCardRequest : public PaymentsRequest { if (base::StringToInt(exp_year, &value)) request_dict.SetKey("expiration_year", base::Value(value)); + if (base::FeatureList::IsEnabled( + features::kAutofillEnableCardNicknameUpstream) && + request_details_.card.HasValidNickname()) { + request_dict.SetKey("nickname", + base::Value(request_details_.card.nickname())); + } + SetActiveExperiments(request_details_.active_experiments, request_dict); const base::string16 pan = request_details_.card.GetInfo( diff --git a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc index ac12ca2ff90..821f70b48fc 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc @@ -235,12 +235,16 @@ class PaymentsClientTest : public testing::Test { // Issue an UploadCard request. This requires an OAuth token before starting // the request. - void StartUploading(bool include_cvc) { + void StartUploading(bool include_cvc, bool include_nickname = false) { PaymentsClient::UploadRequestDetails request_details; request_details.billing_customer_number = 111222333444; request_details.card = test::GetCreditCard(); if (include_cvc) request_details.cvc = base::ASCIIToUTF16("123"); + if (include_nickname) { + upstream_nickname_ = base::ASCIIToUTF16("grocery"); + request_details.card.SetNickname(upstream_nickname_); + } request_details.context_token = base::ASCIIToUTF16("context token"); request_details.risk_data = "some risk data"; request_details.app_locale = "language-LOCALE"; @@ -251,7 +255,8 @@ class PaymentsClientTest : public testing::Test { } #if !defined(OS_ANDROID) && !defined(OS_IOS) - void StartMigrating(bool has_cardholder_name) { + void StartMigrating(bool has_cardholder_name, + bool set_nickname_for_first_card = false) { PaymentsClient::MigrationRequestDetails request_details; request_details.context_token = base::ASCIIToUTF16("context token"); request_details.risk_data = "some risk data"; @@ -259,6 +264,8 @@ class PaymentsClientTest : public testing::Test { migratable_credit_cards_.clear(); CreditCard card1 = test::GetCreditCard(); + if (set_nickname_for_first_card) + card1.SetNickname(base::ASCIIToUTF16("grocery")); CreditCard card2 = test::GetCreditCard2(); if (!has_cardholder_name) { card1.SetRawInfo(CREDIT_CARD_NAME_FULL, base::UTF8ToUTF16("")); @@ -313,6 +320,8 @@ class PaymentsClientTest : public testing::Test { // A list of card BIN ranges supported by Google Payments, returned from a // GetDetails upload save preflight call. std::vector> supported_card_bin_ranges_; + // The nickname name in the UploadRequest that was supposed to be saved. + base::string16 upstream_nickname_; #if !defined(OS_ANDROID) && !defined(OS_IOS) // Credit cards to be upload saved during a local credit card migration call. @@ -895,6 +904,41 @@ TEST_F(PaymentsClientTest, UploadDoesNotIncludeCvcInRequestIfNotProvided) { EXPECT_TRUE(GetUploadData().find("&s7e_13_cvc=") == std::string::npos); } +TEST_F(PaymentsClientTest, UploadIncludesCardNickname) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableCardNicknameUpstream); + + StartUploading(/*include_cvc=*/true, /*include_nickname=*/true); + IssueOAuthToken(); + + // Card nickname was set. + EXPECT_TRUE(GetUploadData().find("nickname") != std::string::npos); + EXPECT_TRUE(GetUploadData().find(base::UTF16ToUTF8(upstream_nickname_)) != + std::string::npos); +} + +TEST_F(PaymentsClientTest, UploadDoesNotIncludeCardNicknameFlagDisabled) { + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillEnableCardNicknameUpstream); + + StartUploading(/*include_cvc=*/true, /*include_nickname=*/true); + IssueOAuthToken(); + + // Card nickname was not set. + EXPECT_FALSE(GetUploadData().find("nickname") != std::string::npos); +} + +TEST_F(PaymentsClientTest, UploadDoesNotIncludeCardNicknameEmptyNickname) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableCardNicknameUpstream); + + StartUploading(/*include_cvc=*/true, /*include_nickname=*/false); + IssueOAuthToken(); + + // Card nickname was not set. + EXPECT_FALSE(GetUploadData().find("nickname") != std::string::npos); +} + TEST_F(PaymentsClientTest, UnmaskMissingPan) { StartUnmasking(CardUnmaskOptions()); ReturnResponse(net::HTTP_OK, "{}"); @@ -1087,6 +1131,37 @@ TEST_F(PaymentsClientTest, EXPECT_TRUE(GetUploadData().find("full_sync_enabled") != std::string::npos); } +TEST_F(PaymentsClientTest, MigrationRequestIncludesCardNickname) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableCardNicknameUpstream); + + StartMigrating(/*has_cardholder_name=*/true, + /*set_nickname_to_first_card=*/true); + IssueOAuthToken(); + + // Nickname was set for the first card. + std::size_t pos = GetUploadData().find("nickname"); + EXPECT_TRUE(pos != std::string::npos); + EXPECT_TRUE(GetUploadData().find(base::UTF16ToUTF8( + migratable_credit_cards_[0].credit_card().nickname())) != + std::string::npos); + + // Nickname was not set for the second card. + EXPECT_FALSE(GetUploadData().find("nickname", pos + 1) != std::string::npos); +} + +TEST_F(PaymentsClientTest, MigrationRequestExcludesCardNicknameIfFlagDisabled) { + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillEnableCardNicknameUpstream); + + StartMigrating(/*has_cardholder_name=*/true, + /*set_nickname_for_first_card=*/true); + IssueOAuthToken(); + + // Nickname was not set. + EXPECT_FALSE(GetUploadData().find("nickname") != std::string::npos); +} + TEST_F(PaymentsClientTest, MigrationSuccessWithSaveResult) { StartMigrating(/*has_cardholder_name=*/true); IssueOAuthToken(); diff --git a/chromium/components/autofill/core/browser/payments/test_internal_authenticator.cc b/chromium/components/autofill/core/browser/payments/test_internal_authenticator.cc new file mode 100644 index 00000000000..68a073b8114 --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/test_internal_authenticator.cc @@ -0,0 +1,15 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/payments/test_internal_authenticator.h" + +namespace autofill { + +void TestInternalAuthenticator::IsUserVerifyingPlatformAuthenticatorAvailable( + blink::mojom::Authenticator:: + IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) { + std::move(callback).Run(false); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/test_internal_authenticator.h b/chromium/components/autofill/core/browser/payments/test_internal_authenticator.h index ec6ec8902ba..aecafe23605 100644 --- a/chromium/components/autofill/core/browser/payments/test_internal_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/test_internal_authenticator.h @@ -28,7 +28,7 @@ class TestInternalAuthenticator : public InternalAuthenticator { void IsUserVerifyingPlatformAuthenticatorAvailable( blink::mojom::Authenticator:: IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) - override {} + override; void Cancel() override {} }; diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index e45d579f73f..aa552996904 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -445,11 +445,6 @@ void PersonalDataManager::OnWebDataServiceRequestDone( ReceiveLoadedDbValues(h, result.get(), &pending_server_creditcards_query_, &server_credit_cards_); - - // If the user has a saved unmasked server card and the experiment is - // disabled, force mask all cards back to the unsaved state. - if (!OfferStoreUnmaskedCards(is_off_the_record_)) - ResetFullServerCards(); } break; case AUTOFILL_CLOUDTOKEN_RESULT: @@ -2067,12 +2062,17 @@ std::vector PersonalDataManager::GetSuggestionsForCards( ? Suggestion::PREFIX_MATCH : Suggestion::SUBSTRING_MATCH; + // Get the nickname for the card suggestion, which may not be the same as + // the card's nickname if there are duplicates of the card on file. + base::string16 suggestion_nickname = + GetDisplayNicknameForCreditCard(*credit_card); + // If the value is the card number, the label is the expiration date. // Otherwise the label is the card number, or if that is empty the // cardholder name. The label should never repeat the value. if (type.GetStorableType() == CREDIT_CARD_NUMBER) { - suggestion->value = - credit_card->CardIdentifierStringForAutofillDisplay(); + suggestion->value = credit_card->CardIdentifierStringForAutofillDisplay( + suggestion_nickname); #if defined(OS_ANDROID) || defined(OS_IOS) suggestion->label = credit_card->GetInfo( @@ -2082,9 +2082,10 @@ std::vector PersonalDataManager::GetSuggestionsForCards( #endif // defined(OS_ANDROID) || defined(OS_IOS) } else if (credit_card->number().empty()) { - // TODO(crbug/1059087): Update suggestion label with nickname for - // empty-number local cards when nickname is supported for local card. - if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) { + DCHECK_EQ(credit_card->record_type(), CreditCard::LOCAL_CARD); + if (credit_card->HasValidNickname()) { + suggestion->label = credit_card->nickname(); + } else if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) { suggestion->label = credit_card->GetInfo( AutofillType(CREDIT_CARD_NAME_FULL), app_locale_); } @@ -2096,7 +2097,8 @@ std::vector PersonalDataManager::GetSuggestionsForCards( suggestion->label = base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) ? credit_card->ObfuscatedLastFourDigits() - : credit_card->CardIdentifierStringForAutofillDisplay(); + : credit_card->CardIdentifierStringForAutofillDisplay( + suggestion_nickname); #elif defined(OS_IOS) // E.g. "••••1234"". suggestion->label = credit_card->ObfuscatedLastFourDigits(); @@ -2651,4 +2653,27 @@ void PersonalDataManager::MigrateUserOptedInWalletSyncTransportIfNeeded() { /*opted_in=*/true); } +base::string16 PersonalDataManager::GetDisplayNicknameForCreditCard( + const CreditCard& card) const { + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableCardNicknameManagement)) { + return base::string16(); + } + + // Always prefer a local nickname if available. + if (card.HasValidNickname() && card.record_type() == CreditCard::LOCAL_CARD) + return card.nickname(); + // Either the card a) has no nickname or b) is a server card and we would + // prefer to use the nickname of a local card. + std::vector candidates = GetCreditCards(); + for (CreditCard* candidate : candidates) { + if (candidate->guid() != card.guid() && candidate->HasSameNumberAs(card) && + candidate->HasValidNickname()) { + return candidate->nickname(); + } + } + // Fall back to nickname of |card|, which may be empty. + return card.nickname(); +} + } // 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 1e787a4d3c0..42d68aa0a67 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -752,6 +752,12 @@ class PersonalDataManager : public KeyedService, // migrating from using email to Gaia ID as th account identifier. void MigrateUserOptedInWalletSyncTransportIfNeeded(); + // Return a nickname for the |card| to display. This is generally the nickname + // stored in |card|, unless |card| exists as a local and a server copy. In + // this case, we prefer the nickname of the local if it is defined. If only + // one copy has a nickname, take that. + base::string16 GetDisplayNicknameForCreditCard(const CreditCard& card) const; + // Stores the |app_locale| supplied on construction. const std::string app_locale_; 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 766044927d5..10d3b58f676 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -1058,6 +1058,7 @@ TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) { CreditCard credit_card0(base::GenerateGUID(), test::kEmptyOrigin); test::SetCreditCardInfo(&credit_card0, "John Dillinger", "4234567890123456" /* Visa */, "01", "2999", "1"); + credit_card0.SetNickname(base::ASCIIToUTF16("card zero")); CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); test::SetCreditCardInfo(&credit_card1, "Bonnie Parker", @@ -1068,6 +1069,7 @@ TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) { test::SetCreditCardInfo(&credit_card2, "Clyde Barrow", "378282246310005" /* American Express */, "04", "2999", "1"); + credit_card2.SetNickname(base::ASCIIToUTF16("card two")); // Add two test credit cards to the database. personal_data_->AddCreditCard(credit_card0); @@ -1082,6 +1084,7 @@ TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) { // Update, remove, and add. credit_card0.SetRawInfo(CREDIT_CARD_NAME_FULL, base::ASCIIToUTF16("Joe")); + credit_card0.SetNickname(base::ASCIIToUTF16("new card zero")); personal_data_->UpdateCreditCard(credit_card0); RemoveByGUIDFromPersonalDataManager(credit_card1.guid()); personal_data_->AddCreditCard(credit_card2); @@ -1433,136 +1436,6 @@ TEST_F(PersonalDataManagerTest, KeepExistingLocalDataOnSignIn) { EXPECT_EQ(0, local_card.Compare(*personal_data_->GetCreditCards()[0])); } -// Makes sure that full cards are re-masked when full PAN storage is off. -TEST_F(PersonalDataManagerTest, RefuseToStoreFullCard) { -// On Linux this should be disabled automatically. Elsewhere, only if the -// flag is passed. -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableOfferStoreUnmaskedWalletCards)); -#else - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kDisableOfferStoreUnmaskedWalletCards); -#endif - - std::vector server_cards; - server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789")); - test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", - "378282246310005" /* American Express */, "04", - "2999", "1"); - SetServerCards(server_cards); - personal_data_->Refresh(); - - WaitForOnPersonalDataChanged(); - - ASSERT_EQ(1U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, - personal_data_->GetCreditCards()[0]->record_type()); -} - -// Makes sure that full cards are only added as masked card when full PAN -// storage is disabled. -TEST_F(PersonalDataManagerTest, AddFullCardAsMaskedCard) { -// On Linux this should be disabled automatically. Elsewhere, only if the -// flag is passed. -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableOfferStoreUnmaskedWalletCards)); -#else - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kDisableOfferStoreUnmaskedWalletCards); -#endif - - CreditCard server_card(CreditCard::FULL_SERVER_CARD, "c789"); - test::SetCreditCardInfo(&server_card, "Clyde Barrow", - "378282246310005" /* American Express */, "04", - "2999", "1"); - - personal_data_->AddFullServerCreditCard(server_card); - - WaitForOnPersonalDataChanged(); - - ASSERT_EQ(1U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, - personal_data_->GetCreditCards()[0]->record_type()); -} - -TEST_F(PersonalDataManagerTest, OfferStoreUnmaskedCards) { -#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX) || \ - defined(OS_IOS) || defined(OS_ANDROID) || defined(OS_FUCHSIA) - bool should_offer = true; -#elif defined(OS_LINUX) - bool should_offer = false; -#endif - EXPECT_EQ(should_offer, OfferStoreUnmaskedCards(/*is_off_the_record=*/false)); -} - -// Tests that OfferStoreUnmaskedCards always returns false if the user is off -// the record. -TEST_F(PersonalDataManagerTest, OfferStoreUnmaskedCards_OffTheRecord) { - EXPECT_EQ(false, OfferStoreUnmaskedCards(/*is_off_the_record=*/true)); -} - -// Tests that UpdateServerCreditCard can be used to mask or unmask server cards. -TEST_F(PersonalDataManagerTest, UpdateServerCreditCards) { - EnableWalletCardImport(); - - std::vector server_cards; - server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123")); - test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", - "3456" /* Visa */, "01", "2999", "1"); - server_cards.back().SetNetworkForMaskedCard(kVisaCard); - - server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456")); - test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker", - "5100" /* Mastercard */, "12", "2999", "1"); - server_cards.back().SetNetworkForMaskedCard(kMasterCard); - - server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789")); - test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", - "378282246310005" /* American Express */, "04", - "2999", "1"); - - SetServerCards(server_cards); - personal_data_->Refresh(); - - WaitForOnPersonalDataChanged(); - - ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); - if (!OfferStoreUnmaskedCards(/*is_off_the_record=*/false)) { - for (CreditCard* card : personal_data_->GetCreditCards()) { - EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type()); - } - // The rest of this test doesn't work if we're force-masking all unmasked - // cards. - return; - } - - // The GUIDs will be different, so just compare the data. - for (size_t i = 0; i < 3; ++i) - EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); - - CreditCard* unmasked_card = &server_cards.front(); - unmasked_card->set_record_type(CreditCard::FULL_SERVER_CARD); - unmasked_card->SetNumber(base::ASCIIToUTF16("4234567890123456")); - personal_data_->UpdateServerCreditCard(*unmasked_card); - - WaitForOnPersonalDataChanged(); - - for (size_t i = 0; i < 3; ++i) - EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); - - CreditCard* remasked_card = &server_cards.back(); - remasked_card->set_record_type(CreditCard::MASKED_SERVER_CARD); - remasked_card->SetNumber(base::ASCIIToUTF16("0005")); - personal_data_->UpdateServerCreditCard(*remasked_card); - - WaitForOnPersonalDataChanged(); - - for (size_t i = 0; i < 3; ++i) - EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); -} - TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) { AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile0, "Marion", "Mitchell", "Morrison", @@ -3345,7 +3218,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_LocalCardsRanking) { std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(3U, suggestions.size()); @@ -3394,7 +3267,7 @@ TEST_F(PersonalDataManagerTest, std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(5U, suggestions.size()); @@ -3448,7 +3321,7 @@ TEST_F(PersonalDataManagerTest, std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(0U, suggestions.size()); } @@ -3498,7 +3371,7 @@ TEST_F(PersonalDataManagerTest, std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(0U, suggestions.size()); } @@ -3563,7 +3436,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ExpiredCards) { std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /* include_server_cards= */ true); ASSERT_EQ(3U, suggestions.size()); @@ -3691,7 +3564,8 @@ TEST_F(PersonalDataManagerTest, // Test that a card that doesn't have a number is not shown in the suggestions // when querying credit cards by their number. -TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) { +TEST_F(PersonalDataManagerTest, + GetCreditCardSuggestions_NumberMissing_QueryNumberField) { // Create one normal credit card and one credit card with the number missing. ASSERT_EQ(0U, personal_data_->GetCreditCards().size()); @@ -3723,7 +3597,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) { std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NUMBER), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(1U, suggestions.size()); EXPECT_EQ(base::UTF8ToUTF16(std::string("Amex ") + @@ -3737,6 +3611,36 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) { #endif // defined (OS_ANDROID) || defined(OS_IOS) } +// Test that a card that doesn't have a number is shown in the suggestion list +// with nickname if a non-number field is queried. +TEST_F(PersonalDataManagerTest, + GetCreditCardSuggestions_NumberMissing_QueryNonNumberField) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableSurfacingServerCardNickname); + ASSERT_EQ(0U, personal_data_->GetCreditCards().size()); + + CreditCard credit_card("1141084B-72D7-4B73-90CF-3D6AC154673B", + test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card, "John Dillinger", "", "01", "2999", + "1"); + credit_card.SetNickname(base::UTF8ToUTF16("nickname")); + personal_data_->AddCreditCard(credit_card); + + // Make sure everything is set up correctly. + WaitForOnPersonalDataChanged(); + ASSERT_EQ(1U, personal_data_->GetCreditCards().size()); + + // Ensures the suggestion label is the card's nickname. + std::vector suggestions = + personal_data_->GetCreditCardSuggestions( + AutofillType(CREDIT_CARD_NAME_FULL), + /*field_contents=*/base::string16(), + /*include_server_cards=*/true); + ASSERT_EQ(1U, suggestions.size()); + EXPECT_EQ(base::UTF8ToUTF16("nickname"), suggestions[0].label); +} + // Tests the suggestions of duplicate local and server credit cards. TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { EnableWalletCardImport(); @@ -3777,7 +3681,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(3U, suggestions.size()); EXPECT_EQ(base::ASCIIToUTF16("John Dillinger"), suggestions[0].value); @@ -3785,7 +3689,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { EXPECT_EQ(base::ASCIIToUTF16("Bonnie Parker"), suggestions[2].value); suggestions = personal_data_->GetCreditCardSuggestions( - AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16(), + AutofillType(CREDIT_CARD_NUMBER), /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(3U, suggestions.size()); EXPECT_EQ(base::UTF8ToUTF16(std::string("Visa ") + @@ -3824,7 +3728,7 @@ TEST_F(PersonalDataManagerTest, std::vector suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(3U, suggestions.size()); @@ -3839,7 +3743,7 @@ TEST_F(PersonalDataManagerTest, suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NAME_FULL), - /* field_contents= */ base::string16(), /*include_server_cards=*/true); + /*field_contents=*/base::string16(), /*include_server_cards=*/true); ASSERT_EQ(3U, suggestions.size()); } @@ -4036,120 +3940,6 @@ TEST_F(PersonalDataManagerTest, RecordUseOf) { EXPECT_EQ(kArbitraryTime, added_card->modification_date()); } -TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { - EnableWalletCardImport(); - - std::vector server_cards; - server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123")); - test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", - "3456" /* Visa */, "01", "2999", "1"); - server_cards.back().SetNetworkForMaskedCard(kVisaCard); - - server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456")); - test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker", - "4444" /* Mastercard */, "12", "2999", "1"); - server_cards.back().SetNetworkForMaskedCard(kMasterCard); - - server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789")); - test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", - "378282246310005" /* American Express */, "04", - "2999", "1"); - - // Create the test clock and set the time to a specific value. - TestAutofillClock test_clock; - test_clock.SetNow(kArbitraryTime); - - SetServerCards(server_cards); - - // Make sure everything is set up correctly. - personal_data_->Refresh(); - WaitForOnPersonalDataChanged(); - EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); - - if (!OfferStoreUnmaskedCards(/*is_off_the_record=*/false)) { - for (CreditCard* card : personal_data_->GetCreditCards()) { - EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type()); - } - // The rest of this test doesn't work if we're force-masking all unmasked - // cards. - return; - } - - // The GUIDs will be different, so just compare the data. - for (size_t i = 0; i < 3; ++i) - EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); - - CreditCard* unmasked_card = &server_cards.front(); - unmasked_card->set_record_type(CreditCard::FULL_SERVER_CARD); - unmasked_card->SetNumber(base::ASCIIToUTF16("4234567890123456")); - personal_data_->UpdateServerCreditCard(*unmasked_card); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); - - for (size_t i = 0; i < 3; ++i) - EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); - - // For an unmasked card, usage data starts out as 2 because of the unmasking - // which is considered a use. The use date should now be the specified Now() - // time kArbitraryTime. - EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count()); - EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date()); - - EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date()); - - // Having unmasked this card, usage stats should be 2 and - // kArbitraryTime. - EXPECT_EQ(2U, personal_data_->GetCreditCards()[2]->use_count()); - EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[2]->use_date()); - - // Change the Now() value for a second time. - test_clock.SetNow(kSomeLaterTime); - - server_cards.back().set_guid(personal_data_->GetCreditCards()[2]->guid()); - personal_data_->RecordUseOf(server_cards.back()); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count()); - EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date()); - - EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date()); - - // The RecordUseOf call should have incremented the use_count to 3 and set the - // use_date to kSomeLaterTime. - EXPECT_EQ(3U, personal_data_->GetCreditCards()[2]->use_count()); - EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[2]->use_date()); - - // Can record usage stats on masked cards. - server_cards[1].set_guid(personal_data_->GetCreditCards()[1]->guid()); - personal_data_->RecordUseOf(server_cards[1]); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(2U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[1]->use_date()); - - // Change Now()'s return value for a third time. - test_clock.SetNow(kMuchLaterTime); - - // Upgrading to unmasked retains the usage stats (and increments them). - CreditCard* unmasked_card2 = &server_cards[1]; - unmasked_card2->set_record_type(CreditCard::FULL_SERVER_CARD); - unmasked_card2->SetNumber(base::ASCIIToUTF16("5555555555554444")); - personal_data_->UpdateServerCreditCard(*unmasked_card2); - - server_cards[1].set_guid(personal_data_->GetCreditCards()[1]->guid()); - personal_data_->RecordUseOf(server_cards[1]); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(3U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_EQ(kMuchLaterTime, personal_data_->GetCreditCards()[1]->use_date()); -} - TEST_F(PersonalDataManagerTest, ClearAllServerData) { // Add a server card. std::vector server_cards; @@ -6746,21 +6536,6 @@ TEST_F(PersonalDataManagerTest, CreateDataForTest) { } } -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -// Make sure that it's not possible to add full server cards on Linux. -TEST_F(PersonalDataManagerTest, CannotAddFullServerCardOnLinux) { - SetUpThreeCardTypes(); - - // Check that cards were masked and other were untouched. - EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); - std::vector server_cards = - personal_data_->GetServerCreditCards(); - EXPECT_EQ(2U, server_cards.size()); - for (CreditCard* card : server_cards) - EXPECT_TRUE(card->record_type() == CreditCard::MASKED_SERVER_CARD); -} -#endif // #if defined(OS_LINUX) && !defined(OS_CHROMEOS) - // These tests are not applicable on Linux since it does not support full server // cards. #if !defined(OS_LINUX) || defined(OS_CHROMEOS) @@ -7884,11 +7659,11 @@ TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { /////////////////////////////////////////////////////////// // kSignedInAndWalletSyncTransportEnabled /////////////////////////////////////////////////////////// - // Make a non-primary account available with both a refresh token and cookie - // to be in Sync Transport for Wallet mode. + // Make a primary account with no sync consent available to be in Sync + // Transport for Wallet mode. CoreAccountInfo active_info = - identity_test_env_.MakeAccountAvailable(kSyncTransportAccountEmail); - identity_test_env_.SetCookieAccounts({{active_info.email, active_info.gaia}}); + identity_test_env_.MakeUnconsentedPrimaryAccountAvailable( + kSyncTransportAccountEmail); sync_service_.SetAuthenticatedAccountInfo(active_info); sync_service_.SetIsAuthenticatedAccountPrimary(false); sync_service_.SetActiveDataTypes( @@ -7943,12 +7718,11 @@ TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { prefs::ClearSyncTransportOptIns(prefs_.get()); ASSERT_FALSE(prefs::IsUserOptedInWalletSyncTransport(prefs_.get(), active_info.account_id)); -#endif // !defined(OS_CHROMEOS) /////////////////////////////////////////////////////////// // kSignedOut /////////////////////////////////////////////////////////// - identity_test_env_.RemoveRefreshTokenForAccount(active_info.account_id); + identity_test_env_.ClearPrimaryAccount(); { EXPECT_EQ(AutofillSyncSigninState::kSignedOut, personal_data_->GetSyncSigninState()); @@ -7959,6 +7733,7 @@ TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { EXPECT_FALSE(prefs::IsUserOptedInWalletSyncTransport( prefs_.get(), active_info.account_id)); } +#endif // !defined(OS_CHROMEOS) /////////////////////////////////////////////////////////// // kSignedInAndSyncFeature @@ -8028,4 +7803,132 @@ TEST_F(PersonalDataManagerTest, AddAndGetUpiId) { EXPECT_THAT(all_upi_ids, testing::ElementsAre(upi_id)); } +struct ShareNicknameTestParam { + std::string local_nickname; + std::string server_nickname; + std::string expected_nickname; +}; + +const ShareNicknameTestParam kShareNicknameTestParam[] = { + {"", "", ""}, + {"", "server nickname", "server nickname"}, + {"local nickname", "", "local nickname"}, + {"local nickname", "server nickname", "local nickname"}, +}; + +class PersonalDataManagerTestForSharingNickname + : public PersonalDataManagerTest, + public testing::WithParamInterface { + public: + PersonalDataManagerTestForSharingNickname() + : local_nickname_(base::UTF8ToUTF16(GetParam().local_nickname)), + server_nickname_(base::UTF8ToUTF16(GetParam().server_nickname)), + expected_nickname_(base::UTF8ToUTF16(GetParam().expected_nickname)) {} + + CreditCard GetLocalCard() { + CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15", + test::kEmptyOrigin); + test::SetCreditCardInfo(&local_card, "Clyde Barrow", + "378282246310005" /* American Express */, "04", + "2999", "1"); + local_card.set_use_count(3); + local_card.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); + local_card.SetNickname(local_nickname_); + return local_card; + } + + CreditCard GetServerCard() { + CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "c789"); + test::SetCreditCardInfo(&full_server_card, "Clyde Barrow", + "378282246310005" /* American Express */, "04", + "2999", "1"); + full_server_card.SetNickname(server_nickname_); + return full_server_card; + } + + base::string16 local_nickname_; + base::string16 server_nickname_; + base::string16 expected_nickname_; + + protected: + void SetUp() override { + PersonalDataManagerTest::SetUp(); + EnableWalletCardImport(); + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{features:: + kAutofillEnableSurfacingServerCardNickname, + features::kAutofillEnableCardNicknameManagement}, + /*disabled_features=*/{}); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(, + PersonalDataManagerTestForSharingNickname, + testing::ValuesIn(kShareNicknameTestParam)); + +TEST_P(PersonalDataManagerTestForSharingNickname, + VerifySuggestion_DuplicateCards) { + ASSERT_EQ(0U, personal_data_->GetCreditCards().size()); + CreditCard local_card = GetLocalCard(); + personal_data_->AddCreditCard(local_card); + + SetServerCards({GetServerCard()}); + + personal_data_->Refresh(); + WaitForOnPersonalDataChanged(); + ASSERT_EQ(2U, personal_data_->GetCreditCards().size()); + + // Verifies the suggestion shows the right text. + std::vector suggestions = + personal_data_->GetCreditCardSuggestions( + AutofillType(CREDIT_CARD_NUMBER), + /*field_contents=*/base::string16(), + /*include_server_cards=*/true); + ASSERT_EQ(1U, suggestions.size()); + EXPECT_EQ(suggestions[0].value, + (expected_nickname_.empty() ? base::ASCIIToUTF16("Amex") + : expected_nickname_) + + base::UTF8ToUTF16(" ") + + local_card.ObfuscatedLastFourDigits()); +} + +TEST_P(PersonalDataManagerTestForSharingNickname, + VerifySuggestion_UnrelatedCards) { + ASSERT_EQ(0U, personal_data_->GetCreditCards().size()); + CreditCard local_card = GetLocalCard(); + personal_data_->AddCreditCard(local_card); + + std::vector server_cards; + CreditCard server_card = GetServerCard(); + // Make sure the cards are different by giving a different card number. + server_card.SetNumber(base::ASCIIToUTF16("371449635398431")); + server_cards.emplace_back(server_card); + SetServerCards(server_cards); + + personal_data_->Refresh(); + WaitForOnPersonalDataChanged(); + ASSERT_EQ(2U, personal_data_->GetCreditCards().size()); + + // Verifies the suggestion shows the right text. + std::vector suggestions = + personal_data_->GetCreditCardSuggestions( + AutofillType(CREDIT_CARD_NUMBER), + /*field_contents=*/base::string16(), + /*include_server_cards=*/true); + ASSERT_EQ(2U, suggestions.size()); + EXPECT_THAT( + std::vector({suggestions[0].value, suggestions[1].value}), + testing::UnorderedElementsAre( + (server_nickname_.empty() ? base::ASCIIToUTF16("Amex") + : server_nickname_) + + base::UTF8ToUTF16(" ") + server_card.ObfuscatedLastFourDigits(), + (local_nickname_.empty() ? base::ASCIIToUTF16("Amex") + : local_nickname_) + + base::UTF8ToUTF16(" ") + local_card.ObfuscatedLastFourDigits())); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index a36839bde15..3eb58500da2 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -117,6 +117,20 @@ message AutofillRandomizedValue { optional bytes encoded_bits = 2; } +// Describes how the button is implemented in HTML source. Corresponds to +// the mojo ButtonTitleType enum defined in +// components/autofill/core/common/mojom/autofill_types.mojom.h +enum ButtonTitleType { + NONE = 0; + BUTTON_ELEMENT_SUBMIT_TYPE = 1; //