summaryrefslogtreecommitdiff
path: root/chromium/ui
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-29 10:46:47 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-11-02 12:02:10 +0000
commit99677208ff3b216fdfec551fbe548da5520cd6fb (patch)
tree476a4865c10320249360e859d8fdd3e01833b03a /chromium/ui
parentc30a6232df03e1efbd9f3b226777b07e087a1122 (diff)
downloadqtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui')
-rw-r--r--chromium/ui/accelerated_widget_mac/BUILD.gn4
-rw-r--r--chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h4
-rw-r--r--chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm26
-rw-r--r--chromium/ui/accessibility/BUILD.gn12
-rw-r--r--chromium/ui/accessibility/accessibility_features.cc16
-rw-r--r--chromium/ui/accessibility/accessibility_features.h6
-rw-r--r--chromium/ui/accessibility/accessibility_switches.cc13
-rw-r--r--chromium/ui/accessibility/accessibility_switches.h8
-rw-r--r--chromium/ui/accessibility/ax_action_handler.cc11
-rw-r--r--chromium/ui/accessibility/ax_action_handler.h37
-rw-r--r--chromium/ui/accessibility/ax_action_handler_base.cc32
-rw-r--r--chromium/ui/accessibility/ax_action_handler_base.h60
-rw-r--r--chromium/ui/accessibility/ax_assistant_structure.cc5
-rw-r--r--chromium/ui/accessibility/ax_enum_util.cc69
-rw-r--r--chromium/ui/accessibility/ax_enum_util.h10
-rw-r--r--chromium/ui/accessibility/ax_enum_util_unittest.cc6
-rw-r--r--chromium/ui/accessibility/ax_enums.mojom21
-rw-r--r--chromium/ui/accessibility/ax_event_generator.cc194
-rw-r--r--chromium/ui/accessibility/ax_event_generator.h22
-rw-r--r--chromium/ui/accessibility/ax_event_generator_unittest.cc632
-rw-r--r--chromium/ui/accessibility/ax_language_detection.cc39
-rw-r--r--chromium/ui/accessibility/ax_language_detection.h4
-rw-r--r--chromium/ui/accessibility/ax_language_detection_unittest.cc67
-rw-r--r--chromium/ui/accessibility/ax_node.cc24
-rw-r--r--chromium/ui/accessibility/ax_node.h22
-rw-r--r--chromium/ui/accessibility/ax_node_data.cc46
-rw-r--r--chromium/ui/accessibility/ax_node_data.h9
-rw-r--r--chromium/ui/accessibility/ax_node_position.cc13
-rw-r--r--chromium/ui/accessibility/ax_node_position.h1
-rw-r--r--chromium/ui/accessibility/ax_node_position_unittest.cc672
-rw-r--r--chromium/ui/accessibility/ax_position.h100
-rw-r--r--chromium/ui/accessibility/ax_range.h6
-rw-r--r--chromium/ui/accessibility/ax_role_properties.cc11
-rw-r--r--chromium/ui/accessibility/ax_role_properties.h3
-rw-r--r--chromium/ui/accessibility/ax_table_fuzzer.cc2
-rw-r--r--chromium/ui/accessibility/ax_tree.cc32
-rw-r--r--chromium/ui/accessibility/ax_tree_combiner.cc5
-rw-r--r--chromium/ui/accessibility/ax_tree_data.h1
-rw-r--r--chromium/ui/accessibility/ax_tree_id.cc5
-rw-r--r--chromium/ui/accessibility/ax_tree_id.h14
-rw-r--r--chromium/ui/accessibility/ax_tree_id_registry.cc14
-rw-r--r--chromium/ui/accessibility/ax_tree_id_registry.h18
-rw-r--r--chromium/ui/accessibility/ax_tree_serializer.h21
-rw-r--r--chromium/ui/accessibility/ax_tree_serializer_unittest.cc158
-rw-r--r--chromium/ui/accessibility/ax_tree_source.h6
-rw-r--r--chromium/ui/accessibility/ax_tree_unittest.cc82
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/api.js6
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher_test.unitjs2
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/script_installer.js4
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js10
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs6
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/common/chromevox.js2
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/common/command_store.js66
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/common/math_semantic_tree_test.unitjs26
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/background.js36
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/sre_browser.js4
-rw-r--r--chromium/ui/accessibility/extensions/chromevoxclassic/walkers/walker_unittest_base.js18
-rw-r--r--chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb14
-rw-r--r--chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_da.xtb16
-rw-r--r--chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_eu.xtb16
-rw-r--r--chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb14
-rw-r--r--chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_sq.xtb8
-rw-r--r--chromium/ui/accessibility/platform/BUILD.gn3
-rw-r--r--chromium/ui/accessibility/platform/atk_util_auralinux.cc7
-rw-r--r--chromium/ui/accessibility/platform/atk_util_auralinux_unittest.cc20
-rw-r--r--chromium/ui/accessibility/platform/ax_fragment_root_win.cc26
-rw-r--r--chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc220
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node.cc8
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node.h10
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc160
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_auralinux.h10
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc81
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_base.cc228
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_base.h22
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_base_unittest.cc127
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_delegate.h11
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc12
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_delegate_base.h5
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_mac.mm2
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc44
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc116
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h7
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc360
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_win.cc369
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_win.h58
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc228
-rw-r--r--chromium/ui/accessibility/platform/ax_platform_node_win_unittest.h30
-rw-r--r--chromium/ui/accessibility/platform/ax_system_caret_win.cc4
-rw-r--r--chromium/ui/accessibility/platform/test_ax_node_wrapper.cc6
-rw-r--r--chromium/ui/accessibility/test_ax_node_helper.cc6
-rw-r--r--chromium/ui/android/BUILD.gn18
-rw-r--r--chromium/ui/android/OWNERS1
-rw-r--r--chromium/ui/android/delegated_frame_host_android.cc26
-rw-r--r--chromium/ui/android/delegated_frame_host_android.h26
-rw-r--r--chromium/ui/android/edge_effect.cc350
-rw-r--r--chromium/ui/android/edge_effect.h73
-rw-r--r--chromium/ui/android/edge_effect_base.cc51
-rw-r--r--chromium/ui/android/edge_effect_base.h66
-rw-r--r--chromium/ui/android/edge_effect_l.cc301
-rw-r--r--chromium/ui/android/edge_effect_l.h86
-rw-r--r--chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java14
-rw-r--r--chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java37
-rw-r--r--chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java27
-rw-r--r--chromium/ui/android/overscroll_glow.cc18
-rw-r--r--chromium/ui/android/overscroll_glow.h10
-rw-r--r--chromium/ui/android/overscroll_refresh.cc10
-rw-r--r--chromium/ui/android/overscroll_refresh.h11
-rw-r--r--chromium/ui/android/overscroll_refresh_unittest.cc20
-rw-r--r--chromium/ui/android/resources/resource_manager.h8
-rw-r--r--chromium/ui/android/resources/resource_manager_impl.cc16
-rw-r--r--chromium/ui/android/resources/resource_manager_impl.h10
-rw-r--r--chromium/ui/android/resources/system_ui_resource_type.h2
-rw-r--r--chromium/ui/android/run_all_unittests.cc6
-rw-r--r--chromium/ui/android/view_android.cc25
-rw-r--r--chromium/ui/android/view_android.h12
-rw-r--r--chromium/ui/android/window_android.cc2
-rw-r--r--chromium/ui/aura/BUILD.gn24
-rw-r--r--chromium/ui/aura/DEPS1
-rw-r--r--chromium/ui/aura/client/drag_drop_client.h4
-rw-r--r--chromium/ui/aura/env.cc17
-rw-r--r--chromium/ui/aura/test/ui_controls_factory_ozone.cc358
-rw-r--r--chromium/ui/aura/test/ui_controls_ozone.cc358
-rw-r--r--chromium/ui/aura/test/ui_controls_ozone.h135
-rw-r--r--chromium/ui/aura/window.cc19
-rw-r--r--chromium/ui/aura/window.h30
-rw-r--r--chromium/ui/aura/window_delegate.h7
-rw-r--r--chromium/ui/aura/window_occlusion_change_builder_unittest.cc10
-rw-r--r--chromium/ui/aura/window_occlusion_tracker.cc1
-rw-r--r--chromium/ui/aura/window_occlusion_tracker_unittest.cc54
-rw-r--r--chromium/ui/aura/window_tree_host_platform.cc14
-rw-r--r--chromium/ui/aura/window_tree_host_platform.h10
-rw-r--r--chromium/ui/base/BUILD.gn49
-rw-r--r--chromium/ui/base/accelerators/accelerator.cc18
-rw-r--r--chromium/ui/base/accelerators/accelerator.h2
-rw-r--r--chromium/ui/base/accelerators/accelerator_unittest.cc4
-rw-r--r--chromium/ui/base/android/OWNERS1
-rw-r--r--chromium/ui/base/clipboard/BUILD.gn34
-rw-r--r--chromium/ui/base/clipboard/clipboard.h83
-rw-r--r--chromium/ui/base/clipboard/clipboard_android.cc76
-rw-r--r--chromium/ui/base/clipboard/clipboard_android.h37
-rw-r--r--chromium/ui/base/clipboard/clipboard_android_test_support.cc15
-rw-r--r--chromium/ui/base/clipboard/clipboard_constants.cc15
-rw-r--r--chromium/ui/base/clipboard/clipboard_constants.h26
-rw-r--r--chromium/ui/base/clipboard/clipboard_data.cc77
-rw-r--r--chromium/ui/base/clipboard/clipboard_data.h135
-rw-r--r--chromium/ui/base/clipboard/clipboard_data_endpoint.cc34
-rw-r--r--chromium/ui/base/clipboard/clipboard_data_endpoint.h58
-rw-r--r--chromium/ui/base/clipboard/clipboard_data_unittest.cc45
-rw-r--r--chromium/ui/base/clipboard/clipboard_dlp_controller.h28
-rw-r--r--chromium/ui/base/clipboard/clipboard_format_type.h10
-rw-r--r--chromium/ui/base/clipboard/clipboard_format_type_android.cc2
-rw-r--r--chromium/ui/base/clipboard/clipboard_format_type_aura.cc2
-rw-r--r--chromium/ui/base/clipboard/clipboard_format_type_mac.mm2
-rw-r--r--chromium/ui/base/clipboard/clipboard_format_type_win.cc2
-rw-r--r--chromium/ui/base/clipboard/clipboard_mac.h36
-rw-r--r--chromium/ui/base/clipboard/clipboard_mac.mm74
-rw-r--r--chromium/ui/base/clipboard/clipboard_non_backed.cc386
-rw-r--r--chromium/ui/base/clipboard/clipboard_non_backed.h56
-rw-r--r--chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc57
-rw-r--r--chromium/ui/base/clipboard/clipboard_ozone.cc104
-rw-r--r--chromium/ui/base/clipboard/clipboard_ozone.h37
-rw-r--r--chromium/ui/base/clipboard/clipboard_test_template.h280
-rw-r--r--chromium/ui/base/clipboard/clipboard_util_win.cc2
-rw-r--r--chromium/ui/base/clipboard/clipboard_win.cc71
-rw-r--r--chromium/ui/base/clipboard/clipboard_win.h36
-rw-r--r--chromium/ui/base/clipboard/clipboard_x11.cc81
-rw-r--r--chromium/ui/base/clipboard/clipboard_x11.h39
-rw-r--r--chromium/ui/base/clipboard/scoped_clipboard_writer.cc18
-rw-r--r--chromium/ui/base/clipboard/scoped_clipboard_writer.h14
-rw-r--r--chromium/ui/base/cocoa/menu_controller.h8
-rw-r--r--chromium/ui/base/cocoa/menu_controller.mm14
-rw-r--r--chromium/ui/base/cocoa/menu_controller_unittest.mm14
-rw-r--r--chromium/ui/base/cursor/BUILD.gn23
-rw-r--r--chromium/ui/base/cursor/cursor_theme_manager.h6
-rw-r--r--chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone_unittest.cc24
-rw-r--r--chromium/ui/base/default_style.h4
-rw-r--r--chromium/ui/base/dragdrop/drag_drop_types.h9
-rw-r--r--chromium/ui/base/dragdrop/drop_target_win.h2
-rw-r--r--chromium/ui/base/dragdrop/mojom/BUILD.gn9
-rw-r--r--chromium/ui/base/dragdrop/mojom/OWNERS2
-rw-r--r--chromium/ui/base/dragdrop/mojom/drag_drop_types.mojom10
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider.h4
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc46
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc8
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.h4
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc23
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_win.h11
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_x11.cc33
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_x11.h2
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_provider_x11_unittest.cc3
-rw-r--r--chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc148
-rw-r--r--chromium/ui/base/fullscreen_win.cc6
-rw-r--r--chromium/ui/base/idle/BUILD.gn2
-rw-r--r--chromium/ui/base/idle/idle.h2
-rw-r--r--chromium/ui/base/idle/idle_linux.cc21
-rw-r--r--chromium/ui/base/idle/screensaver_window_finder_x11.cc6
-rw-r--r--chromium/ui/base/ime/BUILD.gn9
-rw-r--r--chromium/ui/base/ime/chromeos/BUILD.gn8
-rw-r--r--chromium/ui/base/ime/dummy_text_input_client.cc23
-rw-r--r--chromium/ui/base/ime/dummy_text_input_client.h7
-rw-r--r--chromium/ui/base/ime/fuchsia/BUILD.gn4
-rw-r--r--chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc1
-rw-r--r--chromium/ui/base/ime/ime_assistive_window_handler_interface.h15
-rw-r--r--chromium/ui/base/ime/ime_engine_handler_interface.h152
-rw-r--r--chromium/ui/base/ime/ime_input_context_handler_interface.h74
-rw-r--r--chromium/ui/base/ime/init/BUILD.gn3
-rw-r--r--chromium/ui/base/ime/init/input_method_factory.cc4
-rw-r--r--chromium/ui/base/ime/init/input_method_initializer.cc2
-rw-r--r--chromium/ui/base/ime/input_method_base.cc92
-rw-r--r--chromium/ui/base/ime/input_method_base.h29
-rw-r--r--chromium/ui/base/ime/linux/BUILD.gn3
-rw-r--r--chromium/ui/base/ime/linux/input_method_auralinux.cc9
-rw-r--r--chromium/ui/base/ime/linux/input_method_auralinux.h2
-rw-r--r--chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc5
-rw-r--r--chromium/ui/base/ime/mac/BUILD.gn6
-rw-r--r--chromium/ui/base/ime/mock_ime_input_context_handler.cc113
-rw-r--r--chromium/ui/base/ime/mock_ime_input_context_handler.h104
-rw-r--r--chromium/ui/base/ime/mock_input_method.cc6
-rw-r--r--chromium/ui/base/ime/mojom/BUILD.gn4
-rw-r--r--chromium/ui/base/ime/mojom/text_input_state.mojom9
-rw-r--r--chromium/ui/base/ime/text_input_client.h37
-rw-r--r--chromium/ui/base/ime/win/BUILD.gn13
-rw-r--r--chromium/ui/base/ime/win/input_method_win_imm32.cc8
-rw-r--r--chromium/ui/base/ime/win/input_method_win_imm32.h3
-rw-r--r--chromium/ui/base/ime/win/input_method_win_tsf.cc11
-rw-r--r--chromium/ui/base/ime/win/input_method_win_tsf.h4
-rw-r--r--chromium/ui/base/ime/win/mock_tsf_bridge.cc1
-rw-r--r--chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc19
-rw-r--r--chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc34
-rw-r--r--chromium/ui/base/ime/win/tsf_bridge.cc299
-rw-r--r--chromium/ui/base/ime/win/tsf_bridge.h6
-rw-r--r--chromium/ui/base/ime/win/tsf_event_router.cc28
-rw-r--r--chromium/ui/base/ime/win/tsf_input_policy_unittest.cc5
-rw-r--r--chromium/ui/base/ime/win/tsf_input_scope.cc8
-rw-r--r--chromium/ui/base/ime/win/tsf_text_store.cc191
-rw-r--r--chromium/ui/base/ime/win/tsf_text_store.h9
-rw-r--r--chromium/ui/base/ime/win/tsf_text_store_unittest.cc249
-rw-r--r--chromium/ui/base/l10n/l10n_util.cc100
-rw-r--r--chromium/ui/base/l10n/l10n_util.h4
-rw-r--r--chromium/ui/base/l10n/l10n_util_unittest.cc6
-rw-r--r--chromium/ui/base/l10n/time_format.cc104
-rw-r--r--chromium/ui/base/models/dialog_model.cc191
-rw-r--r--chromium/ui/base/models/dialog_model.h254
-rw-r--r--chromium/ui/base/models/dialog_model_field.cc223
-rw-r--r--chromium/ui/base/models/dialog_model_field.h299
-rw-r--r--chromium/ui/base/models/dialog_model_host.h45
-rw-r--r--chromium/ui/base/models/image_model.cc41
-rw-r--r--chromium/ui/base/models/image_model.h32
-rw-r--r--chromium/ui/base/models/image_model_unittest.cc66
-rw-r--r--chromium/ui/base/models/simple_menu_model.h3
-rw-r--r--chromium/ui/base/mojom/BUILD.gn8
-rw-r--r--chromium/ui/base/mojom/clipboard_blink_mojom_traits.h4
-rw-r--r--chromium/ui/base/mojom/ui_base_types.mojom16
-rw-r--r--chromium/ui/base/mojom/ui_base_types_mojom_traits.h73
-rw-r--r--chromium/ui/base/pointer/touch_ui_controller.cc4
-rw-r--r--chromium/ui/base/pointer/touch_ui_controller.h2
-rw-r--r--chromium/ui/base/prediction/BUILD.gn37
-rw-r--r--chromium/ui/base/prediction/DEPS3
-rw-r--r--chromium/ui/base/prediction/empty_filter.cc28
-rw-r--r--chromium/ui/base/prediction/empty_filter.h40
-rw-r--r--chromium/ui/base/prediction/empty_filter_unittests.cc47
-rw-r--r--chromium/ui/base/prediction/empty_predictor.cc43
-rw-r--r--chromium/ui/base/prediction/empty_predictor.h47
-rw-r--r--chromium/ui/base/prediction/input_filter.h37
-rw-r--r--chromium/ui/base/prediction/input_filter_unittest_helpers.cc83
-rw-r--r--chromium/ui/base/prediction/input_filter_unittest_helpers.h37
-rw-r--r--chromium/ui/base/prediction/input_predictor.h85
-rw-r--r--chromium/ui/base/prediction/input_predictor_unittest_helpers.cc67
-rw-r--r--chromium/ui/base/prediction/input_predictor_unittest_helpers.h50
-rw-r--r--chromium/ui/base/prediction/kalman_filter.cc124
-rw-r--r--chromium/ui/base/prediction/kalman_filter.h86
-rw-r--r--chromium/ui/base/prediction/kalman_predictor.cc139
-rw-r--r--chromium/ui/base/prediction/kalman_predictor.h84
-rw-r--r--chromium/ui/base/prediction/kalman_predictor_unittest.cc204
-rw-r--r--chromium/ui/base/prediction/least_squares_predictor.cc113
-rw-r--r--chromium/ui/base/prediction/least_squares_predictor.h59
-rw-r--r--chromium/ui/base/prediction/least_squares_predictor_unittest.cc106
-rw-r--r--chromium/ui/base/prediction/linear_predictor.cc128
-rw-r--r--chromium/ui/base/prediction/linear_predictor.h80
-rw-r--r--chromium/ui/base/prediction/linear_predictor_unittest.cc149
-rw-r--r--chromium/ui/base/prediction/linear_resampling.cc97
-rw-r--r--chromium/ui/base/prediction/linear_resampling.h60
-rw-r--r--chromium/ui/base/prediction/linear_resampling_unittest.cc123
-rw-r--r--chromium/ui/base/prediction/one_euro_filter.cc53
-rw-r--r--chromium/ui/base/prediction/one_euro_filter.h52
-rw-r--r--chromium/ui/base/prediction/one_euro_filter_unittests.cc46
-rw-r--r--chromium/ui/base/prediction/prediction_metrics_handler.cc186
-rw-r--r--chromium/ui/base/prediction/prediction_metrics_handler.h110
-rw-r--r--chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc233
-rw-r--r--chromium/ui/base/prediction/prediction_unittest_helpers.h32
-rw-r--r--chromium/ui/base/resource/resource_bundle.cc30
-rw-r--r--chromium/ui/base/ui_base_features.cc35
-rw-r--r--chromium/ui/base/ui_base_features.h24
-rw-r--r--chromium/ui/base/ui_base_paths.cc2
-rw-r--r--chromium/ui/base/ui_base_switches.cc9
-rw-r--r--chromium/ui/base/ui_base_switches.h5
-rw-r--r--chromium/ui/base/ui_base_types.h1
-rw-r--r--chromium/ui/base/ui_features.gni6
-rw-r--r--chromium/ui/base/webui/web_ui_util.cc14
-rw-r--r--chromium/ui/base/win/event_creation_utils.cc22
-rw-r--r--chromium/ui/base/win/shell.cc9
-rw-r--r--chromium/ui/base/window_open_disposition.cc2
-rw-r--r--chromium/ui/base/x/BUILD.gn15
-rw-r--r--chromium/ui/base/x/selection_owner.cc3
-rw-r--r--chromium/ui/base/x/selection_owner.h2
-rw-r--r--chromium/ui/base/x/selection_requestor.cc19
-rw-r--r--chromium/ui/base/x/selection_requestor.h2
-rw-r--r--chromium/ui/base/x/selection_utils.cc25
-rw-r--r--chromium/ui/base/x/selection_utils.h4
-rw-r--r--chromium/ui/base/x/x11_cursor.cc52
-rw-r--r--chromium/ui/base/x/x11_cursor.h38
-rw-r--r--chromium/ui/base/x/x11_cursor_factory.cc142
-rw-r--r--chromium/ui/base/x/x11_cursor_factory.h3
-rw-r--r--chromium/ui/base/x/x11_cursor_loader.cc549
-rw-r--r--chromium/ui/base/x/x11_cursor_loader.h92
-rw-r--r--chromium/ui/base/x/x11_cursor_loader_unittest.cc262
-rw-r--r--chromium/ui/base/x/x11_display_manager.cc25
-rw-r--r--chromium/ui/base/x/x11_display_manager.h1
-rw-r--r--chromium/ui/base/x/x11_display_util.cc2
-rw-r--r--chromium/ui/base/x/x11_drag_context.cc4
-rw-r--r--chromium/ui/base/x/x11_drag_drop_client.h19
-rw-r--r--chromium/ui/base/x/x11_error_handler.cc5
-rw-r--r--chromium/ui/base/x/x11_gl_egl_utility.cc14
-rw-r--r--chromium/ui/base/x/x11_gl_egl_utility.h5
-rw-r--r--chromium/ui/base/x/x11_menu_registrar.cc27
-rw-r--r--chromium/ui/base/x/x11_menu_registrar.h2
-rw-r--r--chromium/ui/base/x/x11_move_loop.h10
-rw-r--r--chromium/ui/base/x/x11_os_exchange_data_provider.cc41
-rw-r--r--chromium/ui/base/x/x11_os_exchange_data_provider.h6
-rw-r--r--chromium/ui/base/x/x11_pointer_grab.cc126
-rw-r--r--chromium/ui/base/x/x11_pointer_grab.h12
-rw-r--r--chromium/ui/base/x/x11_shm_image_pool.cc114
-rw-r--r--chromium/ui/base/x/x11_shm_image_pool.h46
-rw-r--r--chromium/ui/base/x/x11_software_bitmap_presenter.cc106
-rw-r--r--chromium/ui/base/x/x11_software_bitmap_presenter.h15
-rw-r--r--chromium/ui/base/x/x11_ui_thread.cc49
-rw-r--r--chromium/ui/base/x/x11_ui_thread.h48
-rw-r--r--chromium/ui/base/x/x11_util.cc646
-rw-r--r--chromium/ui/base/x/x11_util.h241
-rw-r--r--chromium/ui/base/x/x11_util_internal.h120
-rw-r--r--chromium/ui/base/x/x11_whole_screen_move_loop.cc34
-rw-r--r--chromium/ui/base/x/x11_whole_screen_move_loop.h10
-rw-r--r--chromium/ui/base/x/x11_window.cc115
-rw-r--r--chromium/ui/base/x/x11_window.h15
-rw-r--r--chromium/ui/base/x/x11_workspace_handler.cc30
-rw-r--r--chromium/ui/chromeos/colors/BUILD.gn (renamed from chromium/ui/webui/resources/css/BUILD.gn)2
-rw-r--r--chromium/ui/color/BUILD.gn9
-rw-r--r--chromium/ui/color/color_id.h7
-rw-r--r--chromium/ui/color/ui_color_mixer.cc7
-rw-r--r--chromium/ui/compositor/BUILD.gn29
-rw-r--r--chromium/ui/compositor/animation_metrics_reporter.h20
-rw-r--r--chromium/ui/compositor/animation_throughput_reporter.cc36
-rw-r--r--chromium/ui/compositor/animation_throughput_reporter_unittest.cc43
-rw-r--r--chromium/ui/compositor/compositor.cc14
-rw-r--r--chromium/ui/compositor/compositor.h11
-rw-r--r--chromium/ui/compositor/compositor_observer.h2
-rw-r--r--chromium/ui/compositor/compositor_switches.cc2
-rw-r--r--chromium/ui/compositor/float_animation_curve_adapter.cc6
-rw-r--r--chromium/ui/compositor/layer.cc3
-rw-r--r--chromium/ui/compositor/layer.h4
-rw-r--r--chromium/ui/compositor/layer_animation_element.cc2
-rw-r--r--chromium/ui/compositor/layer_animator_unittest.cc11
-rw-r--r--chromium/ui/compositor/layer_unittest.cc2
-rw-r--r--chromium/ui/compositor/test/test_compositor_host_ozone.cc45
-rw-r--r--chromium/ui/compositor/test/test_compositor_host_ozone.h44
-rw-r--r--chromium/ui/compositor/transform_animation_curve_adapter.cc5
-rw-r--r--chromium/ui/display/BUILD.gn7
-rw-r--r--chromium/ui/display/display.cc2
-rw-r--r--chromium/ui/display/display_features.cc13
-rw-r--r--chromium/ui/display/fake/BUILD.gn3
-rw-r--r--chromium/ui/display/fake/fake_display_delegate.cc55
-rw-r--r--chromium/ui/display/fake/fake_display_delegate.h8
-rw-r--r--chromium/ui/display/mac/screen_mac.mm10
-rw-r--r--chromium/ui/display/manager/BUILD.gn3
-rw-r--r--chromium/ui/display/manager/configure_displays_task.cc165
-rw-r--r--chromium/ui/display/manager/configure_displays_task.h3
-rw-r--r--chromium/ui/display/manager/configure_displays_task_unittest.cc114
-rw-r--r--chromium/ui/display/manager/display_change_observer.cc25
-rw-r--r--chromium/ui/display/manager/display_configurator.cc99
-rw-r--r--chromium/ui/display/manager/display_configurator.h9
-rw-r--r--chromium/ui/display/manager/display_configurator_unittest.cc129
-rw-r--r--chromium/ui/display/manager/display_manager_utilities.cc35
-rw-r--r--chromium/ui/display/manager/display_manager_utilities.h22
-rw-r--r--chromium/ui/display/manager/update_display_configuration_task_unittest.cc96
-rw-r--r--chromium/ui/display/screen.cc2
-rw-r--r--chromium/ui/display/screen.h4
-rw-r--r--chromium/ui/display/types/BUILD.gn4
-rw-r--r--chromium/ui/display/types/display_configuration_params.cc16
-rw-r--r--chromium/ui/display/types/display_configuration_params.h4
-rw-r--r--chromium/ui/display/types/display_snapshot.h3
-rw-r--r--chromium/ui/display/types/native_display_delegate.h18
-rw-r--r--chromium/ui/display/util/BUILD.gn5
-rw-r--r--chromium/ui/display/util/display_util.cc8
-rw-r--r--chromium/ui/display/util/display_util.h7
-rw-r--r--chromium/ui/display/util/display_util_unittest.cc18
-rw-r--r--chromium/ui/display/win/scaling_util.cc4
-rw-r--r--chromium/ui/display/win/screen_win.cc29
-rw-r--r--chromium/ui/display/win/screen_win_unittest.cc53
-rw-r--r--chromium/ui/events/BUILD.gn55
-rw-r--r--chromium/ui/events/DEPS2
-rw-r--r--chromium/ui/events/android/event_handler_android.cc5
-rw-r--r--chromium/ui/events/android/event_handler_android.h6
-rw-r--r--chromium/ui/events/base_event_utils.cc4
-rw-r--r--chromium/ui/events/blink/BUILD.gn5
-rw-r--r--chromium/ui/events/blink/blink_event_util.cc1
-rw-r--r--chromium/ui/events/blink/web_input_event_unittest.cc40
-rw-r--r--chromium/ui/events/cocoa/cocoa_event_utils.mm4
-rw-r--r--chromium/ui/events/cocoa/events_mac_unittest.mm4
-rw-r--r--chromium/ui/events/devices/BUILD.gn6
-rw-r--r--chromium/ui/events/devices/gamepad_device.cc7
-rw-r--r--chromium/ui/events/devices/gamepad_device.h7
-rw-r--r--chromium/ui/events/devices/x11/BUILD.gn4
-rw-r--r--chromium/ui/events/devices/x11/OWNERS1
-rw-r--r--chromium/ui/events/devices/x11/device_data_manager_x11.cc484
-rw-r--r--chromium/ui/events/devices/x11/device_data_manager_x11.h53
-rw-r--r--chromium/ui/events/devices/x11/device_data_manager_x11_unittest.cc175
-rw-r--r--chromium/ui/events/devices/x11/device_list_cache_x11.cc61
-rw-r--r--chromium/ui/events/devices/x11/device_list_cache_x11.h31
-rw-r--r--chromium/ui/events/devices/x11/touch_factory_x11.cc221
-rw-r--r--chromium/ui/events/devices/x11/touch_factory_x11.h31
-rw-r--r--chromium/ui/events/devices/x11/xinput_util.h28
-rw-r--r--chromium/ui/events/event.cc37
-rw-r--r--chromium/ui/events/event.h2
-rw-r--r--chromium/ui/events/event_constants.h2
-rw-r--r--chromium/ui/events/event_switches.cc2
-rw-r--r--chromium/ui/events/event_switches.h2
-rw-r--r--chromium/ui/events/event_unittest.cc125
-rw-r--r--chromium/ui/events/event_utils.cc2
-rw-r--r--chromium/ui/events/fraction_of_time_without_user_input_recorder.cc3
-rw-r--r--chromium/ui/events/gesture_detection/motion_event_buffer.cc3
-rw-r--r--chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc64
-rw-r--r--chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc1
-rw-r--r--chromium/ui/events/gestures/physics_based_fling_curve.cc53
-rw-r--r--chromium/ui/events/gestures/physics_based_fling_curve.h31
-rw-r--r--chromium/ui/events/ipc/BUILD.gn4
-rw-r--r--chromium/ui/events/keyboard_hook_base.cc5
-rw-r--r--chromium/ui/events/keyboard_hook_base.h2
-rw-r--r--chromium/ui/events/keyboard_hook_linux.cc56
-rw-r--r--chromium/ui/events/keycodes/BUILD.gn5
-rw-r--r--chromium/ui/events/keycodes/DEPS3
-rw-r--r--chromium/ui/events/keycodes/dom/keycode_converter.cc8
-rw-r--r--chromium/ui/events/keycodes/keyboard_code_conversion_x.cc156
-rw-r--r--chromium/ui/events/keycodes/keyboard_code_conversion_x.h24
-rw-r--r--chromium/ui/events/ozone/BUILD.gn2
-rw-r--r--chromium/ui/events/ozone/device/udev/device_manager_udev.cc4
-rw-r--r--chromium/ui/events/ozone/evdev/BUILD.gn2
-rw-r--r--chromium/ui/events/ozone/evdev/event_converter_evdev.cc18
-rw-r--r--chromium/ui/events/ozone/evdev/event_converter_evdev.h11
-rw-r--r--chromium/ui/events/ozone/evdev/event_device_info.cc46
-rw-r--r--chromium/ui/events/ozone/evdev/event_device_info.h6
-rw-r--r--chromium/ui/events/ozone/evdev/event_device_test_util.cc5
-rw-r--r--chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc114
-rw-r--r--chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h44
-rw-r--r--chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc76
-rw-r--r--chromium/ui/events/ozone/evdev/input_controller_evdev.cc14
-rw-r--r--chromium/ui/events/ozone/evdev/input_controller_evdev.h4
-rw-r--r--chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc36
-rw-r--r--chromium/ui/events/ozone/evdev/input_device_factory_evdev.h4
-rw-r--r--chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc16
-rw-r--r--chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h2
-rw-r--r--chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc21
-rw-r--r--chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h8
-rw-r--r--chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc15
-rw-r--r--chromium/ui/events/ozone/features.cc3
-rw-r--r--chromium/ui/events/ozone/features.h3
-rw-r--r--chromium/ui/events/ozone/keyboard_hook_ozone.cc26
-rw-r--r--chromium/ui/events/ozone/keyboard_hook_ozone.h29
-rw-r--r--chromium/ui/events/platform/BUILD.gn3
-rw-r--r--chromium/ui/events/platform/x11/BUILD.gn8
-rw-r--r--chromium/ui/events/platform/x11/DEPS1
-rw-r--r--chromium/ui/events/platform/x11/OWNERS1
-rw-r--r--chromium/ui/events/platform/x11/x11_event_source.cc258
-rw-r--r--chromium/ui/events/platform/x11/x11_event_source.h15
-rw-r--r--chromium/ui/events/platform/x11/x11_event_watcher_fdwatch.cc10
-rw-r--r--chromium/ui/events/platform/x11/x11_event_watcher_glib.cc10
-rw-r--r--chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc194
-rw-r--r--chromium/ui/events/platform_event.h4
-rw-r--r--chromium/ui/events/x/BUILD.gn25
-rw-r--r--chromium/ui/events/x/OWNERS3
-rw-r--r--chromium/ui/events/x/events_x_unittest.cc583
-rw-r--r--chromium/ui/events/x/events_x_utils.cc680
-rw-r--r--chromium/ui/events/x/events_x_utils.h9
-rw-r--r--chromium/ui/events/x/keyboard_hook_x11.cc79
-rw-r--r--chromium/ui/events/x/keyboard_hook_x11.h50
-rw-r--r--chromium/ui/events/x/x11_event_translation.cc113
-rw-r--r--chromium/ui/events/x/x11_event_translation_unittest.cc226
-rw-r--r--chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn19
-rw-r--r--chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn17
-rw-r--r--chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn5
-rw-r--r--chromium/ui/gfx/BUILD.gn38
-rw-r--r--chromium/ui/gfx/animation/BUILD.gn5
-rw-r--r--chromium/ui/gfx/animation/animation_mac.mm5
-rw-r--r--chromium/ui/gfx/animation/linear_animation.cc13
-rw-r--r--chromium/ui/gfx/animation/multi_animation.cc21
-rw-r--r--chromium/ui/gfx/animation/multi_animation_unittest.cc35
-rw-r--r--chromium/ui/gfx/animation/tween.cc15
-rw-r--r--chromium/ui/gfx/ca_layer_params.h4
-rw-r--r--chromium/ui/gfx/canvas.cc10
-rw-r--r--chromium/ui/gfx/codec/BUILD.gn3
-rw-r--r--chromium/ui/gfx/color_space.cc138
-rw-r--r--chromium/ui/gfx/color_space.h62
-rw-r--r--chromium/ui/gfx/color_space_unittest.cc84
-rw-r--r--chromium/ui/gfx/color_utils.cc23
-rw-r--r--chromium/ui/gfx/display_color_spaces.cc7
-rw-r--r--chromium/ui/gfx/display_color_spaces.h7
-rw-r--r--chromium/ui/gfx/font.cc4
-rw-r--r--chromium/ui/gfx/font.h4
-rw-r--r--chromium/ui/gfx/font_fallback_win.cc4
-rw-r--r--chromium/ui/gfx/font_list.cc2
-rw-r--r--chromium/ui/gfx/font_names_testing.cc8
-rw-r--r--chromium/ui/gfx/font_render_params.h6
-rw-r--r--chromium/ui/gfx/font_unittest.cc4
-rw-r--r--chromium/ui/gfx/font_util.cc6
-rw-r--r--chromium/ui/gfx/geometry/BUILD.gn5
-rw-r--r--chromium/ui/gfx/geometry/point.cc6
-rw-r--r--chromium/ui/gfx/geometry/point.h6
-rw-r--r--chromium/ui/gfx/geometry/point_conversions.cc14
-rw-r--r--chromium/ui/gfx/geometry/rect.cc6
-rw-r--r--chromium/ui/gfx/geometry/rect.h22
-rw-r--r--chromium/ui/gfx/geometry/rect_conversions.cc82
-rw-r--r--chromium/ui/gfx/geometry/rect_f.cc15
-rw-r--r--chromium/ui/gfx/geometry/rect_f.h4
-rw-r--r--chromium/ui/gfx/geometry/safe_integer_conversions.h61
-rw-r--r--chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc80
-rw-r--r--chromium/ui/gfx/geometry/scroll_offset.h4
-rw-r--r--chromium/ui/gfx/geometry/size.cc7
-rw-r--r--chromium/ui/gfx/geometry/size.h8
-rw-r--r--chromium/ui/gfx/geometry/size_conversions.cc14
-rw-r--r--chromium/ui/gfx/geometry/vector2d_conversions.cc16
-rw-r--r--chromium/ui/gfx/gpu_fence.cc8
-rw-r--r--chromium/ui/gfx/gpu_memory_buffer.cc4
-rw-r--r--chromium/ui/gfx/gpu_memory_buffer.h8
-rw-r--r--chromium/ui/gfx/icon_util.cc9
-rw-r--r--chromium/ui/gfx/icon_util_unittest.cc34
-rw-r--r--chromium/ui/gfx/image/image.cc38
-rw-r--r--chromium/ui/gfx/image/image.h15
-rw-r--r--chromium/ui/gfx/image/image_platform.h6
-rw-r--r--chromium/ui/gfx/image/image_skia_operations.cc4
-rw-r--r--chromium/ui/gfx/image/image_unittest.cc6
-rw-r--r--chromium/ui/gfx/image/image_unittest_util.cc16
-rw-r--r--chromium/ui/gfx/image/image_unittest_util.h2
-rw-r--r--chromium/ui/gfx/image/image_util.cc4
-rw-r--r--chromium/ui/gfx/interpolated_transform.cc4
-rw-r--r--chromium/ui/gfx/ipc/BUILD.gn4
-rw-r--r--chromium/ui/gfx/ipc/buffer_types/BUILD.gn4
-rw-r--r--chromium/ui/gfx/ipc/color/BUILD.gn5
-rw-r--r--chromium/ui/gfx/ipc/color/gfx_param_traits.cc31
-rw-r--r--chromium/ui/gfx/ipc/color/gfx_param_traits.h11
-rw-r--r--chromium/ui/gfx/ipc/geometry/BUILD.gn4
-rw-r--r--chromium/ui/gfx/ipc/gfx_param_traits.cc6
-rw-r--r--chromium/ui/gfx/ipc/gfx_param_traits.h6
-rw-r--r--chromium/ui/gfx/ipc/gfx_param_traits_macros.h14
-rw-r--r--chromium/ui/gfx/ipc/skia/BUILD.gn4
-rw-r--r--chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc1
-rw-r--r--chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h1
-rw-r--r--chromium/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h6
-rw-r--r--chromium/ui/gfx/linux/gpu_memory_buffer_support_x11.cc34
-rw-r--r--chromium/ui/gfx/mac/io_surface.cc32
-rw-r--r--chromium/ui/gfx/mojom/BUILD.gn2
-rw-r--r--chromium/ui/gfx/mojom/accelerated_widget_mojom_traits.h4
-rw-r--r--chromium/ui/gfx/mojom/buffer_types_mojom_traits.cc8
-rw-r--r--chromium/ui/gfx/mojom/ca_layer_params_mojom_traits.cc4
-rw-r--r--chromium/ui/gfx/mojom/mojom_traits_unittest.cc8
-rw-r--r--chromium/ui/gfx/mojom/native_handle_types_mojom_traits.cc14
-rw-r--r--chromium/ui/gfx/mojom/native_handle_types_mojom_traits.h8
-rw-r--r--chromium/ui/gfx/native_pixmap_handle.cc12
-rw-r--r--chromium/ui/gfx/native_pixmap_handle.h8
-rw-r--r--chromium/ui/gfx/native_widget_types.h24
-rw-r--r--chromium/ui/gfx/nine_image_painter.cc10
-rw-r--r--chromium/ui/gfx/paint_throbber.cc41
-rw-r--r--chromium/ui/gfx/paint_vector_icon.cc5
-rw-r--r--chromium/ui/gfx/platform_font.h6
-rw-r--r--chromium/ui/gfx/platform_font_mac.h64
-rw-r--r--chromium/ui/gfx/platform_font_mac.mm616
-rw-r--r--chromium/ui/gfx/platform_font_mac_unittest.mm347
-rw-r--r--chromium/ui/gfx/range/BUILD.gn6
-rw-r--r--chromium/ui/gfx/range/range.h8
-rw-r--r--chromium/ui/gfx/range/range_f.cc18
-rw-r--r--chromium/ui/gfx/range/range_unittest.cc30
-rw-r--r--chromium/ui/gfx/render_text.cc53
-rw-r--r--chromium/ui/gfx/render_text.h28
-rw-r--r--chromium/ui/gfx/render_text_harfbuzz.cc77
-rw-r--r--chromium/ui/gfx/render_text_harfbuzz.h2
-rw-r--r--chromium/ui/gfx/render_text_test_api.h6
-rw-r--r--chromium/ui/gfx/render_text_unittest.cc630
-rw-r--r--chromium/ui/gfx/shadow_value.cc25
-rw-r--r--chromium/ui/gfx/skbitmap_operations.cc4
-rw-r--r--chromium/ui/gfx/skbitmap_operations_unittest.cc24
-rw-r--r--chromium/ui/gfx/skia_vector_animation.cc23
-rw-r--r--chromium/ui/gfx/skia_vector_animation.h7
-rw-r--r--chromium/ui/gfx/skia_vector_animation_unittest.cc425
-rw-r--r--chromium/ui/gfx/swap_result.h11
-rw-r--r--chromium/ui/gfx/text_elider_unittest.cc2
-rw-r--r--chromium/ui/gfx/transform.cc3
-rw-r--r--chromium/ui/gfx/win/singleton_hwnd.cc6
-rw-r--r--chromium/ui/gfx/x/BUILD.gn7
-rw-r--r--chromium/ui/gfx/x/DEPS3
-rw-r--r--chromium/ui/gfx/x/connection.cc448
-rw-r--r--chromium/ui/gfx/x/connection.h83
-rw-r--r--chromium/ui/gfx/x/connection_unittest.cc13
-rw-r--r--chromium/ui/gfx/x/event.cc75
-rw-r--r--chromium/ui/gfx/x/event.h38
-rw-r--r--chromium/ui/gfx/x/gen_xproto.py157
-rw-r--r--chromium/ui/gfx/x/x11.h15
-rw-r--r--chromium/ui/gfx/x/x11_types.cc142
-rw-r--r--chromium/ui/gfx/x/x11_types.h36
-rw-r--r--chromium/ui/gfx/x/xproto_internal.cc158
-rw-r--r--chromium/ui/gfx/x/xproto_internal.h160
-rw-r--r--chromium/ui/gfx/x/xproto_types.cc75
-rw-r--r--chromium/ui/gfx/x/xproto_types.h95
-rw-r--r--chromium/ui/gl/BUILD.gn22
-rw-r--r--chromium/ui/gl/OWNERS1
-rw-r--r--chromium/ui/gl/angle_platform_impl.cc13
-rw-r--r--chromium/ui/gl/dc_layer_tree.cc8
-rw-r--r--chromium/ui/gl/dc_layer_tree.h5
-rw-r--r--chromium/ui/gl/direct_composition_child_surface_win.cc226
-rw-r--r--chromium/ui/gl/direct_composition_child_surface_win.h78
-rw-r--r--chromium/ui/gl/direct_composition_surface_win.cc262
-rw-r--r--chromium/ui/gl/direct_composition_surface_win.h62
-rw-r--r--chromium/ui/gl/direct_composition_surface_win_unittest.cc4
-rw-r--r--chromium/ui/gl/features.gni6
-rwxr-xr-xchromium/ui/gl/generate_bindings.py14
-rw-r--r--chromium/ui/gl/gl_angle_util_win.cc12
-rw-r--r--chromium/ui/gl/gl_bindings.h7
-rw-r--r--chromium/ui/gl/gl_bindings_api_autogen_gl.h9
-rw-r--r--chromium/ui/gl/gl_bindings_autogen_gl.cc79
-rw-r--r--chromium/ui/gl/gl_bindings_autogen_gl.h25
-rw-r--r--chromium/ui/gl/gl_bindings_autogen_mock.cc27
-rw-r--r--chromium/ui/gl/gl_bindings_autogen_mock.h12
-rw-r--r--chromium/ui/gl/gl_context.cc23
-rw-r--r--chromium/ui/gl/gl_context.h24
-rw-r--r--chromium/ui/gl/gl_context_cgl.cc2
-rw-r--r--chromium/ui/gl/gl_context_cgl.h2
-rw-r--r--chromium/ui/gl/gl_context_egl.cc29
-rw-r--r--chromium/ui/gl/gl_context_egl.h4
-rw-r--r--chromium/ui/gl/gl_context_glx.cc4
-rw-r--r--chromium/ui/gl/gl_context_glx.h4
-rw-r--r--chromium/ui/gl/gl_context_glx_unittest.cc4
-rw-r--r--chromium/ui/gl/gl_context_stub.cc6
-rw-r--r--chromium/ui/gl/gl_context_stub.h6
-rw-r--r--chromium/ui/gl/gl_context_wgl.cc2
-rw-r--r--chromium/ui/gl/gl_context_wgl.h2
-rw-r--r--chromium/ui/gl/gl_enums_implementation_autogen.h4
-rw-r--r--chromium/ui/gl/gl_features.cc3
-rw-r--r--chromium/ui/gl/gl_fence.cc10
-rw-r--r--chromium/ui/gl/gl_gl_api_implementation.cc27
-rw-r--r--chromium/ui/gl/gl_gl_api_implementation.h18
-rw-r--r--chromium/ui/gl/gl_image.cc12
-rw-r--r--chromium/ui/gl/gl_image.h11
-rw-r--r--chromium/ui/gl/gl_image_dxgi.cc29
-rw-r--r--chromium/ui/gl/gl_image_dxgi_unittest.cc5
-rw-r--r--chromium/ui/gl/gl_image_egl_pixmap.cc118
-rw-r--r--chromium/ui/gl/gl_image_egl_pixmap.h55
-rw-r--r--chromium/ui/gl/gl_image_io_surface.h8
-rw-r--r--chromium/ui/gl/gl_image_io_surface.mm39
-rw-r--r--chromium/ui/gl/gl_image_io_surface_egl.mm13
-rw-r--r--chromium/ui/gl/gl_implementation.cc2
-rw-r--r--chromium/ui/gl/gl_implementation.h3
-rw-r--r--chromium/ui/gl/gl_mock_autogen_gl.h10
-rw-r--r--chromium/ui/gl/gl_share_group.cc4
-rw-r--r--chromium/ui/gl/gl_share_group.h4
-rw-r--r--chromium/ui/gl/gl_stub_autogen_gl.h9
-rw-r--r--chromium/ui/gl/gl_surface_egl.cc52
-rw-r--r--chromium/ui/gl/gl_surface_egl.h6
-rw-r--r--chromium/ui/gl/gl_surface_egl_surface_control.cc46
-rw-r--r--chromium/ui/gl/gl_surface_egl_surface_control.h2
-rw-r--r--chromium/ui/gl/gl_surface_egl_unittest.cc1
-rw-r--r--chromium/ui/gl/gl_surface_egl_x11.cc20
-rw-r--r--chromium/ui/gl/gl_surface_egl_x11_gles2.cc16
-rw-r--r--chromium/ui/gl/gl_surface_glx.cc96
-rw-r--r--chromium/ui/gl/gl_switches.cc24
-rw-r--r--chromium/ui/gl/gl_switches.h5
-rw-r--r--chromium/ui/gl/gl_utils.cc49
-rw-r--r--chromium/ui/gl/gl_utils.h15
-rw-r--r--chromium/ui/gl/gl_version_info.cc19
-rw-r--r--chromium/ui/gl/gl_version_info.h26
-rw-r--r--chromium/ui/gl/init/BUILD.gn5
-rw-r--r--chromium/ui/gl/init/create_gr_gl_interface.cc18
-rw-r--r--chromium/ui/gl/init/gl_factory_android.cc4
-rw-r--r--chromium/ui/gl/init/gl_factory_linux_x11.cc6
-rw-r--r--chromium/ui/gl/swap_chain_presenter.cc227
-rw-r--r--chromium/ui/gl/swap_chain_presenter.h30
-rw-r--r--chromium/ui/gl/sync_control_vsync_provider.cc10
-rw-r--r--chromium/ui/gl/sync_control_vsync_provider.h8
-rw-r--r--chromium/ui/gl/vsync_observer.h2
-rw-r--r--chromium/ui/gl/vsync_thread_win.h2
-rw-r--r--chromium/ui/gl/yuv_to_rgb_converter.cc14
-rw-r--r--chromium/ui/gl/yuv_to_rgb_converter.h3
-rw-r--r--chromium/ui/gtk/BUILD.gn5
-rw-r--r--chromium/ui/gtk/gtk_ui.cc8
-rw-r--r--chromium/ui/gtk/gtk_ui_delegate.h1
-rw-r--r--chromium/ui/gtk/gtk_util.cc4
-rw-r--r--chromium/ui/gtk/gtk_util.h2
-rw-r--r--chromium/ui/gtk/printing/print_dialog_gtk.cc10
-rw-r--r--chromium/ui/gtk/select_file_dialog_impl.cc10
-rw-r--r--chromium/ui/gtk/select_file_dialog_impl.h5
-rw-r--r--chromium/ui/gtk/select_file_dialog_impl_gtk.cc3
-rw-r--r--chromium/ui/gtk/select_file_dialog_impl_kde.cc114
-rw-r--r--chromium/ui/gtk/x/BUILD.gn1
-rw-r--r--chromium/ui/gtk/x/DEPS3
-rw-r--r--chromium/ui/gtk/x/gtk_event_loop_x11.cc1
-rw-r--r--chromium/ui/gtk/x/gtk_ui_delegate_x11.cc39
-rw-r--r--chromium/ui/gtk/x/gtk_ui_delegate_x11.h7
-rw-r--r--chromium/ui/latency/BUILD.gn12
-rw-r--r--chromium/ui/latency/average_lag_tracker.cc220
-rw-r--r--chromium/ui/latency/average_lag_tracker.h106
-rw-r--r--chromium/ui/latency/average_lag_tracker_unittest.cc540
-rw-r--r--chromium/ui/latency/latency_info.dot14
-rw-r--r--chromium/ui/latency/latency_tracker.cc5
-rw-r--r--chromium/ui/latency/latency_tracker.h3
-rw-r--r--chromium/ui/login/account_picker/chromeos_user_pod_row.js3
-rw-r--r--chromium/ui/login/display_manager.js80
-rw-r--r--chromium/ui/login/display_manager_types.js22
-rw-r--r--chromium/ui/login/oobe.css13
-rw-r--r--chromium/ui/message_center/BUILD.gn13
-rw-r--r--chromium/ui/message_center/message_center_impl.cc6
-rw-r--r--chromium/ui/message_center/message_center_impl_unittest.cc46
-rw-r--r--chromium/ui/message_center/public/cpp/BUILD.gn4
-rw-r--r--chromium/ui/message_center/public/cpp/message_center_constants.h2
-rw-r--r--chromium/ui/message_center/public/cpp/notification.h6
-rw-r--r--chromium/ui/message_center/views/message_popup_collection_unittest.cc2
-rw-r--r--chromium/ui/message_center/views/message_popup_view.cc2
-rw-r--r--chromium/ui/message_center/views/notification_header_view.cc4
-rw-r--r--chromium/ui/message_center/views/notification_header_view.h7
-rw-r--r--chromium/ui/message_center/views/notification_view_md.cc101
-rw-r--r--chromium/ui/message_center/views/notification_view_md.h86
-rw-r--r--chromium/ui/message_center/views/notification_view_md_unittest.cc45
-rw-r--r--chromium/ui/message_center/views/relative_time_formatter.cc3
-rw-r--r--chromium/ui/native_theme/BUILD.gn11
-rw-r--r--chromium/ui/native_theme/caption_style.cc2
-rw-r--r--chromium/ui/native_theme/caption_style_win.cc6
-rw-r--r--chromium/ui/native_theme/common_theme.cc9
-rw-r--r--chromium/ui/native_theme/native_theme.cc10
-rw-r--r--chromium/ui/native_theme/native_theme.h7
-rw-r--r--chromium/ui/native_theme/native_theme_aura.cc15
-rw-r--r--chromium/ui/native_theme/native_theme_aura.h3
-rw-r--r--chromium/ui/native_theme/native_theme_base.cc46
-rw-r--r--chromium/ui/native_theme/native_theme_color_id.h4
-rw-r--r--chromium/ui/native_theme/native_theme_mac.h2
-rw-r--r--chromium/ui/native_theme/native_theme_mac.mm6
-rw-r--r--chromium/ui/native_theme/themed_vector_icon.cc9
-rw-r--r--chromium/ui/native_theme/themed_vector_icon.h7
-rw-r--r--chromium/ui/ozone/BUILD.gn17
-rw-r--r--chromium/ui/ozone/demo/demo_window.h1
-rw-r--r--chromium/ui/ozone/demo/skia/skia_gl_renderer.cc9
-rw-r--r--chromium/ui/ozone/demo/skia/skia_gl_renderer.h8
-rw-r--r--chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc3
-rw-r--r--chromium/ui/ozone/demo/software_renderer.cc10
-rw-r--r--chromium/ui/ozone/demo/vulkan_renderer.cc96
-rw-r--r--chromium/ui/ozone/demo/window_manager.cc21
-rw-r--r--chromium/ui/ozone/demo/window_manager.h7
-rw-r--r--chromium/ui/ozone/ozone.gni1
-rw-r--r--chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc4
-rw-r--r--chromium/ui/ozone/platform/cast/surface_factory_cast.cc3
-rw-r--r--chromium/ui/ozone/platform/cast/surface_factory_cast.h3
-rw-r--r--chromium/ui/ozone/platform/drm/common/drm_util.cc20
-rw-r--r--chromium/ui/ozone/platform/drm/common/drm_util.h2
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_device.cc10
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_display.cc35
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc45
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc10
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc30
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_thread.cc31
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_thread.h6
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc21
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h3
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc6
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc12
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h3
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc2
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc13
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h5
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc16
-rw-r--r--chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc4
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_device_connector.cc1
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_display_host.cc34
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_display_host.h7
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc31
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h4
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc13
-rw-r--r--chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h7
-rw-r--r--chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h8
-rw-r--r--chromium/ui/ozone/platform/drm/host/host_drm_device.cc30
-rw-r--r--chromium/ui/ozone/platform/drm/host/host_drm_device.h8
-rw-r--r--chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc44
-rw-r--r--chromium/ui/ozone/platform/headless/headless_surface_factory.cc4
-rw-r--r--chromium/ui/ozone/platform/headless/headless_surface_factory.h3
-rw-r--r--chromium/ui/ozone/platform/headless/ozone_platform_headless.cc1
-rw-r--r--chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc3
-rw-r--r--chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc21
-rw-r--r--chromium/ui/ozone/platform/scenic/scenic_gpu_host.cc1
-rw-r--r--chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc3
-rw-r--r--chromium/ui/ozone/platform/scenic/scenic_surface_factory.h3
-rw-r--r--chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc68
-rw-r--r--chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h14
-rw-r--r--chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc14
-rw-r--r--chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h2
-rw-r--r--chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc15
-rw-r--r--chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/BUILD.gn62
-rw-r--r--chromium/ui/ozone/platform/wayland/common/data_util.cc31
-rw-r--r--chromium/ui/ozone/platform/wayland/common/data_util.h2
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_object.cc10
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_object.h14
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_util.cc24
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_util.h11
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc91
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h10
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc108
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h48
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.cc24
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h39
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.cc56
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h44
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager_unittest.cc77
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h3
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc10
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc115
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h39
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc448
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h43
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc15
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_connection.cc51
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_connection.h5
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc10
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_cursor.h2
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc30
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device.h6
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc5
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc61
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc25
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc186
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc13
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc10
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_source.h6
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc8
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc5
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_output.cc9
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_output.h12
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc50
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_popup.cc28
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_screen.cc56
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_screen.h13
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc175
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc176
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h68
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_surface.cc128
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_surface.h53
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc67
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h31
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_touch.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window.cc255
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window.h89
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc131
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h36
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc118
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc6
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc22
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h12
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc26
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc7
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h9
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc220
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc19
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc40
-rw-r--r--chromium/ui/ozone/platform/wayland/test/constants.h18
-rw-r--r--chromium/ui/ozone/platform/wayland/test/mock_surface.cc19
-rw-r--r--chromium/ui/ozone/platform/wayland/test/mock_surface.h5
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_device_manager.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_offer.cc9
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_offer.h3
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_source.cc1
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_output.cc51
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_output.h17
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc22
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h23
-rw-r--r--chromium/ui/ozone/platform/wayland/test/wayland_test.cc5
-rw-r--r--chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc289
-rw-r--r--chromium/ui/ozone/platform/x11/BUILD.gn3
-rw-r--r--chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc4
-rw-r--r--chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h1
-rw-r--r--chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.cc107
-rw-r--r--chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.h16
-rw-r--r--chromium/ui/ozone/platform/x11/ozone_platform_x11.cc44
-rw-r--r--chromium/ui/ozone/platform/x11/x11_canvas_surface.cc9
-rw-r--r--chromium/ui/ozone/platform/x11/x11_canvas_surface.h5
-rw-r--r--chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc51
-rw-r--r--chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h1
-rw-r--r--chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc17
-rw-r--r--chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc4
-rw-r--r--chromium/ui/ozone/platform/x11/x11_surface_factory.cc23
-rw-r--r--chromium/ui/ozone/platform/x11/x11_surface_factory.h8
-rw-r--r--chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc13
-rw-r--r--chromium/ui/ozone/public/input_controller.cc4
-rw-r--r--chromium/ui/ozone/public/input_controller.h9
-rw-r--r--chromium/ui/ozone/public/mojom/drm_device.mojom9
-rw-r--r--chromium/ui/ozone/public/mojom/wayland/BUILD.gn5
-rw-r--r--chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom10
-rw-r--r--chromium/ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom46
-rw-r--r--chromium/ui/ozone/public/ozone_platform.cc5
-rw-r--r--chromium/ui/ozone/public/ozone_platform.h21
-rw-r--r--chromium/ui/ozone/public/ozone_switches.cc4
-rw-r--r--chromium/ui/ozone/public/ozone_switches.h3
-rw-r--r--chromium/ui/ozone/public/platform_clipboard.h8
-rw-r--r--chromium/ui/ozone/public/platform_gl_egl_utility.h4
-rw-r--r--chromium/ui/ozone/public/platform_screen.h1
-rw-r--r--chromium/ui/ozone/public/surface_factory_ozone.cc3
-rw-r--r--chromium/ui/ozone/public/surface_factory_ozone.h6
-rw-r--r--chromium/ui/ozone/test/mock_platform_window_delegate.h1
-rw-r--r--chromium/ui/platform_window/BUILD.gn13
-rw-r--r--chromium/ui/platform_window/common/BUILD.gn2
-rw-r--r--chromium/ui/platform_window/extensions/BUILD.gn29
-rw-r--r--chromium/ui/platform_window/extensions/wayland_extension.cc27
-rw-r--r--chromium/ui/platform_window/extensions/wayland_extension.h33
-rw-r--r--chromium/ui/platform_window/extensions/workspace_extension.h4
-rw-r--r--chromium/ui/platform_window/extensions/workspace_extension_delegate.h2
-rw-r--r--chromium/ui/platform_window/extensions/x11_extension.h4
-rw-r--r--chromium/ui/platform_window/extensions/x11_extension_delegate.h9
-rw-r--r--chromium/ui/platform_window/platform_window.cc6
-rw-r--r--chromium/ui/platform_window/platform_window.h5
-rw-r--r--chromium/ui/platform_window/platform_window_delegate.h3
-rw-r--r--chromium/ui/platform_window/platform_window_handler/wm_drag_handler.h57
-rw-r--r--chromium/ui/platform_window/platform_window_handler/wm_drop_handler.h55
-rw-r--r--chromium/ui/platform_window/platform_window_handler/wm_platform_export.h32
-rw-r--r--chromium/ui/platform_window/platform_window_init_properties.h4
-rw-r--r--chromium/ui/platform_window/stub/BUILD.gn4
-rw-r--r--chromium/ui/platform_window/win/BUILD.gn4
-rw-r--r--chromium/ui/platform_window/wm/BUILD.gn (renamed from chromium/ui/platform_window/platform_window_handler/BUILD.gn)9
-rw-r--r--chromium/ui/platform_window/wm/DEPS (renamed from chromium/ui/platform_window/platform_window_handler/DEPS)0
-rw-r--r--chromium/ui/platform_window/wm/wm_drag_handler.cc (renamed from chromium/ui/platform_window/platform_window_handler/wm_drag_handler.cc)2
-rw-r--r--chromium/ui/platform_window/wm/wm_drag_handler.h70
-rw-r--r--chromium/ui/platform_window/wm/wm_drop_handler.cc (renamed from chromium/ui/platform_window/platform_window_handler/wm_drop_handler.cc)2
-rw-r--r--chromium/ui/platform_window/wm/wm_drop_handler.h64
-rw-r--r--chromium/ui/platform_window/wm/wm_move_loop_handler.cc (renamed from chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.cc)2
-rw-r--r--chromium/ui/platform_window/wm/wm_move_loop_handler.h (renamed from chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.h)19
-rw-r--r--chromium/ui/platform_window/wm/wm_move_resize_handler.cc (renamed from chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc)2
-rw-r--r--chromium/ui/platform_window/wm/wm_move_resize_handler.h (renamed from chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h)19
-rw-r--r--chromium/ui/platform_window/x11/BUILD.gn15
-rw-r--r--chromium/ui/platform_window/x11/atk_event_conversion.cc33
-rw-r--r--chromium/ui/platform_window/x11/x11_window.cc233
-rw-r--r--chromium/ui/platform_window/x11/x11_window.h58
-rw-r--r--chromium/ui/resources/BUILD.gn5
-rw-r--r--chromium/ui/resources/default_100_percent/common/fingerprint.pngbin40389 -> 0 bytes
-rw-r--r--chromium/ui/resources/default_100_percent/common/tick.pngbin21647 -> 0 bytes
-rw-r--r--chromium/ui/resources/default_200_percent/common/fingerprint.pngbin105080 -> 0 bytes
-rw-r--r--chromium/ui/resources/default_200_percent/common/tick.pngbin36849 -> 0 bytes
-rw-r--r--chromium/ui/resources/ui_resources.grd5
-rw-r--r--chromium/ui/resources/vector/common/fingerprint_enrollment.json1
-rw-r--r--chromium/ui/resources/vector/common/tick.json1
-rw-r--r--chromium/ui/resources/vector/common/tick_dark.json1
-rw-r--r--chromium/ui/shell_dialogs/BUILD.gn13
-rw-r--r--chromium/ui/shell_dialogs/DEPS3
-rw-r--r--chromium/ui/shell_dialogs/OWNERS2
-rw-r--r--chromium/ui/shell_dialogs/run_all_unittests.cc4
-rw-r--r--chromium/ui/shell_dialogs/select_file_dialog.h15
-rw-r--r--chromium/ui/shell_dialogs/select_file_dialog_lacros.cc119
-rw-r--r--chromium/ui/shell_dialogs/select_file_dialog_lacros.h14
-rw-r--r--chromium/ui/shell_dialogs/selected_file_info.h3
-rw-r--r--chromium/ui/snapshot/BUILD.gn11
-rw-r--r--chromium/ui/snapshot/screenshot_grabber.cc6
-rw-r--r--chromium/ui/snapshot/snapshot_aura_unittest.cc4
-rw-r--r--chromium/ui/strings/translations/ui_strings_af.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_am.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ar.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_as.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_az.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_be.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_bg.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_bn.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_bs.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ca.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_cs.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_da.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_de.xtb11
-rw-r--r--chromium/ui/strings/translations/ui_strings_el.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_en-GB.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_es-419.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_es.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_et.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_eu.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_fa.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_fi.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_fil.xtb11
-rw-r--r--chromium/ui/strings/translations/ui_strings_fr-CA.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_fr.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_gl.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_gu.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_hi.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_hr.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_hu.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_hy.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_id.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_is.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_it.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_iw.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_ja.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ka.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_kk.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_km.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_kn.xtb11
-rw-r--r--chromium/ui/strings/translations/ui_strings_ko.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ky.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_lo.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_lt.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_lv.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_mk.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ml.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_mn.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_mr.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_ms.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_my.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ne.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_nl.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_no.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_or.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_pa.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_pl.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_pt-BR.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_pt-PT.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ro.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_ru.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_si.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_sk.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_sl.xtb11
-rw-r--r--chromium/ui/strings/translations/ui_strings_sq.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_sr-Latn.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_sr.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_sv.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_sw.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_ta.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_te.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_th.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_tr.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_uk.xtb11
-rw-r--r--chromium/ui/strings/translations/ui_strings_ur.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_uz.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_vi.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_zh-CN.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_zh-HK.xtb7
-rw-r--r--chromium/ui/strings/translations/ui_strings_zh-TW.xtb9
-rw-r--r--chromium/ui/strings/translations/ui_strings_zu.xtb7
-rw-r--r--chromium/ui/strings/ui_strings.grd25
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_APP_FULLSCREEN_KEY.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_CLIPBOARD.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_DELETE_ALL.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_IMAGE.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_RTF_CONTENT.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_WEB_SMART_PASTE.png.sha11
-rw-r--r--chromium/ui/strings/ui_strings_grd/IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE.png.sha11
-rw-r--r--chromium/ui/surface/BUILD.gn3
-rw-r--r--chromium/ui/touch_selection/BUILD.gn3
-rw-r--r--chromium/ui/touch_selection/touch_handle.cc31
-rw-r--r--chromium/ui/views/BUILD.gn83
-rw-r--r--chromium/ui/views/DEPS2
-rw-r--r--chromium/ui/views/OWNERS19
-rw-r--r--chromium/ui/views/PRESUBMIT.py4
-rw-r--r--chromium/ui/views/accessibility/accessibility_alert_window.cc2
-rw-r--r--chromium/ui/views/accessibility/accessibility_alert_window.h4
-rw-r--r--chromium/ui/views/accessibility/accessibility_alert_window_unittest.cc83
-rw-r--r--chromium/ui/views/accessibility/ax_tree_source_views.h2
-rw-r--r--chromium/ui/views/accessibility/ax_virtual_view.cc26
-rw-r--r--chromium/ui/views/accessibility/ax_virtual_view.h9
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc57
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate.h11
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc4
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc44
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc5
-rw-r--r--chromium/ui/views/accessibility/views_ax_tree_manager.h2
-rw-r--r--chromium/ui/views/accessible_pane_view_unittest.cc2
-rw-r--r--chromium/ui/views/animation/OWNERS1
-rw-r--r--chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc8
-rw-r--r--chromium/ui/views/animation/ink_drop_highlight_unittest.cc5
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view.cc30
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view.h15
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view_unittest.cc97
-rw-r--r--chromium/ui/views/animation/ink_drop_impl.cc16
-rw-r--r--chromium/ui/views/animation/ink_drop_impl_unittest.cc77
-rw-r--r--chromium/ui/views/animation/ink_drop_mask.cc48
-rw-r--r--chromium/ui/views/animation/ink_drop_mask.h40
-rw-r--r--chromium/ui/views/animation/ink_drop_mask_unittest.cc51
-rw-r--r--chromium/ui/views/animation/ink_drop_ripple_unittest.cc18
-rw-r--r--chromium/ui/views/animation/ink_drop_util.cc11
-rw-r--r--chromium/ui/views/animation/installable_ink_drop.cc19
-rw-r--r--chromium/ui/views/animation/installable_ink_drop.h6
-rw-r--r--chromium/ui/views/animation/square_ink_drop_ripple_unittest.cc9
-rw-r--r--chromium/ui/views/bubble/bubble_border.h2
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate_view.cc114
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate_view.h11
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc4
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_model_host.cc347
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_model_host.h88
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view.cc98
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view.h16
-rw-r--r--chromium/ui/views/bubble/info_bubble.cc20
-rw-r--r--chromium/ui/views/bubble/info_bubble.h11
-rw-r--r--chromium/ui/views/bubble/info_bubble_unittest.cc148
-rw-r--r--chromium/ui/views/cocoa/OWNERS2
-rw-r--r--chromium/ui/views/cocoa/drag_drop_client_mac.h4
-rw-r--r--chromium/ui/views/cocoa/drag_drop_client_mac.mm3
-rw-r--r--chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm6
-rw-r--r--chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h4
-rw-r--r--chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm15
-rw-r--r--chromium/ui/views/cocoa/tooltip_manager_mac.mm7
-rw-r--r--chromium/ui/views/controls/base_control_test_widget.cc40
-rw-r--r--chromium/ui/views/controls/base_control_test_widget.h41
-rw-r--r--chromium/ui/views/controls/button/button.cc100
-rw-r--r--chromium/ui/views/controls/button/button.h46
-rw-r--r--chromium/ui/views/controls/button/button_controller.cc20
-rw-r--r--chromium/ui/views/controls/button/button_controller.h1
-rw-r--r--chromium/ui/views/controls/button/button_observer.h28
-rw-r--r--chromium/ui/views/controls/button/button_unittest.cc249
-rw-r--r--chromium/ui/views/controls/button/checkbox.cc36
-rw-r--r--chromium/ui/views/controls/button/checkbox.h6
-rw-r--r--chromium/ui/views/controls/button/checkbox_unittest.cc3
-rw-r--r--chromium/ui/views/controls/button/image_button.cc22
-rw-r--r--chromium/ui/views/controls/button/image_button.h13
-rw-r--r--chromium/ui/views/controls/button/image_button_unittest.cc12
-rw-r--r--chromium/ui/views/controls/button/label_button.cc193
-rw-r--r--chromium/ui/views/controls/button/label_button.h48
-rw-r--r--chromium/ui/views/controls/button/label_button_label.cc4
-rw-r--r--chromium/ui/views/controls/button/label_button_label.h4
-rw-r--r--chromium/ui/views/controls/button/label_button_label_unittest.cc2
-rw-r--r--chromium/ui/views/controls/button/label_button_unittest.cc43
-rw-r--r--chromium/ui/views/controls/button/md_text_button.cc107
-rw-r--r--chromium/ui/views/controls/button/md_text_button.h14
-rw-r--r--chromium/ui/views/controls/button/md_text_button_unittest.cc57
-rw-r--r--chromium/ui/views/controls/button/menu_button.cc4
-rw-r--r--chromium/ui/views/controls/button/menu_button.h6
-rw-r--r--chromium/ui/views/controls/button/menu_button_controller.cc51
-rw-r--r--chromium/ui/views/controls/button/menu_button_controller.h7
-rw-r--r--chromium/ui/views/controls/button/menu_button_unittest.cc67
-rw-r--r--chromium/ui/views/controls/button/radio_button.h3
-rw-r--r--chromium/ui/views/controls/button/toggle_button.cc5
-rw-r--r--chromium/ui/views/controls/button/toggle_button.h3
-rw-r--r--chromium/ui/views/controls/button/toggle_button_unittest.cc3
-rw-r--r--chromium/ui/views/controls/combobox/combobox.cc55
-rw-r--r--chromium/ui/views/controls/combobox/combobox.h17
-rw-r--r--chromium/ui/views/controls/combobox/combobox_unittest.cc129
-rw-r--r--chromium/ui/views/controls/combobox/empty_combobox_model.cc30
-rw-r--r--chromium/ui/views/controls/combobox/empty_combobox_model.h30
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox.cc28
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox.h35
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox_listener.h28
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc114
-rw-r--r--chromium/ui/views/controls/focusable_border.cc1
-rw-r--r--chromium/ui/views/controls/highlight_path_generator.cc17
-rw-r--r--chromium/ui/views/controls/highlight_path_generator.h27
-rw-r--r--chromium/ui/views/controls/image_view.cc1
-rw-r--r--chromium/ui/views/controls/image_view_unittest.cc19
-rw-r--r--chromium/ui/views/controls/label.cc106
-rw-r--r--chromium/ui/views/controls/label.h22
-rw-r--r--chromium/ui/views/controls/label_unittest.cc104
-rw-r--r--chromium/ui/views/controls/link.cc2
-rw-r--r--chromium/ui/views/controls/link.h2
-rw-r--r--chromium/ui/views/controls/link_unittest.cc82
-rw-r--r--chromium/ui/views/controls/menu/menu_config.h24
-rw-r--r--chromium/ui/views/controls/menu/menu_config_mac.mm4
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.cc87
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.h11
-rw-r--r--chromium/ui/views/controls/menu/menu_controller_unittest.cc226
-rw-r--r--chromium/ui/views/controls/menu/menu_host.cc21
-rw-r--r--chromium/ui/views/controls/menu/menu_host.h2
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.cc84
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.h13
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl.cc30
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h4
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm170
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_unittest.cc13
-rw-r--r--chromium/ui/views/controls/menu/menu_separator.cc13
-rw-r--r--chromium/ui/views/controls/menu/menu_separator.h10
-rw-r--r--chromium/ui/views/controls/menu/menu_separator_unittest.cc35
-rw-r--r--chromium/ui/views/controls/menu/submenu_view.cc9
-rw-r--r--chromium/ui/views/controls/menu/submenu_view_unittest.cc6
-rw-r--r--chromium/ui/views/controls/menu/test_menu_item_view.cc2
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac_unittest.mm13
-rw-r--r--chromium/ui/views/controls/prefix_selector.cc18
-rw-r--r--chromium/ui/views/controls/prefix_selector.h5
-rw-r--r--chromium/ui/views/controls/resize_area_unittest.cc6
-rw-r--r--chromium/ui/views/controls/scroll_view.cc69
-rw-r--r--chromium/ui/views/controls/scroll_view.h2
-rw-r--r--chromium/ui/views/controls/scroll_view_unittest.cc12
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc6
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h7
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar_button_unittest.cc134
-rw-r--r--chromium/ui/views/controls/scrollbar/scroll_bar.cc12
-rw-r--r--chromium/ui/views/controls/scrollbar/scroll_bar.h3
-rw-r--r--chromium/ui/views/controls/scrollbar/scroll_bar_views.cc4
-rw-r--r--chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc94
-rw-r--r--chromium/ui/views/controls/slider.cc6
-rw-r--r--chromium/ui/views/controls/slider.h2
-rw-r--r--chromium/ui/views/controls/slider_unittest.cc24
-rw-r--r--chromium/ui/views/controls/styled_label.cc47
-rw-r--r--chromium/ui/views/controls/styled_label.h17
-rw-r--r--chromium/ui/views/controls/styled_label_unittest.cc12
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc8
-rw-r--r--chromium/ui/views/controls/table/table_view.cc10
-rw-r--r--chromium/ui/views/controls/table/table_view_unittest.cc4
-rw-r--r--chromium/ui/views/controls/textfield/textfield.cc160
-rw-r--r--chromium/ui/views/controls/textfield/textfield.h36
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.cc49
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.h25
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model_unittest.cc64
-rw-r--r--chromium/ui/views/controls/textfield/textfield_test_api.cc8
-rw-r--r--chromium/ui/views/controls/textfield/textfield_test_api.h3
-rw-r--r--chromium/ui/views/controls/textfield/textfield_unittest.cc391
-rw-r--r--chromium/ui/views/controls/tree/tree_view.cc4
-rw-r--r--chromium/ui/views/controls/tree/tree_view_unittest.cc17
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.cc4
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.h2
-rw-r--r--chromium/ui/views/controls/webview/BUILD.gn8
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.cc42
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.h11
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view_unittest.cc2
-rw-r--r--chromium/ui/views/controls/webview/webview.cc33
-rw-r--r--chromium/ui/views/controls/webview/webview.h17
-rw-r--r--chromium/ui/views/controls/webview/webview_unittest.cc19
-rw-r--r--chromium/ui/views/corewm/tooltip_aura.cc34
-rw-r--r--chromium/ui/views/corewm/tooltip_aura.h2
-rw-r--r--chromium/ui/views/drag_utils.h4
-rw-r--r--chromium/ui/views/drag_utils_aura.cc2
-rw-r--r--chromium/ui/views/drag_utils_mac.mm2
-rw-r--r--chromium/ui/views/examples/BUILD.gn16
-rw-r--r--chromium/ui/views/examples/DEPS9
-rw-r--r--chromium/ui/views/examples/ax_example.cc2
-rw-r--r--chromium/ui/views/examples/bubble_example.cc4
-rw-r--r--chromium/ui/views/examples/button_example.cc10
-rw-r--r--chromium/ui/views/examples/button_sticker_sheet.cc4
-rw-r--r--chromium/ui/views/examples/colored_dialog_example.cc4
-rw-r--r--chromium/ui/views/examples/create_examples.cc2
-rw-r--r--chromium/ui/views/examples/dialog_example.cc18
-rw-r--r--chromium/ui/views/examples/example_base.cc5
-rw-r--r--chromium/ui/views/examples/example_base.h5
-rw-r--r--chromium/ui/views/examples/examples_main_proc.cc27
-rw-r--r--chromium/ui/views/examples/examples_views_delegate_chromeos.cc46
-rw-r--r--chromium/ui/views/examples/examples_views_delegate_chromeos.h43
-rw-r--r--chromium/ui/views/examples/examples_window.cc6
-rw-r--r--chromium/ui/views/examples/examples_window.h5
-rw-r--r--chromium/ui/views/examples/examples_with_content_main.cc4
-rw-r--r--chromium/ui/views/examples/layout_example_base.cc2
-rw-r--r--chromium/ui/views/examples/login_bubble_dialog_example.cc (renamed from chromium/ui/views/examples/login_bubble_dialog.cc)29
-rw-r--r--chromium/ui/views/examples/login_bubble_dialog_example.h (renamed from chromium/ui/views/examples/login_bubble_dialog.h)8
-rw-r--r--chromium/ui/views/examples/menu_example.cc2
-rw-r--r--chromium/ui/views/examples/message_box_example.cc36
-rw-r--r--chromium/ui/views/examples/progress_bar_example.cc8
-rw-r--r--chromium/ui/views/examples/radio_button_example.cc26
-rw-r--r--chromium/ui/views/examples/scroll_view_example.cc42
-rw-r--r--chromium/ui/views/examples/tabbed_pane_example.cc39
-rw-r--r--chromium/ui/views/examples/table_example.cc68
-rw-r--r--chromium/ui/views/examples/tree_view_example.cc40
-rw-r--r--chromium/ui/views/examples/vector_example.cc5
-rw-r--r--chromium/ui/views/examples/webview_example.cc4
-rw-r--r--chromium/ui/views/examples/widget_example.cc21
-rw-r--r--chromium/ui/views/focus/external_focus_tracker.h6
-rw-r--r--chromium/ui/views/focus/focus_manager.cc8
-rw-r--r--chromium/ui/views/focus/focus_manager.h6
-rw-r--r--chromium/ui/views/focus/focus_manager_factory.cc7
-rw-r--r--chromium/ui/views/focus/focus_manager_factory.h7
-rw-r--r--chromium/ui/views/focus/focus_manager_unittest.cc2
-rw-r--r--chromium/ui/views/focus/focus_search.cc2
-rw-r--r--chromium/ui/views/focus/focus_search.h6
-rw-r--r--chromium/ui/views/focus/focus_traversal_unittest.cc35
-rw-r--r--chromium/ui/views/focus/widget_focus_manager.h13
-rw-r--r--chromium/ui/views/input_event_activation_protector.cc5
-rw-r--r--chromium/ui/views/layout/animating_layout_manager.cc61
-rw-r--r--chromium/ui/views/layout/animating_layout_manager.h9
-rw-r--r--chromium/ui/views/layout/animating_layout_manager_unittest.cc4
-rw-r--r--chromium/ui/views/layout/composite_layout_tests.cc1053
-rw-r--r--chromium/ui/views/layout/flex_layout.cc2
-rw-r--r--chromium/ui/views/layout/grid_layout.cc16
-rw-r--r--chromium/ui/views/layout/grid_layout.h8
-rw-r--r--chromium/ui/views/layout/layout_manager_base.cc4
-rw-r--r--chromium/ui/views/linux_ui/linux_ui.cc12
-rw-r--r--chromium/ui/views/linux_ui/linux_ui.h6
-rw-r--r--chromium/ui/views/linux_ui/window_button_order_provider.cc9
-rw-r--r--chromium/ui/views/metadata/metadata_unittest.cc4
-rw-r--r--chromium/ui/views/metadata/type_conversion.cc16
-rw-r--r--chromium/ui/views/repeat_controller.cc19
-rw-r--r--chromium/ui/views/repeat_controller.h25
-rw-r--r--chromium/ui/views/repeat_controller_unittest.cc107
-rw-r--r--chromium/ui/views/selection_controller.cc2
-rw-r--r--chromium/ui/views/selection_controller.h6
-rw-r--r--chromium/ui/views/selection_controller_unittest.cc5
-rw-r--r--chromium/ui/views/style/platform_style.cc7
-rw-r--r--chromium/ui/views/style/typography_provider.cc6
-rw-r--r--chromium/ui/views/style/typography_provider.h2
-rw-r--r--chromium/ui/views/view.cc22
-rw-r--r--chromium/ui/views/view.h42
-rw-r--r--chromium/ui/views/view_targeter_delegate.cc11
-rw-r--r--chromium/ui/views/view_targeter_unittest.cc25
-rw-r--r--chromium/ui/views/view_unittest.cc98
-rw-r--r--chromium/ui/views/views_delegate.cc9
-rw-r--r--chromium/ui/views/views_delegate.h10
-rw-r--r--chromium/ui/views/views_features.cc11
-rw-r--r--chromium/ui/views/views_test_suite.cc6
-rw-r--r--chromium/ui/views/views_test_suite.h8
-rw-r--r--chromium/ui/views/widget/ax_native_widget_mac_unittest.mm2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc420
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h172
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc663
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc105
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h33
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc62
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc15
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc16
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h7
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc11
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc6
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc16
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h2
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc250
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc7
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc26
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h3
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc13
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h3
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc134
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h60
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc276
-rw-r--r--chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc2
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc37
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc36
-rw-r--r--chromium/ui/views/widget/native_widget_aura.cc17
-rw-r--r--chromium/ui/views/widget/native_widget_aura.h6
-rw-r--r--chromium/ui/views/widget/native_widget_mac.h4
-rw-r--r--chromium/ui/views/widget/native_widget_mac.mm7
-rw-r--r--chromium/ui/views/widget/native_widget_mac_unittest.mm128
-rw-r--r--chromium/ui/views/widget/native_widget_private.h5
-rw-r--r--chromium/ui/views/widget/root_view.cc8
-rw-r--r--chromium/ui/views/widget/root_view_unittest.cc6
-rw-r--r--chromium/ui/views/widget/widget.cc64
-rw-r--r--chromium/ui/views/widget/widget.h19
-rw-r--r--chromium/ui/views/widget/widget_delegate.cc10
-rw-r--r--chromium/ui/views/widget/widget_delegate.h10
-rw-r--r--chromium/ui/views/widget/widget_interactive_uitest.cc51
-rw-r--r--chromium/ui/views/widget/widget_observer.h5
-rw-r--r--chromium/ui/views/widget/widget_unittest.cc59
-rw-r--r--chromium/ui/views/widget/window_reorderer.cc3
-rw-r--r--chromium/ui/views/win/fullscreen_handler.cc3
-rw-r--r--chromium/ui/views/win/hwnd_message_handler.cc7
-rw-r--r--chromium/ui/views/win/hwnd_message_handler_delegate.h2
-rw-r--r--chromium/ui/views/window/client_view.cc4
-rw-r--r--chromium/ui/views/window/client_view.h5
-rw-r--r--chromium/ui/views/window/custom_frame_view.cc28
-rw-r--r--chromium/ui/views/window/custom_frame_view.h22
-rw-r--r--chromium/ui/views/window/custom_frame_view_unittest.cc61
-rw-r--r--chromium/ui/views/window/dialog_client_view.cc2
-rw-r--r--chromium/ui/views/window/dialog_delegate.cc48
-rw-r--r--chromium/ui/views/window/dialog_delegate.h25
-rw-r--r--chromium/ui/views/window/dialog_delegate_unittest.cc6
-rw-r--r--chromium/ui/views/window/frame_background.cc3
-rw-r--r--chromium/ui/views/window/frame_caption_button.cc43
-rw-r--r--chromium/ui/views/window/frame_caption_button.h5
-rw-r--r--chromium/ui/views/window/non_client_view.cc12
-rw-r--r--chromium/ui/views/window/non_client_view.h11
-rw-r--r--chromium/ui/views/window/non_client_view_unittest.cc5
-rw-r--r--chromium/ui/views_content_client/BUILD.gn3
-rw-r--r--chromium/ui/views_content_client/views_content_client_main_parts.cc2
-rw-r--r--chromium/ui/views_content_client/views_content_client_main_parts.h2
-rw-r--r--chromium/ui/views_content_client/views_content_client_main_parts_aura.cc2
-rw-r--r--chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc2
-rw-r--r--chromium/ui/web_dialogs/BUILD.gn6
-rw-r--r--chromium/ui/web_dialogs/web_dialog_delegate.cc9
-rw-r--r--chromium/ui/web_dialogs/web_dialog_delegate.h26
-rw-r--r--chromium/ui/webui/PLATFORM_OWNERS3
-rw-r--r--chromium/ui/webui/resources/PRESUBMIT.py4
-rw-r--r--chromium/ui/webui/resources/cr_components/BUILD.gn2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn5
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn120
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html33
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js340
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js7
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.html35
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js348
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html3
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html9
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js9
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn218
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.html5
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.js (renamed from chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.js)8
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.html (renamed from chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.html)8
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js (renamed from chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.js)3
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html3
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js14
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html28
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js208
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.js2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config_select.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html35
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js16
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_icon.html2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js15
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_list.js4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.html8
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.js17
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_list_types.js2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.js4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js8
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_password_input.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.html6
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js2
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.html5
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js42
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network_health/BUILD.gn38
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html86
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js348
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/os_cr_components.gni22
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn63
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js4
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html8
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js5
-rw-r--r--chromium/ui/webui/resources/cr_components/cr_components_images.grdp7
-rw-r--r--chromium/ui/webui/resources/cr_components/cr_components_resources.grdp24
-rw-r--r--chromium/ui/webui/resources/cr_components/cr_components_resources_v3.grdp106
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/BUILD.gn64
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/OWNERS9
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/browser_proxy.js62
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/brush.svg1
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/colorize.svg1
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.html152
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.js197
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom83
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.html77
-rw-r--r--chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.js25
-rw-r--r--chromium/ui/webui/resources/cr_elements/BUILD.gn2
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/BUILD.gn2
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn88
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js7
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js24
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.js2
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni15
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html4
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_fingerprint/BUILD.gn2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_fingerprint/OWNERS1
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html42
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js87
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_grid/BUILD.gn21
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.html21
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.js88
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.html2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js98
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_radio_button/BUILD.gn31
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html68
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js16
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js34
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js6
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.js7
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html3
-rw-r--r--chromium/ui/webui/resources/cr_elements/icons.html18
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/BUILD.gn26
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.html3
-rw-r--r--chromium/ui/webui/resources/cr_elements/shared_vars_css.html13
-rw-r--r--chromium/ui/webui/resources/cr_elements_resources.grdp20
-rw-r--r--chromium/ui/webui/resources/cr_elements_resources_v3.grdp33
-rw-r--r--chromium/ui/webui/resources/css/cros_colors.json5160
-rw-r--r--chromium/ui/webui/resources/css/cros_palette.json568
-rw-r--r--chromium/ui/webui/resources/js/color_utils.js38
-rw-r--r--chromium/ui/webui/resources/js/cr/ui/bubble.js21
-rw-r--r--chromium/ui/webui/resources/js/cr/ui/tree.js17
-rw-r--r--chromium/ui/webui/resources/js/i18n_template_no_process.js6
-rw-r--r--chromium/ui/webui/resources/js/parse_html_subset.js21
-rw-r--r--chromium/ui/webui/resources/js/util.js7
-rw-r--r--chromium/ui/webui/resources/js/webui_resource_test.js9
-rw-r--r--chromium/ui/webui/resources/polymer_resources.grdp20
-rw-r--r--chromium/ui/webui/resources/webui_resources.grd7
-rw-r--r--chromium/ui/webui/webui_allowlist.cc7
-rw-r--r--chromium/ui/webui/webui_allowlist.h16
-rw-r--r--chromium/ui/webui/webui_features.gni8
-rw-r--r--chromium/ui/wm/BUILD.gn9
-rw-r--r--chromium/ui/wm/core/shadow_controller.cc2
-rw-r--r--chromium/ui/wm/core/window_animations.cc43
-rw-r--r--chromium/ui/wm/public/BUILD.gn4
1498 files changed, 36736 insertions, 19900 deletions
diff --git a/chromium/ui/accelerated_widget_mac/BUILD.gn b/chromium/ui/accelerated_widget_mac/BUILD.gn
index 77848c10036..b9795661986 100644
--- a/chromium/ui/accelerated_widget_mac/BUILD.gn
+++ b/chromium/ui/accelerated_widget_mac/BUILD.gn
@@ -39,7 +39,7 @@ component("accelerated_widget_mac") {
"//ui/gl",
]
- libs = [
+ frameworks = [
"AVFoundation.framework",
"CoreGraphics.framework",
"Foundation.framework",
@@ -65,7 +65,7 @@ test("accelerated_widget_mac_unittests") {
"//ui/gfx:test_support",
"//ui/gl",
]
- libs = [
+ frameworks = [
"AVFoundation.framework",
"QuartzCore.framework",
]
diff --git a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index 96496079c7b..3a3481198f7 100644
--- a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -169,7 +169,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree {
const gfx::RectF& contents_rect,
const gfx::Rect& rect,
unsigned background_color,
- bool has_hdr_color_space,
+ const gfx::ColorSpace& color_space,
unsigned edge_aa_mask,
float opacity,
unsigned filter);
@@ -191,6 +191,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree {
gfx::RectF contents_rect;
gfx::RectF rect;
unsigned background_color = 0;
+ // The color space of |io_surface|. Used for HDR tonemapping.
+ gfx::ColorSpace io_surface_color_space;
// Note that the CoreAnimation edge antialiasing mask is not the same as
// the edge antialiasing mask passed to the constructor.
CAEdgeAntialiasingMask ca_edge_aa_mask = 0;
diff --git a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index f66002644bf..2b3c3548368 100644
--- a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -385,7 +385,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(
const gfx::RectF& contents_rect,
const gfx::Rect& rect_in,
unsigned background_color,
- bool has_hdr_color_space,
+ const gfx::ColorSpace& io_surface_color_space,
unsigned edge_aa_mask,
float opacity,
unsigned filter)
@@ -394,6 +394,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(
contents_rect(contents_rect),
rect(rect_in),
background_color(background_color),
+ io_surface_color_space(io_surface_color_space),
ca_edge_aa_mask(0),
opacity(opacity),
ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) {
@@ -439,15 +440,12 @@ CARendererLayerTree::ContentLayer::ContentLayer(
}
// Determine which type of CALayer subclass we should use.
- if (io_surface) {
+ if (metal::ShouldUseHDRCopier(io_surface, io_surface_color_space)) {
+ type = CALayerType::kHDRCopier;
+ } else if (io_surface) {
switch (IOSurfaceGetPixelFormat(io_surface)) {
- case kCVPixelFormatType_64RGBAHalf:
- case kCVPixelFormatType_ARGB2101010LEPacked:
- // HDR content can come in either as half-float or as 10-10-10-2.
- if (has_hdr_color_space)
- type = CALayerType::kHDRCopier;
- break;
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
// Only allow 4:2:0 frames which fill the layer's contents to be
// promoted to AV layers.
if (tree->allow_av_sample_buffer_display_layer_ &&
@@ -495,6 +493,7 @@ CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer)
contents_rect(layer.contents_rect),
rect(layer.rect),
background_color(layer.background_color),
+ io_surface_color_space(layer.io_surface_color_space),
ca_edge_aa_mask(layer.ca_edge_aa_mask),
opacity(layer.opacity),
ca_filter(layer.ca_filter),
@@ -570,7 +569,7 @@ void CARendererLayerTree::TransformLayer::AddContentLayer(
const CARendererLayerParams& params) {
base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer;
- bool has_hdr_color_space = false;
+ gfx::ColorSpace io_surface_color_space;
if (params.image) {
gl::GLImageIOSurface* io_surface_image =
gl::GLImageIOSurface::FromGLImage(params.image);
@@ -584,11 +583,11 @@ void CARendererLayerTree::TransformLayer::AddContentLayer(
// TODO(ccameron): If this indeed causes the bug to disappear, then
// extirpate the CVPixelBufferRef path.
// cv_pixel_buffer = io_surface_image->cv_pixel_buffer();
- has_hdr_color_space = params.image->color_space().IsHDR();
+ io_surface_color_space = params.image->color_space();
}
content_layers.push_back(
ContentLayer(tree, io_surface, cv_pixel_buffer, params.contents_rect,
- params.rect, params.background_color, has_hdr_color_space,
+ params.rect, params.background_color, io_surface_color_space,
params.edge_aa_mask, params.opacity, params.filter));
}
@@ -808,7 +807,10 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
switch (type) {
case CALayerType::kHDRCopier:
- [ca_layer setContents:static_cast<id>(io_surface.get())];
+ if (update_contents) {
+ metal::UpdateHDRCopierLayer(ca_layer.get(), io_surface.get(),
+ io_surface_color_space);
+ }
break;
case CALayerType::kVideo:
if (update_contents) {
diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn
index 0297bff6f13..03aa4c67a7a 100644
--- a/chromium/ui/accessibility/BUILD.gn
+++ b/chromium/ui/accessibility/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
import("//mojo/public/tools/bindings/mojom.gni")
@@ -41,7 +40,7 @@ mojom_component("ax_enums_mojo") {
# included by Blink. The rule of thumb (for now) is that it's
# anything platform-neutral (no platform/ directory) that
# relates to a single accessibility node (no trees, etc.).
-jumbo_component("ax_base") {
+component("ax_base") {
defines = [ "AX_BASE_IMPLEMENTATION" ]
sources = [
@@ -64,6 +63,8 @@ jumbo_component("ax_base") {
"ax_relative_bounds.h",
"ax_role_properties.cc",
"ax_role_properties.h",
+ "ax_tree_id.cc",
+ "ax_tree_id.h",
]
public_deps = [
@@ -71,6 +72,7 @@ jumbo_component("ax_base") {
":ax_enums_mojo",
"//base",
"//base:i18n",
+ "//base/util/values:values_util",
"//ui/base",
"//ui/gfx",
"//ui/gfx/geometry",
@@ -86,7 +88,7 @@ jumbo_component("ax_base") {
# }
#}
-jumbo_component("accessibility") {
+component("accessibility") {
defines = [ "AX_IMPLEMENTATION" ]
sources = [
@@ -94,6 +96,8 @@ jumbo_component("accessibility") {
"ax_action_data.h",
"ax_action_handler.cc",
"ax_action_handler.h",
+ "ax_action_handler_base.cc",
+ "ax_action_handler_base.h",
"ax_action_target.h",
"ax_active_popup.cc",
"ax_active_popup.h",
@@ -127,8 +131,6 @@ jumbo_component("accessibility") {
"ax_tree_combiner.h",
"ax_tree_data.cc",
"ax_tree_data.h",
- "ax_tree_id.cc",
- "ax_tree_id.h",
"ax_tree_id_registry.cc",
"ax_tree_id_registry.h",
"ax_tree_manager.h",
diff --git a/chromium/ui/accessibility/accessibility_features.cc b/chromium/ui/accessibility/accessibility_features.cc
index 4a52188c1b6..9702d43f50c 100644
--- a/chromium/ui/accessibility/accessibility_features.cc
+++ b/chromium/ui/accessibility/accessibility_features.cc
@@ -37,6 +37,16 @@ bool IsAccessibilityExposeHTMLElementEnabled() {
::features::kEnableAccessibilityExposeHTMLElement);
}
+// Enable language detection to determine language used in page text, exposed
+// on the browser process AXTree.
+const base::Feature kEnableAccessibilityLanguageDetection{
+ "AccessibilityLanguageDetection", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsAccessibilityLanguageDetectionEnabled() {
+ return base::FeatureList::IsEnabled(
+ ::features::kEnableAccessibilityLanguageDetection);
+}
+
// Serializes accessibility information from the Views tree and deserializes it
// into an AXTree in the browser process.
const base::Feature kEnableAccessibilityTreeForViews{
@@ -48,7 +58,7 @@ bool IsAccessibilityTreeForViewsEnabled() {
}
const base::Feature kAccessibilityFocusHighlight{
- "AccessibilityFocusHighlight", base::FEATURE_DISABLED_BY_DEFAULT};
+ "AccessibilityFocusHighlight", base::FEATURE_ENABLED_BY_DEFAULT};
bool IsAccessibilityFocusHighlightEnabled() {
return base::FeatureList::IsEnabled(::features::kAccessibilityFocusHighlight);
@@ -64,8 +74,8 @@ bool IsIChromeAccessibleEnabled() {
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
-const base::Feature kAccessibilityCursorColor{
- "AccessibilityCursorColor", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAccessibilityCursorColor{"AccessibilityCursorColor",
+ base::FEATURE_ENABLED_BY_DEFAULT};
bool IsAccessibilityCursorColorEnabled() {
return base::FeatureList::IsEnabled(::features::kAccessibilityCursorColor);
diff --git a/chromium/ui/accessibility/accessibility_features.h b/chromium/ui/accessibility/accessibility_features.h
index 743a32c5bc7..d6f7a2d31e9 100644
--- a/chromium/ui/accessibility/accessibility_features.h
+++ b/chromium/ui/accessibility/accessibility_features.h
@@ -30,6 +30,12 @@ AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityExposeHTMLElement;
// browser process AXTree (as an ignored node).
AX_BASE_EXPORT bool IsAccessibilityExposeHTMLElementEnabled();
+AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityLanguageDetection;
+
+// Return true if language detection should be used to determine the language
+// of text content in page and exposed to the browser process AXTree.
+AX_BASE_EXPORT bool IsAccessibilityLanguageDetectionEnabled();
+
// Serializes accessibility information from the Views tree and deserializes it
// into an AXTree in the browser process.
AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityTreeForViews;
diff --git a/chromium/ui/accessibility/accessibility_switches.cc b/chromium/ui/accessibility/accessibility_switches.cc
index 334319dac24..f7feae3aa75 100644
--- a/chromium/ui/accessibility/accessibility_switches.cc
+++ b/chromium/ui/accessibility/accessibility_switches.cc
@@ -52,6 +52,10 @@ const char kDisableExperimentalAccessibilityChromeVoxSearchMenus[] =
const char kEnableExperimentalAccessibilityChromeVoxTutorial[] =
"enable-experimental-accessibility-chromevox-tutorial";
+// Enables Switch Access point scanning. This feature hasn't launched yet.
+const char kEnableSwitchAccessPointScanning[] =
+ "enable-switch-access-point-scanning";
+
bool IsExperimentalAccessibilityLanguageDetectionEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
@@ -67,6 +71,11 @@ bool IsExperimentalAccessibilitySwitchAccessTextEnabled() {
::switches::kEnableExperimentalAccessibilitySwitchAccessText);
}
+bool IsSwitchAccessPointScanningEnabled() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kEnableSwitchAccessPointScanning);
+}
+
#if defined(OS_WIN)
// Enables UI Automation platform API in addition to the IAccessible API.
const char kEnableExperimentalUIAutomation[] =
@@ -82,4 +91,8 @@ bool IsExperimentalAccessibilityPlatformUIAEnabled() {
#endif
}
+// Optionally disable AXMenuList, which makes the internal pop-up menu
+// UI for a select element directly accessible.
+const char kDisableAXMenuList[] = "disable-ax-menu-list";
+
} // namespace switches
diff --git a/chromium/ui/accessibility/accessibility_switches.h b/chromium/ui/accessibility/accessibility_switches.h
index 2d3b9f2761b..add766766e2 100644
--- a/chromium/ui/accessibility/accessibility_switches.h
+++ b/chromium/ui/accessibility/accessibility_switches.h
@@ -29,6 +29,7 @@ AX_BASE_EXPORT extern const char
kDisableExperimentalAccessibilityChromeVoxSearchMenus[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityChromeVoxTutorial[];
+AX_BASE_EXPORT extern const char kEnableSwitchAccessPointScanning[];
// Returns true if experimental accessibility language detection is enabled.
AX_BASE_EXPORT bool IsExperimentalAccessibilityLanguageDetectionEnabled();
@@ -48,6 +49,13 @@ AX_BASE_EXPORT extern const char kEnableExperimentalUIAutomation[];
// Returns true if experimental support for UIAutomation is enabled.
AX_BASE_EXPORT bool IsExperimentalAccessibilityPlatformUIAEnabled();
+// Returns true if Switch Access point scanning is enabled.
+AX_BASE_EXPORT bool IsSwitchAccessPointScanningEnabled();
+
+// Optionally disable AXMenuList, which makes the internal pop-up menu
+// UI for a select element directly accessible.
+AX_BASE_EXPORT extern const char kDisableAXMenuList[];
+
} // namespace switches
#endif // UI_ACCESSIBILITY_ACCESSIBILITY_SWITCHES_H_
diff --git a/chromium/ui/accessibility/ax_action_handler.cc b/chromium/ui/accessibility/ax_action_handler.cc
index a570205cab8..3ac9ce81c3f 100644
--- a/chromium/ui/accessibility/ax_action_handler.cc
+++ b/chromium/ui/accessibility/ax_action_handler.cc
@@ -8,15 +8,8 @@
namespace ui {
-bool AXActionHandler::RequiresPerformActionPointInPixels() const {
- return false;
-}
-
AXActionHandler::AXActionHandler()
- : tree_id_(AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(this)) {}
-
-AXActionHandler::~AXActionHandler() {
- AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id_);
-}
+ : AXActionHandlerBase(
+ AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(this)) {}
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_action_handler.h b/chromium/ui/accessibility/ax_action_handler.h
index 0374ca1ce99..d999a0e35d6 100644
--- a/chromium/ui/accessibility/ax_action_handler.h
+++ b/chromium/ui/accessibility/ax_action_handler.h
@@ -5,43 +5,20 @@
#ifndef UI_ACCESSIBILITY_AX_ACTION_HANDLER_H_
#define UI_ACCESSIBILITY_AX_ACTION_HANDLER_H_
+#include "ui/accessibility/ax_action_handler_base.h"
#include "ui/accessibility/ax_export.h"
-#include "ui/accessibility/ax_tree_id.h"
namespace ui {
-struct AXActionData;
-
-// Classes that host an accessibility tree in the browser process that also wish
-// to become visible to accessibility clients (e.g. for relaying targets to
-// source accessibility trees), can subclass this class.
+// The class you normally want to inherit from other classes when you want to
+// make them visible to accessibility clients, since it automatically registers
+// a valid AXTreeID with the AXTreeIDRegistry when constructing the instance.
//
-// Subclasses can use |tree_id| when annotating their |AXNodeData| for clients
-// to respond with the appropriate target node id.
-class AX_EXPORT AXActionHandler {
- public:
- virtual ~AXActionHandler();
-
- // Handle an action from an accessibility client.
- virtual void PerformAction(const AXActionData& data) = 0;
-
- // Returns whether this handler expects points in pixels (true) or dips
- // (false) for data passed to |PerformAction|.
- virtual bool RequiresPerformActionPointInPixels() const;
-
- // A tree id appropriate for annotating events sent to an accessibility
- // client.
- const AXTreeID& ax_tree_id() const { return tree_id_; }
-
+// If you need more control over how the AXTreeID associated to this class is
+// set, please inherit directly from AXActionHandlerBase instead.
+class AX_EXPORT AXActionHandler : public AXActionHandlerBase {
protected:
AXActionHandler();
-
- private:
- // Register or unregister this class with |AXTreeIDRegistry|.
- void UpdateActiveState(bool active);
-
- // Automatically assigned.
- AXTreeID tree_id_;
};
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_action_handler_base.cc b/chromium/ui/accessibility/ax_action_handler_base.cc
new file mode 100644
index 00000000000..43edc1e21e7
--- /dev/null
+++ b/chromium/ui/accessibility/ax_action_handler_base.cc
@@ -0,0 +1,32 @@
+// 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 "ui/accessibility/ax_action_handler_base.h"
+
+#include "ui/accessibility/ax_tree_id_registry.h"
+
+namespace ui {
+
+bool AXActionHandlerBase::RequiresPerformActionPointInPixels() const {
+ return false;
+}
+
+AXActionHandlerBase::AXActionHandlerBase()
+ : AXActionHandlerBase(ui::AXTreeIDUnknown()) {}
+
+AXActionHandlerBase::AXActionHandlerBase(const AXTreeID& ax_tree_id)
+ : tree_id_(ax_tree_id) {}
+
+AXActionHandlerBase::~AXActionHandlerBase() {
+ AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id_);
+}
+
+void AXActionHandlerBase::SetAXTreeID(AXTreeID new_ax_tree_id) {
+ DCHECK_NE(new_ax_tree_id, ui::AXTreeIDUnknown());
+ AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id_);
+ tree_id_ = new_ax_tree_id;
+ AXTreeIDRegistry::GetInstance()->SetAXTreeID(tree_id_, this);
+}
+
+} // namespace ui
diff --git a/chromium/ui/accessibility/ax_action_handler_base.h b/chromium/ui/accessibility/ax_action_handler_base.h
new file mode 100644
index 00000000000..2e2b5b09d91
--- /dev/null
+++ b/chromium/ui/accessibility/ax_action_handler_base.h
@@ -0,0 +1,60 @@
+// 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 UI_ACCESSIBILITY_AX_ACTION_HANDLER_BASE_H_
+#define UI_ACCESSIBILITY_AX_ACTION_HANDLER_BASE_H_
+
+#include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/ax_tree_id.h"
+
+namespace ui {
+
+struct AXActionData;
+
+// Classes that host an accessibility tree in the browser process that also wish
+// to become visible to accessibility clients (e.g. for relaying targets to
+// source accessibility trees), can subclass this class. However, unless you
+// need to have more control over how |tree_id_| is set, most classes will want
+// to inherit from AXActionHandler instead, which manages it automatically.
+//
+// Subclasses can use |tree_id| when annotating their |AXNodeData| for clients
+// to respond with the appropriate target node id.
+class AX_EXPORT AXActionHandlerBase {
+ public:
+ virtual ~AXActionHandlerBase();
+
+ // Handle an action from an accessibility client.
+ virtual void PerformAction(const AXActionData& data) = 0;
+
+ // Returns whether this handler expects points in pixels (true) or dips
+ // (false) for data passed to |PerformAction|.
+ virtual bool RequiresPerformActionPointInPixels() const;
+
+ // A tree id appropriate for annotating events sent to an accessibility
+ // client.
+ const AXTreeID& ax_tree_id() const { return tree_id_; }
+
+ protected:
+ // Initializes the AXActionHandlerBase subclass with ui::AXTreeIDUnknown().
+ AXActionHandlerBase();
+
+ // Initializes the AXActionHandlerBase subclass with |ax_tree_id|. It is Ok to
+ // pass ui::AXTreeIDUnknown() and then call SetAXTreeID() at a later point.
+ explicit AXActionHandlerBase(const AXTreeID& ax_tree_id);
+
+ // Change the AXTreeID.
+ void SetAXTreeID(AXTreeID new_ax_tree_id);
+
+ private:
+ // Register or unregister this class with |AXTreeIDRegistry|.
+ void UpdateActiveState(bool active);
+
+ // Manually set in this base class, but automatically set by instances of the
+ // subclass AXActionHandler, which most classes inherit from.
+ AXTreeID tree_id_;
+};
+
+} // namespace ui
+
+#endif // UI_ACCESSIBILITY_AX_ACTION_HANDLER_BASE_H_
diff --git a/chromium/ui/accessibility/ax_assistant_structure.cc b/chromium/ui/accessibility/ax_assistant_structure.cc
index 0a7b7c5b86a..a1b09d27045 100644
--- a/chromium/ui/accessibility/ax_assistant_structure.cc
+++ b/chromium/ui/accessibility/ax_assistant_structure.cc
@@ -134,8 +134,7 @@ base::string16 GetValue(const AXNode* node, bool show_password) {
bool HasOnlyTextAndImageChildren(const AXNode* node) {
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
- if (child->data().role != ax::mojom::Role::kStaticText &&
- child->data().role != ax::mojom::Role::kImage) {
+ if (!child->IsText() && !ui::IsImage(child->data().role)) {
return false;
}
}
@@ -430,7 +429,6 @@ const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent) {
case ax::mojom::Role::kInputTime:
return kAXSpinnerClassname;
case ax::mojom::Role::kButton:
- case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kPdfActionableHighlight:
return kAXButtonClassname;
case ax::mojom::Role::kCheckBox:
@@ -461,6 +459,7 @@ const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent) {
return kAXDialogClassname;
case ax::mojom::Role::kRootWebArea:
return has_parent ? kAXViewClassname : kAXWebViewClassname;
+ case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
diff --git a/chromium/ui/accessibility/ax_enum_util.cc b/chromium/ui/accessibility/ax_enum_util.cc
index 6d592227fc2..e96bd1bab6c 100644
--- a/chromium/ui/accessibility/ax_enum_util.cc
+++ b/chromium/ui/accessibility/ax_enum_util.cc
@@ -1662,6 +1662,8 @@ const char* ToString(ax::mojom::IntAttribute int_attribute) {
return "checkedState";
case ax::mojom::IntAttribute::kListStyle:
return "listStyle";
+ case ax::mojom::IntAttribute::kTextAlign:
+ return "text-align";
case ax::mojom::IntAttribute::kTextDirection:
return "textDirection";
case ax::mojom::IntAttribute::kTextPosition:
@@ -1788,6 +1790,8 @@ ax::mojom::IntAttribute ParseIntAttribute(const char* int_attribute) {
return ax::mojom::IntAttribute::kCheckedState;
if (0 == strcmp(int_attribute, "listStyle"))
return ax::mojom::IntAttribute::kListStyle;
+ if (0 == strcmp(int_attribute, "text-align"))
+ return ax::mojom::IntAttribute::kTextAlign;
if (0 == strcmp(int_attribute, "textDirection"))
return ax::mojom::IntAttribute::kTextDirection;
if (0 == strcmp(int_attribute, "textPosition"))
@@ -1878,6 +1882,8 @@ const char* ToString(ax::mojom::BoolAttribute bool_attribute) {
return "clickable";
case ax::mojom::BoolAttribute::kClipsChildren:
return "clipsChildren";
+ case ax::mojom::BoolAttribute::kNotUserSelectableStyle:
+ return "notUserSelectableStyle";
case ax::mojom::BoolAttribute::kSelected:
return "selected";
case ax::mojom::BoolAttribute::kSelectedFromFocus:
@@ -1922,6 +1928,8 @@ ax::mojom::BoolAttribute ParseBoolAttribute(const char* bool_attribute) {
return ax::mojom::BoolAttribute::kClickable;
if (0 == strcmp(bool_attribute, "clipsChildren"))
return ax::mojom::BoolAttribute::kClipsChildren;
+ if (0 == strcmp(bool_attribute, "notUserSelectableStyle"))
+ return ax::mojom::BoolAttribute::kNotUserSelectableStyle;
if (0 == strcmp(bool_attribute, "selected"))
return ax::mojom::BoolAttribute::kSelected;
if (0 == strcmp(bool_attribute, "selectedFromFocus"))
@@ -2314,35 +2322,66 @@ ax::mojom::TextDecorationStyle ParseTextDecorationStyle(
return ax::mojom::TextDecorationStyle::kNone;
}
-const char* ToString(ax::mojom::TextDirection text_direction) {
+const char* ToString(ax::mojom::TextAlign text_align) {
+ switch (text_align) {
+ case ax::mojom::TextAlign::kNone:
+ return "none";
+ case ax::mojom::TextAlign::kLeft:
+ return "left";
+ case ax::mojom::TextAlign::kRight:
+ return "right";
+ case ax::mojom::TextAlign::kCenter:
+ return "center";
+ case ax::mojom::TextAlign::kJustify:
+ return "justify";
+ }
+
+ return "";
+}
+
+ax::mojom::TextAlign ParseTextAlign(const char* text_align) {
+ if (0 == strcmp(text_align, "none"))
+ return ax::mojom::TextAlign::kNone;
+ if (0 == strcmp(text_align, "left"))
+ return ax::mojom::TextAlign::kLeft;
+ if (0 == strcmp(text_align, "right"))
+ return ax::mojom::TextAlign::kRight;
+ if (0 == strcmp(text_align, "center"))
+ return ax::mojom::TextAlign::kCenter;
+ if (0 == strcmp(text_align, "justify"))
+ return ax::mojom::TextAlign::kJustify;
+ return ax::mojom::TextAlign::kNone;
+}
+
+const char* ToString(ax::mojom::WritingDirection text_direction) {
switch (text_direction) {
- case ax::mojom::TextDirection::kNone:
+ case ax::mojom::WritingDirection::kNone:
return "none";
- case ax::mojom::TextDirection::kLtr:
+ case ax::mojom::WritingDirection::kLtr:
return "ltr";
- case ax::mojom::TextDirection::kRtl:
+ case ax::mojom::WritingDirection::kRtl:
return "rtl";
- case ax::mojom::TextDirection::kTtb:
+ case ax::mojom::WritingDirection::kTtb:
return "ttb";
- case ax::mojom::TextDirection::kBtt:
+ case ax::mojom::WritingDirection::kBtt:
return "btt";
}
return "";
}
-ax::mojom::TextDirection ParseTextDirection(const char* text_direction) {
+ax::mojom::WritingDirection ParseTextDirection(const char* text_direction) {
if (0 == strcmp(text_direction, "none"))
- return ax::mojom::TextDirection::kNone;
+ return ax::mojom::WritingDirection::kNone;
if (0 == strcmp(text_direction, "ltr"))
- return ax::mojom::TextDirection::kLtr;
+ return ax::mojom::WritingDirection::kLtr;
if (0 == strcmp(text_direction, "rtl"))
- return ax::mojom::TextDirection::kRtl;
+ return ax::mojom::WritingDirection::kRtl;
if (0 == strcmp(text_direction, "ttb"))
- return ax::mojom::TextDirection::kTtb;
+ return ax::mojom::WritingDirection::kTtb;
if (0 == strcmp(text_direction, "btt"))
- return ax::mojom::TextDirection::kBtt;
- return ax::mojom::TextDirection::kNone;
+ return ax::mojom::WritingDirection::kBtt;
+ return ax::mojom::WritingDirection::kNone;
}
const char* ToString(ax::mojom::TextPosition text_position) {
@@ -2754,6 +2793,8 @@ const char* ToString(ax::mojom::Gesture gesture) {
return "tap3";
case ax::mojom::Gesture::kTap4:
return "tap4";
+ case ax::mojom::Gesture::kTouchExplore:
+ return "touchExplore";
}
return "";
@@ -2802,6 +2843,8 @@ ax::mojom::Gesture ParseGesture(const char* gesture) {
return ax::mojom::Gesture::kTap3;
if (0 == strcmp(gesture, "tap4"))
return ax::mojom::Gesture::kTap4;
+ if (0 == strcmp(gesture, "touchExplore"))
+ return ax::mojom::Gesture::kTouchExplore;
return ax::mojom::Gesture::kNone;
}
diff --git a/chromium/ui/accessibility/ax_enum_util.h b/chromium/ui/accessibility/ax_enum_util.h
index d64fea15a8f..154a26e8ba6 100644
--- a/chromium/ui/accessibility/ax_enum_util.h
+++ b/chromium/ui/accessibility/ax_enum_util.h
@@ -109,9 +109,13 @@ AX_BASE_EXPORT const char* ToString(
AX_BASE_EXPORT ax::mojom::TextDecorationStyle ParseTextDecorationStyle(
const char* text_decoration_style);
-// ax::mojom::TextDirection
-AX_BASE_EXPORT const char* ToString(ax::mojom::TextDirection text_direction);
-AX_BASE_EXPORT ax::mojom::TextDirection ParseTextDirection(
+// ax::mojom::TextAlign
+AX_BASE_EXPORT const char* ToString(ax::mojom::TextAlign text_align);
+AX_BASE_EXPORT ax::mojom::TextAlign ParseTextAlign(const char* text_align);
+
+// ax::mojom::WritingDirection
+AX_BASE_EXPORT const char* ToString(ax::mojom::WritingDirection text_direction);
+AX_BASE_EXPORT ax::mojom::WritingDirection ParseTextDirection(
const char* text_direction);
// ax::mojom::TextPosition
diff --git a/chromium/ui/accessibility/ax_enum_util_unittest.cc b/chromium/ui/accessibility/ax_enum_util_unittest.cc
index fd09b715113..b0b0c7d35db 100644
--- a/chromium/ui/accessibility/ax_enum_util_unittest.cc
+++ b/chromium/ui/accessibility/ax_enum_util_unittest.cc
@@ -164,12 +164,16 @@ TEST(AXEnumUtilTest, Command) {
TestEnumStringConversion<ax::mojom::Command>(ParseCommand);
}
+TEST(AXEnumUtilTest, TextAlign) {
+ TestEnumStringConversion<ax::mojom::TextAlign>(ParseTextAlign);
+}
+
TEST(AXEnumUtilTest, TextBoundary) {
TestEnumStringConversion<ax::mojom::TextBoundary>(ParseTextBoundary);
}
TEST(AXEnumUtilTest, TextDirection) {
- TestEnumStringConversion<ax::mojom::TextDirection>(ParseTextDirection);
+ TestEnumStringConversion<ax::mojom::WritingDirection>(ParseTextDirection);
}
TEST(AXEnumUtilTest, TextPosition) {
diff --git a/chromium/ui/accessibility/ax_enums.mojom b/chromium/ui/accessibility/ax_enums.mojom
index b1523555d3b..9a922b04bbe 100644
--- a/chromium/ui/accessibility/ax_enums.mojom
+++ b/chromium/ui/accessibility/ax_enums.mojom
@@ -633,6 +633,9 @@ enum IntAttribute {
// The list style type. Only available on list items.
kListStyle,
+ // Specifies the alignment of the text, e.g. left, center, right, justify
+ kTextAlign,
+
// Specifies the direction of the text, e.g., right-to-left.
kTextDirection,
@@ -730,6 +733,12 @@ enum BoolAttribute {
// overflow: hidden or clip children by default.
kClipsChildren,
+ // Indicates that this node is not selectable because the style has
+ // user-select: none. Note that there may be other reasons why a node is
+ // not selectable - for example, bullets in a list. However, this attribute
+ // is only set on user-select: none.
+ kNotUserSelectableStyle,
+
// Indicates whether this node is selected or unselected.
kSelected,
@@ -894,7 +903,16 @@ enum TextBoundary {
kNone = kObject
};
-enum TextDirection {
+// Types of text alignment according to the IAccessible2 Object Attributes spec.
+enum TextAlign {
+ kNone,
+ kLeft,
+ kRight,
+ kCenter,
+ kJustify,
+};
+
+enum WritingDirection {
kNone,
kLtr,
kRtl,
@@ -1035,6 +1053,7 @@ enum Gesture {
kTap2,
kTap3,
kTap4,
+ kTouchExplore,
};
enum TextAffinity {
diff --git a/chromium/ui/accessibility/ax_event_generator.cc b/chromium/ui/accessibility/ax_event_generator.cc
index 7561e537be6..f06d801fd7b 100644
--- a/chromium/ui/accessibility/ax_event_generator.cc
+++ b/chromium/ui/accessibility/ax_event_generator.cc
@@ -47,6 +47,19 @@ void RemoveEvent(std::set<AXEventGenerator::EventParams>* node_events,
}
}
+// If a node toggled its ignored state, don't also fire children-changed because
+// platforms likely will do that in response to ignored-changed.
+// Suppress name- and description-changed because those can be emitted as a side
+// effect of calculating alternative text values for a newly-displayed object.
+// Ditto for text attributes such as foreground and background colors.
+void RemoveEventsDueToIgnoredChanged(
+ std::set<AXEventGenerator::EventParams>* node_events) {
+ RemoveEvent(node_events, AXEventGenerator::Event::CHILDREN_CHANGED);
+ RemoveEvent(node_events, AXEventGenerator::Event::DESCRIPTION_CHANGED);
+ RemoveEvent(node_events, AXEventGenerator::Event::NAME_CHANGED);
+ RemoveEvent(node_events, AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED);
+}
+
} // namespace
AXEventGenerator::EventParams::EventParams(
@@ -55,6 +68,8 @@ AXEventGenerator::EventParams::EventParams(
const std::vector<AXEventIntent>& event_intents)
: event(event), event_from(event_from), event_intents(event_intents) {}
+AXEventGenerator::EventParams::EventParams(const EventParams& other) = default;
+
AXEventGenerator::EventParams::~EventParams() = default;
AXEventGenerator::TargetedEvent::TargetedEvent(AXNode* node,
@@ -94,11 +109,17 @@ AXEventGenerator::Iterator& AXEventGenerator::Iterator::operator++() {
if (map_iter_ == map_.end())
return *this;
+ DCHECK(set_iter_ != map_iter_->second.end())
+ << "The set of events should not be empty";
set_iter_++;
- while (map_iter_ != map_.end() && set_iter_ == map_iter_->second.end()) {
+
+ if (set_iter_ == map_iter_->second.end()) {
map_iter_++;
- if (map_iter_ != map_.end())
+ if (map_iter_ != map_.end()) {
set_iter_ = map_iter_->second.begin();
+ DCHECK(set_iter_ != map_iter_->second.end())
+ << "The set of events should not be empty";
+ }
}
return *this;
@@ -152,11 +173,11 @@ void AXEventGenerator::OnNodeDataChanged(AXTree* tree,
DCHECK_EQ(tree_, tree);
// Fire CHILDREN_CHANGED events when the list of children updates.
// Internally we store inline text box nodes as children of a static text
- // node, which enables us to determine character bounds and line layout.
- // We don't expose those to platform APIs, though, so suppress
- // CHILDREN_CHANGED events on static text nodes.
+ // node or a line break node, which enables us to determine character bounds
+ // and line layout. We don't expose those to platform APIs, though, so
+ // suppress CHILDREN_CHANGED events on static text nodes.
if (new_node_data.child_ids != old_node_data.child_ids &&
- new_node_data.role != ax::mojom::Role::kStaticText) {
+ !ui::IsText(new_node_data.role)) {
AXNode* node = tree_->GetFromId(new_node_data.id);
tree_events_[node].emplace(Event::CHILDREN_CHANGED,
ax::mojom::EventFrom::kNone,
@@ -178,8 +199,10 @@ void AXEventGenerator::OnStateChanged(AXTree* tree,
bool new_value) {
DCHECK_EQ(tree_, tree);
- if (state != ax::mojom::State::kIgnored)
+ if (state != ax::mojom::State::kIgnored) {
AddEvent(node, Event::STATE_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
+ }
switch (state) {
case ax::mojom::State::kExpanded:
@@ -282,6 +305,9 @@ void AXEventGenerator::OnStringAttributeChanged(AXTree* tree,
// The image annotation is reported as part of the accessible name.
AddEvent(node, Event::IMAGE_ANNOTATION_CHANGED);
break;
+ case ax::mojom::StringAttribute::kFontFamily:
+ AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
+ break;
default:
AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
break;
@@ -306,12 +332,14 @@ void AXEventGenerator::OnIntAttributeChanged(AXTree* tree,
break;
case ax::mojom::IntAttribute::kCheckedState:
AddEvent(node, Event::CHECKED_STATE_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
break;
case ax::mojom::IntAttribute::kDropeffect:
AddEvent(node, Event::DROPEFFECT_CHANGED);
break;
case ax::mojom::IntAttribute::kHasPopup:
AddEvent(node, Event::HASPOPUP_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
break;
case ax::mojom::IntAttribute::kHierarchicalLevel:
AddEvent(node, Event::HIERARCHICAL_LEVEL_CHANGED);
@@ -332,10 +360,14 @@ void AXEventGenerator::OnIntAttributeChanged(AXTree* tree,
GetRestrictionStates(static_cast<ax::mojom::Restriction>(new_value),
&is_enabled, &is_readonly);
- if (was_enabled != is_enabled)
+ if (was_enabled != is_enabled) {
AddEvent(node, Event::ENABLED_CHANGED);
- if (was_readonly != is_readonly)
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
+ }
+ if (was_readonly != is_readonly) {
AddEvent(node, Event::READONLY_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
+ }
break;
}
case ax::mojom::IntAttribute::kScrollX:
@@ -357,6 +389,19 @@ void AXEventGenerator::OnIntAttributeChanged(AXTree* tree,
case ax::mojom::IntAttribute::kSetSize:
AddEvent(node, Event::SET_SIZE_CHANGED);
break;
+ case ax::mojom::IntAttribute::kBackgroundColor:
+ case ax::mojom::IntAttribute::kColor:
+ case ax::mojom::IntAttribute::kTextDirection:
+ case ax::mojom::IntAttribute::kTextPosition:
+ case ax::mojom::IntAttribute::kTextStyle:
+ case ax::mojom::IntAttribute::kTextOverlineStyle:
+ case ax::mojom::IntAttribute::kTextStrikethroughStyle:
+ case ax::mojom::IntAttribute::kTextUnderlineStyle:
+ AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
+ break;
+ case ax::mojom::IntAttribute::kTextAlign:
+ AddEvent(node, Event::OBJECT_ATTRIBUTE_CHANGED);
+ break;
default:
AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
break;
@@ -383,6 +428,10 @@ void AXEventGenerator::OnFloatAttributeChanged(AXTree* tree,
case ax::mojom::FloatAttribute::kValueForRange:
AddEvent(node, Event::VALUE_CHANGED);
break;
+ case ax::mojom::FloatAttribute::kFontSize:
+ case ax::mojom::FloatAttribute::kFontWeight:
+ AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
+ break;
default:
AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
break;
@@ -398,6 +447,7 @@ void AXEventGenerator::OnBoolAttributeChanged(AXTree* tree,
switch (attr) {
case ax::mojom::BoolAttribute::kBusy:
AddEvent(node, Event::BUSY_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
// Fire an 'invalidated' event when aria-busy becomes false
if (!new_value)
AddEvent(node, Event::LAYOUT_INVALIDATED);
@@ -410,6 +460,7 @@ void AXEventGenerator::OnBoolAttributeChanged(AXTree* tree,
break;
case ax::mojom::BoolAttribute::kSelected: {
AddEvent(node, Event::SELECTED_CHANGED);
+ AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
AXNode* container = node;
while (container &&
!IsContainerWithSelectableChildren(container->data().role))
@@ -452,6 +503,17 @@ void AXEventGenerator::OnIntListAttributeChanged(
case ax::mojom::IntListAttribute::kLabelledbyIds:
AddEvent(node, Event::LABELED_BY_CHANGED);
break;
+ case ax::mojom::IntListAttribute::kMarkerEnds:
+ case ax::mojom::IntListAttribute::kMarkerStarts:
+ case ax::mojom::IntListAttribute::kMarkerTypes:
+ // On a native text field, the spelling- and grammar-error markers are
+ // associated with children not exposed on any platform. Therefore, we
+ // adjust the node we fire that event on here.
+ if (AXNode* text_field = node->GetTextFieldAncestor())
+ AddEvent(text_field, Event::TEXT_ATTRIBUTE_CHANGED);
+ else
+ AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
+ break;
default:
AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
break;
@@ -624,9 +686,52 @@ bool AXEventGenerator::ShouldFireLoadEvents(AXNode* node) {
data.relative_bounds.bounds.height();
}
+void AXEventGenerator::TrimEventsDueToAncestorIgnoredChanged(
+ AXNode* node,
+ std::map<AXNode*, bool>& ancestor_ignored_changed_map) {
+ DCHECK(node);
+
+ // Recursively compute and cache ancestor ignored changed results in
+ // |ancestor_ignored_changed_map|, if |node|'s ancestors have become ignored
+ // and the ancestor's ignored changed results have not been cached.
+ if (node->parent() &&
+ !base::Contains(ancestor_ignored_changed_map, node->parent())) {
+ TrimEventsDueToAncestorIgnoredChanged(node->parent(),
+ ancestor_ignored_changed_map);
+ }
+
+ // If an ancestor of |node| changed to ignored state, update the corresponding
+ // entry in the map for |node| based on the ancestor result (i.e. if an
+ // ancestor changed to ignored state, set the entry in the map to true for the
+ // current node). If |node|'s state changed to ignored as well, we want to
+ // remove its IGNORED_CHANGED event.
+ const auto& map_iter = ancestor_ignored_changed_map.find(node->parent());
+ const auto& events_iter = tree_events_.find(node);
+ if (map_iter != ancestor_ignored_changed_map.end() && map_iter->second) {
+ ancestor_ignored_changed_map.insert(std::make_pair(node, true));
+ if (node->IsIgnored() && events_iter != tree_events_.end()) {
+ RemoveEvent(&(events_iter->second), Event::IGNORED_CHANGED);
+ RemoveEventsDueToIgnoredChanged(&(events_iter->second));
+ }
+ return;
+ }
+
+ // If ignored changed results are not cached, calculate the corresponding
+ // entry for |node| in the map using the ignored states and events of |node|.
+ if (events_iter != tree_events_.end() &&
+ HasEvent(events_iter->second, Event::IGNORED_CHANGED) &&
+ node->IsIgnored()) {
+ ancestor_ignored_changed_map.insert(std::make_pair(node, true));
+ return;
+ }
+
+ ancestor_ignored_changed_map.insert(std::make_pair(node, false));
+}
+
void AXEventGenerator::PostprocessEvents() {
- std::vector<AXNode*> nodes_to_remove_subtree_created;
+ std::map<AXNode*, bool> ancestor_ignored_changed_map;
+ // First pass through |tree_events_|, remove events that we do not need.
for (auto& iter : tree_events_) {
AXNode* node = iter.first;
std::set<EventParams>& node_events = iter.second;
@@ -638,32 +743,55 @@ void AXEventGenerator::PostprocessEvents() {
RemoveEvent(&node_events, Event::LIVE_REGION_CHANGED);
}
- // If a node toggled its ignored state, we shouldn't also fire
- // children changed events on it.
- if (HasEvent(node_events, Event::IGNORED_CHANGED))
- RemoveEvent(&node_events, Event::CHILDREN_CHANGED);
+ if (HasEvent(node_events, Event::IGNORED_CHANGED)) {
+ // If a node toggled its ignored state from show to hide, we only want to
+ // fire IGNORED_CHANGED event on the top most ancestor where this ignored
+ // state change takes place and suppress all the descendants's
+ // IGNORED_CHANGED events.
+ TrimEventsDueToAncestorIgnoredChanged(node, ancestor_ignored_changed_map);
+ RemoveEventsDueToIgnoredChanged(&node_events);
+ }
+
+ // When the selected option in an expanded select element changes, the
+ // foreground and background colors change. But we don't want to treat
+ // those as text attribute changes. This can also happen when a widget
+ // such as a button becomes enabled/disabled.
+ if (HasEvent(node_events, Event::SELECTED_CHANGED) ||
+ HasEvent(node_events, Event::ENABLED_CHANGED)) {
+ RemoveEvent(&node_events, Event::TEXT_ATTRIBUTE_CHANGED);
+ }
- // We shouldn't fire subtree created if the parent also has subtree
- // created on it.
AXNode* parent = node->GetUnignoredParent();
- if (parent && HasEvent(node_events, Event::SUBTREE_CREATED)) {
- if (tree_events_.find(parent) != tree_events_.end()) {
- std::set<EventParams>& parent_events = tree_events_[parent];
- if (HasEvent(parent_events, Event::SUBTREE_CREATED))
- nodes_to_remove_subtree_created.push_back(node);
+
+ // Don't fire text attribute changed on this node if its immediate parent
+ // also has text attribute changed.
+ if (parent && HasEvent(node_events, Event::TEXT_ATTRIBUTE_CHANGED) &&
+ tree_events_.find(parent) != tree_events_.end() &&
+ HasEvent(tree_events_[parent], Event::TEXT_ATTRIBUTE_CHANGED)) {
+ RemoveEvent(&node_events, Event::TEXT_ATTRIBUTE_CHANGED);
+ }
+
+ // Don't fire subtree created on this node if any of its ancestors also has
+ // subtree created.
+ while (parent && HasEvent(node_events, Event::SUBTREE_CREATED) &&
+ tree_events_.find(parent) != tree_events_.end()) {
+ if (HasEvent(tree_events_[parent], Event::SUBTREE_CREATED)) {
+ RemoveEvent(&node_events, Event::SUBTREE_CREATED);
+ break;
}
+ parent = parent->GetUnignoredParent();
}
}
- for (AXNode* node : nodes_to_remove_subtree_created) {
- std::set<EventParams>& node_events = tree_events_[node];
- RemoveEvent(&node_events, Event::SUBTREE_CREATED);
- // If this was the only event, remove the node entirely from the
- // tree events. Note that this can't happen with any of the other logic
- // above since it's all dealing with one event superseding another in
- // the same node.
- if (node_events.size() == 0)
- tree_events_.erase(node);
+ // Second pass through |tree_events_|, remove nodes that do not have any
+ // events left.
+ auto iter = tree_events_.begin();
+ while (iter != tree_events_.end()) {
+ std::set<EventParams>& node_events = iter->second;
+ if (node_events.empty())
+ iter = tree_events_.erase(iter);
+ else
+ ++iter;
}
}
@@ -787,6 +915,8 @@ const char* ToString(AXEventGenerator::Event event) {
return "MULTISELECTABLE_STATE_CHANGED";
case AXEventGenerator::Event::NAME_CHANGED:
return "NAME_CHANGED";
+ case AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
+ return "OBJECT_ATTRIBUTE_CHANGED";
case AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
return "OTHER_ATTRIBUTE_CHANGED";
case AXEventGenerator::Event::PLACEHOLDER_CHANGED:
@@ -819,6 +949,8 @@ const char* ToString(AXEventGenerator::Event event) {
return "STATE_CHANGED";
case AXEventGenerator::Event::SUBTREE_CREATED:
return "SUBTREE_CREATED";
+ case AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
+ return "TEXT_ATTRIBUTE_CHANGED";
case AXEventGenerator::Event::VALUE_CHANGED:
return "VALUE_CHANGED";
case AXEventGenerator::Event::VALUE_MAX_CHANGED:
@@ -833,6 +965,8 @@ const char* ToString(AXEventGenerator::Event event) {
return "FOCUS_CHANGED";
case AXEventGenerator::Event::SORT_CHANGED:
return "SORT_CHANGED";
+ case AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
+ return "WIN_IACCESSIBLE_STATE_CHANGED";
}
NOTREACHED();
}
diff --git a/chromium/ui/accessibility/ax_event_generator.h b/chromium/ui/accessibility/ax_event_generator.h
index 47c64ef7b80..2c25ad1e7dc 100644
--- a/chromium/ui/accessibility/ax_event_generator.h
+++ b/chromium/ui/accessibility/ax_event_generator.h
@@ -67,6 +67,7 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver {
MULTILINE_STATE_CHANGED,
MULTISELECTABLE_STATE_CHANGED,
NAME_CHANGED,
+ OBJECT_ATTRIBUTE_CHANGED,
OTHER_ATTRIBUTE_CHANGED,
PLACEHOLDER_CHANGED,
PORTAL_ACTIVATED,
@@ -84,16 +85,23 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver {
SORT_CHANGED,
STATE_CHANGED,
SUBTREE_CREATED,
+ TEXT_ATTRIBUTE_CHANGED,
VALUE_CHANGED,
VALUE_MAX_CHANGED,
VALUE_MIN_CHANGED,
VALUE_STEP_CHANGED,
+
+ // This event is for the exact set of attributes that affect
+ // the MSAA/IAccessible state on Windows. Not needed on other platforms,
+ // but very natural to compute here.
+ WIN_IACCESSIBLE_STATE_CHANGED,
};
- struct EventParams {
+ struct AX_EXPORT EventParams {
EventParams(Event event,
ax::mojom::EventFrom event_from,
const std::vector<AXEventIntent>& event_intents);
+ EventParams(const EventParams& other);
~EventParams();
Event event;
ax::mojom::EventFrom event_from;
@@ -228,6 +236,18 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver {
void FireActiveDescendantEvents();
void FireRelationSourceEvents(AXTree* tree, AXNode* target_node);
bool ShouldFireLoadEvents(AXNode* node);
+ // Remove excessive events for a tree update containing node.
+ // We remove certain events on a node when it changes to IGNORED state and one
+ // of the node's ancestor has also changed to IGNORED in the same tree update.
+ // |ancestor_has_ignored_map| contains if a node's ancestor has changed to
+ // IGNORED state.
+ // Map's key is: an ax node.
+ // Map's value is:
+ // - True if an ancestor of node changed to IGNORED state.
+ // - False if no ancestor of node changed to IGNORED state.
+ void TrimEventsDueToAncestorIgnoredChanged(
+ AXNode* node,
+ std::map<AXNode*, bool>& ancestor_has_ignored_map);
void PostprocessEvents();
static void GetRestrictionStates(ax::mojom::Restriction restriction,
bool* is_enabled,
diff --git a/chromium/ui/accessibility/ax_event_generator_unittest.cc b/chromium/ui/accessibility/ax_event_generator_unittest.cc
index fdb41c8ade8..611ce3394f5 100644
--- a/chromium/ui/accessibility/ax_event_generator_unittest.cc
+++ b/chromium/ui/accessibility/ax_event_generator_unittest.cc
@@ -204,13 +204,18 @@ TEST(AXEventGeneratorTest, ExpandedAndRowCount) {
update.nodes[3].state = 0;
ASSERT_TRUE(tree.Unserialize(update));
- EXPECT_THAT(event_generator,
- UnorderedElementsAre(
- HasEventAtNode(AXEventGenerator::Event::COLLAPSED, 4),
- HasEventAtNode(AXEventGenerator::Event::EXPANDED, 3),
- HasEventAtNode(AXEventGenerator::Event::ROW_COUNT_CHANGED, 2),
- HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 3),
- HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 4)));
+ EXPECT_THAT(
+ event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::COLLAPSED, 4),
+ HasEventAtNode(AXEventGenerator::Event::EXPANDED, 3),
+ HasEventAtNode(AXEventGenerator::Event::ROW_COUNT_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 3),
+ HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 4),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 4)));
}
TEST(AXEventGeneratorTest, SelectedAndSelectedChildren) {
@@ -247,7 +252,11 @@ TEST(AXEventGeneratorTest, SelectedAndSelectedChildren) {
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED, 2),
HasEventAtNode(AXEventGenerator::Event::SELECTED_CHANGED, 3),
- HasEventAtNode(AXEventGenerator::Event::SELECTED_CHANGED, 4)));
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 3),
+ HasEventAtNode(AXEventGenerator::Event::SELECTED_CHANGED, 4),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 4)));
}
TEST(AXEventGeneratorTest, StringValueChanged) {
@@ -359,9 +368,12 @@ TEST(AXEventGeneratorTest, CheckedStateChanged) {
AXTreeUpdate update = initial_state;
update.nodes[0].SetCheckedState(ax::mojom::CheckedState::kTrue);
ASSERT_TRUE(tree.Unserialize(update));
- EXPECT_THAT(event_generator,
- UnorderedElementsAre(HasEventAtNode(
- AXEventGenerator::Event::CHECKED_STATE_CHANGED, 1)));
+ EXPECT_THAT(
+ event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHECKED_STATE_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
TEST(AXEventGeneratorTest, ActiveDescendantChanged) {
@@ -521,6 +533,8 @@ TEST(AXEventGeneratorTest, LiveRegionOnlyTextChanges) {
event_generator,
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::CHECKED_STATE_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 3),
HasEventAtNode(AXEventGenerator::Event::DESCRIPTION_CHANGED, 2)));
}
@@ -673,6 +687,122 @@ TEST(AXEventGeneratorTest, ScrollVerticalPositionChanged) {
AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED, 1)));
}
+TEST(AXEventGeneratorTest, TextAttributeChanged) {
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(17);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].child_ids = {2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[4].id = 5;
+ initial_state.nodes[5].id = 6;
+ initial_state.nodes[6].id = 7;
+ initial_state.nodes[7].id = 8;
+ initial_state.nodes[8].id = 9;
+ initial_state.nodes[9].id = 10;
+ initial_state.nodes[10].id = 11;
+ initial_state.nodes[11].id = 12;
+ initial_state.nodes[12].id = 13;
+ initial_state.nodes[13].id = 14;
+ initial_state.nodes[14].id = 15;
+ initial_state.nodes[15].id = 16;
+ initial_state.nodes[16].id = 17;
+
+ // To test changing the start and end of existing markers.
+ initial_state.nodes[11].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {static_cast<int32_t>(ax::mojom::MarkerType::kTextMatch)});
+ initial_state.nodes[11].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerStarts, {5});
+ initial_state.nodes[11].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerEnds, {10});
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kColor, 0);
+ update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, 0);
+ update.nodes[3].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextDirection,
+ static_cast<int32_t>(ax::mojom::WritingDirection::kRtl));
+ update.nodes[4].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextPosition,
+ static_cast<int32_t>(ax::mojom::TextPosition::kSuperscript));
+ update.nodes[5].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextStyle,
+ static_cast<int32_t>(ax::mojom::TextStyle::kBold));
+ update.nodes[6].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextOverlineStyle,
+ static_cast<int32_t>(ax::mojom::TextDecorationStyle::kSolid));
+ update.nodes[7].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextStrikethroughStyle,
+ static_cast<int32_t>(ax::mojom::TextDecorationStyle::kWavy));
+ update.nodes[8].AddIntAttribute(
+ ax::mojom::IntAttribute::kTextUnderlineStyle,
+ static_cast<int32_t>(ax::mojom::TextDecorationStyle::kDotted));
+ update.nodes[9].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {static_cast<int32_t>(ax::mojom::MarkerType::kSpelling)});
+ update.nodes[10].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {static_cast<int32_t>(ax::mojom::MarkerType::kGrammar)});
+ update.nodes[11].AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
+ {11});
+ update.nodes[12].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {static_cast<int32_t>(ax::mojom::MarkerType::kActiveSuggestion)});
+ update.nodes[13].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {static_cast<int32_t>(ax::mojom::MarkerType::kSuggestion)});
+ update.nodes[14].AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize,
+ 12.0f);
+ update.nodes[15].AddFloatAttribute(ax::mojom::FloatAttribute::kFontWeight,
+ 600.0f);
+ update.nodes[16].AddStringAttribute(ax::mojom::StringAttribute::kFontFamily,
+ "sans");
+
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(
+ event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 4),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 5),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 6),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 7),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 8),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 9),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 10),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 11),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 12),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 13),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 14),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 15),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 16),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 17)));
+}
+
+TEST(AXEventGeneratorTest, TextObjectAttributeChanged) {
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(1);
+ initial_state.nodes[0].id = 1;
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTextAlign, 2);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(HasEventAtNode(
+ AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED, 1)));
+}
+
TEST(AXEventGeneratorTest, OtherAttributeChanged) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
@@ -709,7 +839,7 @@ TEST(AXEventGeneratorTest, OtherAttributeChanged) {
HasEventAtNode(AXEventGenerator::Event::CONTROLS_CHANGED, 6),
HasEventAtNode(AXEventGenerator::Event::LANGUAGE_CHANGED, 2),
HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 3),
- HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 4),
+ HasEventAtNode(AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED, 4),
HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 5),
HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 6)));
}
@@ -1022,6 +1152,459 @@ TEST(AXEventGeneratorTest, TwoNodesSwapIgnored2) {
HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2)));
}
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly1) {
+ // BEFORE
+ // 1 (IGN)
+ // / \
+ // 2 3 (IGN)
+ // AFTER
+ // 1 (IGN)
+ // / \
+ // 2 (IGN) 3
+ // IGNORED_CHANGED expected on #2, #3
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(3);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].AddState(ax::mojom::State::kIgnored);
+ initial_state.nodes[0].child_ids = {2, 3};
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kStaticText;
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[1].AddState(ax::mojom::State::kIgnored);
+ update.nodes[2].RemoveState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly2) {
+ // BEFORE
+ // 1
+ // |
+ // 2
+ // / \
+ // 3 4 (IGN)
+ // AFTER
+ // 1
+ // |
+ // 2 ___
+ // / \
+ // 3 (IGN) 4
+ // IGNORED_CHANGED expected on #3, #4
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(4);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3, 4};
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[3].AddState(ax::mojom::State::kIgnored);
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[2].AddState(ax::mojom::State::kIgnored);
+ update.nodes[3].RemoveState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 4),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 4)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly3) {
+ // BEFORE
+ // 1
+ // |
+ // 2 ___
+ // / \
+ // 3 (IGN) 4
+ // AFTER
+ // 1 (IGN)
+ // |
+ // 2
+ // / \
+ // 3 4 (IGN)
+ // IGNORED_CHANGED expected on #1, #3
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(4);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3, 4};
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kStaticText;
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[0].AddState(ax::mojom::State::kIgnored);
+ update.nodes[2].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[3].AddState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly4) {
+ // BEFORE
+ // 1 (IGN)
+ // |
+ // 2
+ // |
+ // 3 (IGN)
+ // |
+ // 4 (IGN)
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 (IGN) 7 (IGN) 8
+ // AFTER
+ // 1 (IGN)
+ // |
+ // 2
+ // |
+ // 3 (IGN)
+ // |
+ // 4 (IGN)
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 7 8 (IGN)
+
+ // IGNORED_CHANGED expected on #6, #7, #8
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(8);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3};
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[2].child_ids = {4};
+ initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[3].child_ids = {5};
+ initial_state.nodes[3].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[4].id = 5;
+ initial_state.nodes[4].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[4].child_ids = {6, 7, 8};
+
+ initial_state.nodes[5].id = 6;
+ initial_state.nodes[5].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[5].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[6].id = 7;
+ initial_state.nodes[6].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[6].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[7].id = 8;
+ initial_state.nodes[7].role = ax::mojom::Role::kStaticText;
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[5].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[6].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[7].AddState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 5),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 6),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 7),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 6),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 7),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 8)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly5) {
+ // BEFORE
+ // 1
+ // |
+ // 2
+ // |
+ // 3 (IGN)
+ // |
+ // 4 (IGN)
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 (IGN) 7 8
+ // AFTER
+ // 1 (IGN)
+ // |
+ // 2
+ // |
+ // 3 (IGN)
+ // |
+ // 4 (IGN)
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 7 (IGN) 8 (IGN)
+
+ // IGNORED_CHANGED expected on #1, #6
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(8);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3};
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[2].child_ids = {4};
+ initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[3].child_ids = {5};
+ initial_state.nodes[3].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[4].id = 5;
+ initial_state.nodes[4].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[4].child_ids = {6, 7, 8};
+
+ initial_state.nodes[5].id = 6;
+ initial_state.nodes[5].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[5].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[6].id = 7;
+ initial_state.nodes[6].role = ax::mojom::Role::kStaticText;
+
+ initial_state.nodes[7].id = 8;
+ initial_state.nodes[7].role = ax::mojom::Role::kStaticText;
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[0].AddState(ax::mojom::State::kIgnored);
+ update.nodes[5].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[6].AddState(ax::mojom::State::kIgnored);
+ update.nodes[7].AddState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 5),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 6),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 6)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly6) {
+ // BEFORE
+ // 1 (IGN)
+ // |
+ // 2
+ // |
+ // 3
+ // |
+ // 4
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 (IGN) 7 (IGN) 8
+ // AFTER
+ // 1
+ // |
+ // 2
+ // |
+ // 3
+ // |
+ // 4
+ // |
+ // ____ 5 _____
+ // / | \
+ // 6 7 8 (IGN)
+
+ // IGNORED_CHANGED expected on #1, #6, #7, #8
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(8);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+ initial_state.nodes[0].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3};
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[2].child_ids = {4};
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[3].child_ids = {5};
+
+ initial_state.nodes[4].id = 5;
+ initial_state.nodes[4].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[4].child_ids = {6, 7, 8};
+
+ initial_state.nodes[5].id = 6;
+ initial_state.nodes[5].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[5].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[6].id = 7;
+ initial_state.nodes[6].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[6].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[7].id = 8;
+ initial_state.nodes[7].role = ax::mojom::Role::kStaticText;
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[0].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[5].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[6].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[7].AddState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 5),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 1),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 6),
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 7),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 6),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 7),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 8)));
+}
+
+TEST(AXEventGeneratorTest, IgnoredChangedFiredOnAncestorOnly7) {
+ // BEFORE
+ // 1 (IGN)
+ // |
+ // 2 (IGN)
+ // |
+ // 3
+ // |
+ // __ 4 ___
+ // / \
+ // 5 (IGN) 6 (IGN)
+ // AFTER
+ // 1
+ // |
+ // 2
+ // |
+ // 3 (IGN)
+ // |
+ // __ 4 (IGN)
+ // / \
+ // 5 (IGN) 6 (IGN)
+
+ // IGNORED_CHANGED expected on #1, #2, #3
+
+ AXTreeUpdate initial_state;
+ initial_state.root_id = 1;
+ initial_state.nodes.resize(6);
+ initial_state.nodes[0].id = 1;
+ initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+ initial_state.nodes[0].child_ids = {2};
+ initial_state.nodes[0].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[1].id = 2;
+ initial_state.nodes[1].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[1].child_ids = {3};
+ initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[2].id = 3;
+ initial_state.nodes[2].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[2].child_ids = {4};
+
+ initial_state.nodes[3].id = 4;
+ initial_state.nodes[3].role = ax::mojom::Role::kGroup;
+ initial_state.nodes[3].child_ids = {5, 6};
+
+ initial_state.nodes[4].id = 5;
+ initial_state.nodes[4].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[4].AddState(ax::mojom::State::kIgnored);
+
+ initial_state.nodes[5].id = 6;
+ initial_state.nodes[5].role = ax::mojom::Role::kStaticText;
+ initial_state.nodes[5].AddState(ax::mojom::State::kIgnored);
+
+ AXTree tree(initial_state);
+
+ AXEventGenerator event_generator(&tree);
+ AXTreeUpdate update = initial_state;
+ update.nodes[0].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
+ update.nodes[2].AddState(ax::mojom::State::kIgnored);
+ update.nodes[3].AddState(ax::mojom::State::kIgnored);
+ ASSERT_TRUE(tree.Unserialize(update));
+ EXPECT_THAT(event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 1),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 2),
+ HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3)));
+}
+
TEST(AXEventGeneratorTest, ActiveDescendantChangeOnDescendant) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
@@ -1244,7 +1827,9 @@ TEST(AXEventGeneratorTest, AriaBusyChanged) {
event_generator,
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::BUSY_CHANGED, 1),
- HasEventAtNode(AXEventGenerator::Event::LAYOUT_INVALIDATED, 1)));
+ HasEventAtNode(AXEventGenerator::Event::LAYOUT_INVALIDATED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
TEST(AXEventGeneratorTest, MultiselectableStateChanged) {
@@ -1265,7 +1850,9 @@ TEST(AXEventGeneratorTest, MultiselectableStateChanged) {
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED,
1),
- HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
+ HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
TEST(AXEventGeneratorTest, RequiredStateChanged) {
@@ -1285,7 +1872,9 @@ TEST(AXEventGeneratorTest, RequiredStateChanged) {
event_generator,
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::REQUIRED_STATE_CHANGED, 1),
- HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
+ HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
TEST(AXEventGeneratorTest, FlowToChanged) {
@@ -1412,9 +2001,12 @@ TEST(AXEventGeneratorTest, HasPopupChanged) {
update.nodes[0].SetHasPopup(ax::mojom::HasPopup::kTrue);
EXPECT_TRUE(tree.Unserialize(update));
- EXPECT_THAT(event_generator,
- UnorderedElementsAre(HasEventAtNode(
- AXEventGenerator::Event::HASPOPUP_CHANGED, 1)));
+ EXPECT_THAT(
+ event_generator,
+ UnorderedElementsAre(
+ HasEventAtNode(AXEventGenerator::Event::HASPOPUP_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
TEST(AXEventGeneratorTest, LiveRelevantChanged) {
@@ -1451,7 +2043,9 @@ TEST(AXEventGeneratorTest, MultilineStateChanged) {
event_generator,
UnorderedElementsAre(
HasEventAtNode(AXEventGenerator::Event::MULTILINE_STATE_CHANGED, 1),
- HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
+ HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1),
+ HasEventAtNode(AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED,
+ 1)));
}
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_language_detection.cc b/chromium/ui/accessibility/ax_language_detection.cc
index ea70f3dbcf2..433eae37389 100644
--- a/chromium/ui/accessibility/ax_language_detection.cc
+++ b/chromium/ui/accessibility/ax_language_detection.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
+#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/accessibility_switches.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_tree.h"
@@ -208,10 +209,28 @@ AXLanguageDetectionManager::AXLanguageDetectionManager(AXTree* tree)
AXLanguageDetectionManager::~AXLanguageDetectionManager() = default;
+bool AXLanguageDetectionManager::IsStaticLanguageDetectionEnabled() {
+ // Static language detection can be enabled by either:
+ // 1) The general language detection feature flag which gates both static and
+ // dynamic language detection (feature flag for experiment), or
+ // 2) The Static specific flag (user controlled switch).
+ return features::IsAccessibilityLanguageDetectionEnabled() ||
+ ::switches::IsExperimentalAccessibilityLanguageDetectionEnabled();
+}
+
+bool AXLanguageDetectionManager::IsDynamicLanguageDetectionEnabled() {
+ // Dynamic language detection can be enabled by either:
+ // 1) The general language detection feature flag which gates both static and
+ // dynamic language detection (feature flag for experiment), or
+ // 2) The Dynamic specific flag (user controlled switch).
+ return features::IsAccessibilityLanguageDetectionEnabled() ||
+ ::switches::
+ IsExperimentalAccessibilityLanguageDetectionDynamicEnabled();
+}
+
void AXLanguageDetectionManager::RegisterLanguageDetectionObserver() {
- // If the dynamic feature flag is not enabled then do nothing.
- if (!::switches::
- IsExperimentalAccessibilityLanguageDetectionDynamicEnabled()) {
+ // Do not perform dynamic language detection unless explicitly enabled.
+ if (!IsDynamicLanguageDetectionEnabled()) {
return;
}
@@ -223,7 +242,8 @@ void AXLanguageDetectionManager::RegisterLanguageDetectionObserver() {
// Detect languages for each node.
void AXLanguageDetectionManager::DetectLanguages() {
TRACE_EVENT0("accessibility", "AXLanguageInfo::DetectLanguages");
- if (!::switches::IsExperimentalAccessibilityLanguageDetectionEnabled()) {
+
+ if (!IsStaticLanguageDetectionEnabled()) {
return;
}
@@ -234,9 +254,9 @@ void AXLanguageDetectionManager::DetectLanguages() {
// Will not check feature flag.
void AXLanguageDetectionManager::DetectLanguagesForSubtree(
AXNode* subtree_root) {
- // Only perform detection for kStaticText(s).
+ // Only perform detection for kStaticText nodes.
//
- // Do not visit the children of kStaticText(s) as they don't have
+ // Do not visit the children of kStaticText nodes as they don't have
// interesting children for language detection.
//
// Since kInlineTextBox(es) contain text from their parent, any detection on
@@ -308,7 +328,7 @@ void AXLanguageDetectionManager::DetectLanguagesForNode(AXNode* node) {
void AXLanguageDetectionManager::LabelLanguages() {
TRACE_EVENT0("accessibility", "AXLanguageInfo::LabelLanguages");
- if (!::switches::IsExperimentalAccessibilityLanguageDetectionEnabled()) {
+ if (!IsStaticLanguageDetectionEnabled()) {
return;
}
@@ -443,8 +463,7 @@ AXLanguageDetectionObserver::AXLanguageDetectionObserver(AXTree* tree)
// We expect the feature flag to have be checked before this Observer is
// constructed, this should have been checked by
// RegisterLanguageDetectionObserver.
- DCHECK(
- ::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
+ DCHECK(AXLanguageDetectionManager::IsDynamicLanguageDetectionEnabled());
tree_->AddObserver(this);
}
@@ -473,7 +492,7 @@ void AXLanguageDetectionObserver::OnAtomicUpdateFinished(
DCHECK(tree->language_detection_manager);
// Perform Detect and Label for each node changed or created.
- // We currently only consider kStaticText for detection.
+ // We currently only consider nodes with a role of kStaticText for detection.
//
// Note that language inheritance is now handled by AXNode::GetLanguage.
//
diff --git a/chromium/ui/accessibility/ax_language_detection.h b/chromium/ui/accessibility/ax_language_detection.h
index d8424e53304..035530a86f9 100644
--- a/chromium/ui/accessibility/ax_language_detection.h
+++ b/chromium/ui/accessibility/ax_language_detection.h
@@ -281,6 +281,10 @@ class AX_EXPORT AXLanguageDetectionManager {
// Allow access from a fixture only used in testing.
friend class AXLanguageDetectionTestFixture;
+ // Helper methods to test if language detection features are enabled.
+ static bool IsStaticLanguageDetectionEnabled();
+ static bool IsDynamicLanguageDetectionEnabled();
+
// Perform detection for subtree rooted at subtree_root.
void DetectLanguagesForSubtree(AXNode* subtree_root);
// Perform detection for node. Will not descend into children.
diff --git a/chromium/ui/accessibility/ax_language_detection_unittest.cc b/chromium/ui/accessibility/ax_language_detection_unittest.cc
index c4162974047..6b4bc538128 100644
--- a/chromium/ui/accessibility/ax_language_detection_unittest.cc
+++ b/chromium/ui/accessibility/ax_language_detection_unittest.cc
@@ -11,7 +11,9 @@
#include "base/command_line.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/accessibility_switches.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
@@ -56,6 +58,14 @@ class AXLanguageDetectionTestFixture : public testing::Test {
const AXLanguageDetectionTestFixture&) = delete;
protected:
+ bool IsStaticLanguageDetectionEnabled() {
+ return AXLanguageDetectionManager::IsStaticLanguageDetectionEnabled();
+ }
+
+ bool IsDynamicLanguageDetectionEnabled() {
+ return AXLanguageDetectionManager::IsDynamicLanguageDetectionEnabled();
+ }
+
AXLanguageDetectionObserver* getObserver(AXTree& tree) {
return tree.language_detection_manager->language_detection_observer_.get();
}
@@ -138,28 +148,45 @@ class AXLanguageDetectionTestDynamicContent
}
};
-TEST(AXLanguageDetectionTest, StaticContentFeatureFlag) {
+TEST_F(AXLanguageDetectionTestFixture, StaticContentFeatureFlag) {
// TODO(crbug/889370): Remove this test once this feature is stable
EXPECT_FALSE(
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
+ EXPECT_FALSE(IsStaticLanguageDetectionEnabled());
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
EXPECT_TRUE(
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
+ EXPECT_TRUE(IsStaticLanguageDetectionEnabled());
}
-TEST(AXLanguageDetectionTest, DynamicContentFeatureFlag) {
+TEST_F(AXLanguageDetectionTestFixture, DynamicContentFeatureFlag) {
// TODO(crbug/889370): Remove this test once this feature is stable
EXPECT_FALSE(
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
+ EXPECT_FALSE(IsDynamicLanguageDetectionEnabled());
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
EXPECT_TRUE(
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
+ EXPECT_TRUE(IsDynamicLanguageDetectionEnabled());
+}
+
+TEST_F(AXLanguageDetectionTestFixture, FeatureFlag) {
+ // TODO(crbug/889370): Remove this test once this feature is stable
+ EXPECT_FALSE(IsStaticLanguageDetectionEnabled());
+ EXPECT_FALSE(IsDynamicLanguageDetectionEnabled());
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kEnableAccessibilityLanguageDetection}, {});
+
+ EXPECT_TRUE(IsStaticLanguageDetectionEnabled());
+ EXPECT_TRUE(IsDynamicLanguageDetectionEnabled());
}
TEST(AXLanguageDetectionTest, LangAttrInheritanceFeatureFlagOff) {
@@ -840,8 +867,10 @@ TEST_F(AXLanguageDetectionTestStaticContent, kLanguageUntouched) {
}
}
-// Test RegisterLanguageDetectionObserver correctly respects the feature flag.
+// Test RegisterLanguageDetectionObserver correctly respects the command line
+// flags.
TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFlag) {
+ // Enable only the flag controlling static language detection.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
@@ -855,11 +884,39 @@ TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFlag) {
ASSERT_EQ(getObserver(tree), nullptr);
+ // Now enable the dynamic feature flag.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
- // Try registration without turning on Dynamic feature flag, this should
- // do nothing.
+ // Try registration again, this should construct and register observer as flag
+ // is now enabled.
+ tree.language_detection_manager->RegisterLanguageDetectionObserver();
+
+ // Check our observer was constructed.
+ ASSERT_NE(getObserver(tree), nullptr);
+
+ // Check our observer was registered in our tree.
+ ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
+}
+
+// Test RegisterLanguageDetectionObserver correctly respects the feature flag.
+TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFeatureFlag) {
+ // Construct empty tree and check initialisation.
+ AXTree tree;
+ ASSERT_NE(tree.language_detection_manager, nullptr);
+ ASSERT_EQ(getObserver(tree), nullptr);
+
+ // Try registration without enabling Dynamic feature flag, should be a no-op.
+ tree.language_detection_manager->RegisterLanguageDetectionObserver();
+
+ ASSERT_EQ(getObserver(tree), nullptr);
+
+ // Enable general feature flag which gates both Static and Dynamic features.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kEnableAccessibilityLanguageDetection}, {});
+
+ // Try registration again, this should now construct and register an observer.
tree.language_detection_manager->RegisterLanguageDetectionObserver();
// Check our observer was constructed.
diff --git a/chromium/ui/accessibility/ax_node.cc b/chromium/ui/accessibility/ax_node.cc
index 3c7b06ecd91..da368ef0f1d 100644
--- a/chromium/ui/accessibility/ax_node.cc
+++ b/chromium/ui/accessibility/ax_node.cc
@@ -488,7 +488,9 @@ std::string AXNode::GetInnerText() const {
// value or its placeholder. Otherwise we prefer to look at its descendant
// text nodes because Blink doesn't always add all trailing white space to the
// value attribute.
- if (data().IsTextField() && children().empty()) {
+ const bool is_plain_text_field_without_descendants =
+ (data().IsTextField() && !GetUnignoredChildCount());
+ if (is_plain_text_field_without_descendants) {
std::string value =
data().GetStringAttribute(ax::mojom::StringAttribute::kValue);
// If the value is empty, then there might be some placeholder text in the
@@ -499,9 +501,12 @@ std::string AXNode::GetInnerText() const {
}
// Ordinarily, plain text fields are leaves. We need to exclude them from the
- // set of leaf nodes when they expose any descendants if we want to compute
- // their inner text from their descendant text nodes.
- if (IsLeaf() && !(data().IsTextField() && !children().empty())) {
+ // set of leaf nodes when they expose any descendants. This is because we want
+ // to compute their inner text from their descendant text nodes as we don't
+ // always trust the "value" attribute provided by Blink.
+ const bool is_plain_text_field_with_descendants =
+ (data().IsTextField() && GetUnignoredChildCount());
+ if (IsLeaf() && !is_plain_text_field_with_descendants) {
switch (data().GetNameFrom()) {
case ax::mojom::NameFrom::kNone:
case ax::mojom::NameFrom::kUninitialized:
@@ -736,7 +741,7 @@ std::vector<AXNode::AXID> AXNode::GetTableRowNodeIds() const {
return row_node_ids;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
//
// Table column-like nodes. These nodes are only present on macOS.
@@ -763,7 +768,7 @@ base::Optional<int> AXNode::GetTableColColIndex() const {
return index;
}
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
//
// Table cell-like nodes.
@@ -1104,11 +1109,8 @@ bool AXNode::IsChildOfLeaf() const {
}
bool AXNode::IsLeaf() const {
- return !GetUnignoredChildCount() || IsLeafIncludingIgnored();
-}
-
-bool AXNode::IsLeafIncludingIgnored() const {
- if (children().empty())
+ // A node is also a leaf if all of it's descendants are ignored.
+ if (children().empty() || !GetUnignoredChildCount())
return true;
#if defined(OS_WIN)
diff --git a/chromium/ui/accessibility/ax_node.h b/chromium/ui/accessibility/ax_node.h
index b9fb97ff04e..c59d00da334 100644
--- a/chromium/ui/accessibility/ax_node.h
+++ b/chromium/ui/accessibility/ax_node.h
@@ -140,11 +140,13 @@ class AX_EXPORT AXNode final {
AXNode* GetPreviousSibling() const;
AXNode* GetNextSibling() const;
- // Returns true if the node has any of the text related roles.
+ // Returns true if the node has any of the text related roles, including
+ // kStaticText, kInlineTextBox and kListMarker (for Legacy Layout). Does not
+ // include any text field roles.
bool IsText() const;
- // Returns true if the node has any line break related roles or is the child a
- // node with line break related roles.
+ // Returns true if the node has any line break related roles or is the child
+ // of a node with line break related roles.
bool IsLineBreak() const;
// Set the node's accessibility data. This may be done during initialization
@@ -360,11 +362,11 @@ class AX_EXPORT AXNode final {
// Get the node ids that represent rows in a table.
std::vector<AXNode::AXID> GetTableRowNodeIds() const;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Table column-like nodes. These nodes are only present on macOS.
bool IsTableColumn() const;
base::Optional<int> GetTableColColIndex() const;
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
// Table cell-like nodes.
bool IsTableCellOrHeader() const;
@@ -418,19 +420,13 @@ class AX_EXPORT AXNode final {
// The definition of a leaf includes nodes with children that are exclusively
// an internal renderer implementation, such as the children of an HTML native
// text field, as well as nodes with presentational children according to the
- // ARIA and HTML5 Specs.
+ // ARIA and HTML5 Specs. Also returns true if all of the node's descendants
+ // are ignored.
//
// A leaf node should never have children that are focusable or
// that might send notifications.
bool IsLeaf() const;
- // Returns true if this is a leaf node, (see "IsLeaf"), or if all of the
- // node's children are ignored.
- //
- // TODO(nektar): There are no performance advantages in keeping this method
- // since unignored child count is cached. Please remove.
- bool IsLeafIncludingIgnored() const;
-
// Returns true if this node is a list marker or if it's a descendant
// of a list marker node. Returns false otherwise.
bool IsInListMarker() const;
diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc
index 789e9e01feb..0a1178b893b 100644
--- a/chromium/ui/accessibility/ax_node_data.cc
+++ b/chromium/ui/accessibility/ax_node_data.cc
@@ -153,6 +153,7 @@ bool IsNodeIdIntAttribute(ax::mojom::IntAttribute attr) {
case ax::mojom::IntAttribute::kCheckedState:
case ax::mojom::IntAttribute::kRestriction:
case ax::mojom::IntAttribute::kListStyle:
+ case ax::mojom::IntAttribute::kTextAlign:
case ax::mojom::IntAttribute::kTextDirection:
case ax::mojom::IntAttribute::kTextPosition:
case ax::mojom::IntAttribute::kTextStyle:
@@ -895,15 +896,27 @@ void AXNodeData::SetListStyle(ax::mojom::ListStyle list_style) {
}
}
-ax::mojom::TextDirection AXNodeData::GetTextDirection() const {
- return static_cast<ax::mojom::TextDirection>(
+ax::mojom::TextAlign AXNodeData::GetTextAlign() const {
+ return static_cast<ax::mojom::TextAlign>(
+ GetIntAttribute(ax::mojom::IntAttribute::kTextAlign));
+}
+
+void AXNodeData::SetTextAlign(ax::mojom::TextAlign text_align) {
+ if (HasIntAttribute(ax::mojom::IntAttribute::kTextAlign))
+ RemoveIntAttribute(ax::mojom::IntAttribute::kTextAlign);
+ AddIntAttribute(ax::mojom::IntAttribute::kTextAlign,
+ static_cast<int32_t>(text_align));
+}
+
+ax::mojom::WritingDirection AXNodeData::GetTextDirection() const {
+ return static_cast<ax::mojom::WritingDirection>(
GetIntAttribute(ax::mojom::IntAttribute::kTextDirection));
}
-void AXNodeData::SetTextDirection(ax::mojom::TextDirection text_direction) {
+void AXNodeData::SetTextDirection(ax::mojom::WritingDirection text_direction) {
if (HasIntAttribute(ax::mojom::IntAttribute::kTextDirection))
RemoveIntAttribute(ax::mojom::IntAttribute::kTextDirection);
- if (text_direction != ax::mojom::TextDirection::kNone) {
+ if (text_direction != ax::mojom::WritingDirection::kNone) {
AddIntAttribute(ax::mojom::IntAttribute::kTextDirection,
static_cast<int32_t>(text_direction));
}
@@ -935,6 +948,12 @@ bool AXNodeData::IsClickable() const {
return ui::IsClickable(role);
}
+bool AXNodeData::IsSelectable() const {
+ // It's selectable if it has the attribute, whether it's true or false.
+ return HasBoolAttribute(ax::mojom::BoolAttribute::kSelected) &&
+ GetRestriction() != ax::mojom::Restriction::kDisabled;
+}
+
bool AXNodeData::IsIgnored() const {
return HasState(ax::mojom::State::kIgnored) ||
role == ax::mojom::Role::kIgnored;
@@ -1239,18 +1258,24 @@ std::string AXNodeData::ToString() const {
break;
}
break;
+ case ax::mojom::IntAttribute::kTextAlign:
+ result += " text_align=";
+ result += ui::ToString(
+ static_cast<ax::mojom::TextAlign>(int_attribute.second));
+ break;
case ax::mojom::IntAttribute::kTextDirection:
- switch (static_cast<ax::mojom::TextDirection>(int_attribute.second)) {
- case ax::mojom::TextDirection::kLtr:
+ switch (
+ static_cast<ax::mojom::WritingDirection>(int_attribute.second)) {
+ case ax::mojom::WritingDirection::kLtr:
result += " text_direction=ltr";
break;
- case ax::mojom::TextDirection::kRtl:
+ case ax::mojom::WritingDirection::kRtl:
result += " text_direction=rtl";
break;
- case ax::mojom::TextDirection::kTtb:
+ case ax::mojom::WritingDirection::kTtb:
result += " text_direction=ttb";
break;
- case ax::mojom::TextDirection::kBtt:
+ case ax::mojom::WritingDirection::kBtt:
result += " text_direction=btt";
break;
default:
@@ -1550,6 +1575,9 @@ std::string AXNodeData::ToString() const {
case ax::mojom::BoolAttribute::kClipsChildren:
result += " clips_children=" + value;
break;
+ case ax::mojom::BoolAttribute::kNotUserSelectableStyle:
+ result += " not_user_selectable=" + value;
+ break;
case ax::mojom::BoolAttribute::kSelected:
result += " selected=" + value;
break;
diff --git a/chromium/ui/accessibility/ax_node_data.h b/chromium/ui/accessibility/ax_node_data.h
index c2ac3b5ad75..bc2484cb711 100644
--- a/chromium/ui/accessibility/ax_node_data.h
+++ b/chromium/ui/accessibility/ax_node_data.h
@@ -195,8 +195,10 @@ struct AX_BASE_EXPORT AXNodeData {
void SetRestriction(ax::mojom::Restriction restriction);
ax::mojom::ListStyle GetListStyle() const;
void SetListStyle(ax::mojom::ListStyle list_style);
- ax::mojom::TextDirection GetTextDirection() const;
- void SetTextDirection(ax::mojom::TextDirection text_direction);
+ ax::mojom::TextAlign GetTextAlign() const;
+ void SetTextAlign(ax::mojom::TextAlign text_align);
+ ax::mojom::WritingDirection GetTextDirection() const;
+ void SetTextDirection(ax::mojom::WritingDirection text_direction);
ax::mojom::ImageAnnotationStatus GetImageAnnotationStatus() const;
void SetImageAnnotationStatus(ax::mojom::ImageAnnotationStatus status);
@@ -212,6 +214,9 @@ struct AX_BASE_EXPORT AXNodeData {
// clicks.
bool IsClickable() const;
+ // Helper to determine if the object is selectable.
+ bool IsSelectable() const;
+
// Helper to determine if the data has the ignored state or ignored role.
bool IsIgnored() const;
diff --git a/chromium/ui/accessibility/ax_node_position.cc b/chromium/ui/accessibility/ax_node_position.cc
index ef23c509c7e..a61526d1d7b 100644
--- a/chromium/ui/accessibility/ax_node_position.cc
+++ b/chromium/ui/accessibility/ax_node_position.cc
@@ -261,6 +261,19 @@ int AXNodePosition::MaxTextOffset() const {
return text_length;
}
+bool AXNodePosition::IsEmbeddedObjectInParent() const {
+ switch (g_ax_embedded_object_behavior) {
+ case AXEmbeddedObjectBehavior::kSuppressCharacter:
+ return false;
+ case AXEmbeddedObjectBehavior::kExposeCharacter:
+ // We don't need to expose an "embedded object character" for textual
+ // nodes and nodes that are invisible to platform APIs. Textual nodes are
+ // represented by their actual text.
+ return !IsNullPosition() && !GetAnchor()->IsText() &&
+ GetAnchor()->IsChildOfLeaf();
+ }
+}
+
bool AXNodePosition::IsInLineBreakingObject() const {
if (IsNullPosition())
return false;
diff --git a/chromium/ui/accessibility/ax_node_position.h b/chromium/ui/accessibility/ax_node_position.h
index 838bdd9f2c2..025b212538c 100644
--- a/chromium/ui/accessibility/ax_node_position.h
+++ b/chromium/ui/accessibility/ax_node_position.h
@@ -56,6 +56,7 @@ class AX_EXPORT AXNodePosition : public AXPosition<AXNodePosition, AXNode> {
AXNode::AXID GetAnchorID(AXNode* node) const override;
AXTreeID GetTreeID(AXNode* node) const override;
+ bool IsEmbeddedObjectInParent() const override;
bool IsInLineBreakingObject() const override;
ax::mojom::Role GetRole() const override;
AXNodeTextStyles GetTextStyles() const override;
diff --git a/chromium/ui/accessibility/ax_node_position_unittest.cc b/chromium/ui/accessibility/ax_node_position_unittest.cc
index 170054bbb71..0328bd69464 100644
--- a/chromium/ui/accessibility/ax_node_position_unittest.cc
+++ b/chromium/ui/accessibility/ax_node_position_unittest.cc
@@ -1687,7 +1687,9 @@ TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
EXPECT_FALSE(text_position->AtStartOfParagraph());
// An "after text" position anchored at the line break should not be the same
- // as a text position at the start of the next paragraph.
+ // as a text position at the start of the next paragraph because in practice
+ // they should have resulted from two different ancestor positions. The former
+ // should have been an upstream position, whilst the latter a downstream one.
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), line_break_.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
@@ -1712,122 +1714,6 @@ TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
EXPECT_FALSE(text_position->AtStartOfParagraph());
}
-TEST_F(AXPositionTest, AtStartOfParagraphOnAListMarkerDescendant) {
- // This test updates the tree structure to test a specific edge case -
- // AtStartOfParagraph should return false on the next sibling of a list marker
- // text descendant.
- // ++1 kRootWebArea
- // ++++2 kList
- // ++++++3 kListItem
- // ++++++++4 kListMarker
- // ++++++++++5 kStaticText
- // ++++++++++++6 kInlineTextBox "1. "
- // ++++++++7 kStaticText
- // ++++++++++8 kInlineTextBox "content"
- // ++++++9 kListItem
- // ++++++++10 kListMarker
- // +++++++++++11 kStaticText
- // ++++++++++++++12 kInlineTextBox "2. "
- // ++++13 kStaticText
- // +++++++14 kInlineTextBox "after"
- AXNodeData root;
- AXNodeData list;
- AXNodeData list_item1;
- AXNodeData list_item2;
- AXNodeData list_marker1;
- AXNodeData list_marker2;
- AXNodeData inline_box1;
- AXNodeData inline_box2;
- AXNodeData inline_box3;
- AXNodeData inline_box4;
- AXNodeData static_text1;
- AXNodeData static_text2;
- AXNodeData static_text3;
- AXNodeData static_text4;
-
- root.id = 1;
- list.id = 2;
- list_item1.id = 3;
- list_marker1.id = 4;
- static_text1.id = 5;
- inline_box1.id = 6;
- static_text2.id = 7;
- inline_box2.id = 8;
- list_item2.id = 9;
- list_marker2.id = 10;
- static_text3.id = 11;
- inline_box3.id = 12;
- static_text4.id = 13;
- inline_box4.id = 14;
-
- root.role = ax::mojom::Role::kRootWebArea;
- root.child_ids = {list.id, static_text4.id};
-
- list.role = ax::mojom::Role::kList;
- list.child_ids = {list_item1.id, list_item2.id};
-
- list_item1.role = ax::mojom::Role::kListItem;
- list_item1.child_ids = {list_marker1.id, static_text2.id};
- list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
- true);
-
- list_marker1.role = ax::mojom::Role::kListMarker;
- list_marker1.child_ids = {static_text1.id};
-
- static_text1.role = ax::mojom::Role::kStaticText;
- static_text1.child_ids = {inline_box1.id};
-
- inline_box1.role = ax::mojom::Role::kInlineTextBox;
- inline_box1.SetName("1. ");
-
- static_text2.role = ax::mojom::Role::kStaticText;
- static_text2.child_ids = {inline_box2.id};
-
- inline_box2.role = ax::mojom::Role::kInlineTextBox;
- inline_box2.SetName("content");
- inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
- inline_box1.id);
-
- list_item2.role = ax::mojom::Role::kListItem;
- list_item2.child_ids = {list_marker2.id};
- list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
- true);
-
- list_marker2.role = ax::mojom::Role::kListMarker;
- list_marker2.child_ids = {static_text3.id};
-
- static_text3.role = ax::mojom::Role::kStaticText;
- static_text3.child_ids = {inline_box3.id};
-
- inline_box3.role = ax::mojom::Role::kInlineTextBox;
- inline_box3.SetName("2. ");
-
- static_text4.role = ax::mojom::Role::kStaticText;
- static_text4.child_ids = {inline_box4.id};
-
- inline_box4.role = ax::mojom::Role::kInlineTextBox;
- inline_box4.SetName("after");
-
- SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
- inline_box1, static_text2, inline_box2, list_item2,
- list_marker2, static_text3, inline_box3, static_text4,
- inline_box4}));
-
- TestPositionType text_position = AXNodePosition::CreateTextPosition(
- GetTreeID(), inline_box2.id, 0 /* text_offset */,
- ax::mojom::TextAffinity::kDownstream);
- ASSERT_NE(nullptr, text_position);
- ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_FALSE(text_position->AtStartOfParagraph());
-
- text_position = AXNodePosition::CreateTextPosition(
- GetTreeID(), inline_box4.id, 0 /* text_offset */,
- ax::mojom::TextAffinity::kDownstream);
- ASSERT_NE(nullptr, text_position);
- ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_TRUE(text_position->AtStartOfParagraph());
-}
-
TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
// End of |inline_box1_| is not the end of paragraph since it's
// followed by a whitespace-only line breaking object
@@ -1873,119 +1759,21 @@ TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
EXPECT_TRUE(text_position->AtEndOfParagraph());
}
-TEST_F(AXPositionTest, AtEndOfParagraphOnAListMarkerDescendant) {
- // This test updates the tree structure to test a specific edge case -
- // AtEndOfParagraph should return false on a child of a list marker if the
- // list item has content. When the list marker is the only child of a list
- // item, it should return true.
- // ++1 kRootWebArea
- // ++++2 kList
- // ++++++3 kListItem
- // ++++++++4 kListMarker
- // ++++++++++5 kStaticText
- // ++++++++++++6 kInlineTextBox "1. "
- // ++++++++7 kStaticText
- // ++++++++++8 kInlineTextBox "content"
- // ++++++9 kListItem
- // ++++++++10 kListMarker
- // +++++++++++11 kStaticText
- // ++++++++++++++12 kInlineTextBox "2. "
- AXNodeData root;
- AXNodeData list;
- AXNodeData list_item1;
- AXNodeData list_item2;
- AXNodeData list_marker1;
- AXNodeData list_marker2;
- AXNodeData inline_box1;
- AXNodeData inline_box2;
- AXNodeData inline_box3;
- AXNodeData static_text1;
- AXNodeData static_text2;
- AXNodeData static_text3;
-
- root.id = 1;
- list.id = 2;
- list_item1.id = 3;
- list_marker1.id = 4;
- static_text1.id = 5;
- inline_box1.id = 6;
- static_text2.id = 7;
- inline_box2.id = 8;
- list_item2.id = 9;
- list_marker2.id = 10;
- static_text3.id = 11;
- inline_box3.id = 12;
-
- root.role = ax::mojom::Role::kRootWebArea;
- root.child_ids = {list.id};
-
- list.role = ax::mojom::Role::kList;
- list.child_ids = {list_item1.id, list_item2.id};
-
- list_item1.role = ax::mojom::Role::kListItem;
- list_item1.child_ids = {list_marker1.id, static_text2.id};
- list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
- true);
-
- list_marker1.role = ax::mojom::Role::kListMarker;
- list_marker1.child_ids = {static_text1.id};
-
- static_text1.role = ax::mojom::Role::kStaticText;
- static_text1.child_ids = {inline_box1.id};
-
- inline_box1.role = ax::mojom::Role::kInlineTextBox;
- inline_box1.SetName("1. ");
-
- static_text2.role = ax::mojom::Role::kStaticText;
- static_text2.child_ids = {inline_box2.id};
-
- inline_box2.role = ax::mojom::Role::kInlineTextBox;
- inline_box2.SetName("content");
-
- list_item2.role = ax::mojom::Role::kListItem;
- list_item2.child_ids = {list_marker2.id};
- list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
- true);
-
- list_marker2.role = ax::mojom::Role::kListMarker;
- list_marker2.child_ids = {static_text3.id};
-
- static_text3.role = ax::mojom::Role::kStaticText;
- static_text3.child_ids = {inline_box3.id};
-
- inline_box3.role = ax::mojom::Role::kInlineTextBox;
- inline_box3.SetName("2. ");
-
- SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
- inline_box1, static_text2, inline_box2, list_item2,
- list_marker2, static_text3, inline_box3}));
-
- TestPositionType text_position = AXNodePosition::CreateTextPosition(
- GetTreeID(), inline_box1.id, 3 /* text_offset */,
- ax::mojom::TextAffinity::kDownstream);
- ASSERT_NE(nullptr, text_position);
- ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_FALSE(text_position->AtEndOfParagraph());
-
- text_position = AXNodePosition::CreateTextPosition(
- GetTreeID(), inline_box3.id, 3 /* text_offset */,
- ax::mojom::TextAffinity::kDownstream);
- ASSERT_NE(nullptr, text_position);
- ASSERT_TRUE(text_position->IsTextPosition());
- ASSERT_TRUE(text_position->AtEndOfParagraph());
-}
-
TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
- // This test updates the tree structure to test a specific edge case -
- // At{Start|End}OfParagraph when an ancestor position can resolve to a
- // preserved newline descendant.
+ // This test ensures that "At{Start|End}OfParagraph" work correctly when a
+ // text position is on a preserved newline character.
+ //
+ // Newline characters are used to separate paragraphs. If there is a series of
+ // newline characters, a paragraph should start after the last newline
+ // character.
// ++1 kRootWebArea isLineBreakingObject
- // ++++2 kStaticText
+ // ++++2 kStaticText "some text"
// ++++++3 kInlineTextBox "some text"
// ++++4 kGenericContainer isLineBreakingObject
- // ++++++5 kStaticText
+ // ++++++5 kStaticText "\nmore text"
// ++++++++6 kInlineTextBox "\n" isLineBreakingObject
// ++++++++7 kInlineTextBox "more text"
+
AXNodeData root_data;
root_data.id = 1;
root_data.role = ax::mojom::Role::kRootWebArea;
@@ -2025,69 +1813,79 @@ TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
more_text_data.role = ax::mojom::Role::kInlineTextBox;
more_text_data.SetName("more text");
- static_text_data_1.child_ids = {3};
- container_data.child_ids = {5};
- static_text_data_2.child_ids = {6, 7};
- root_data.child_ids = {2, 4};
+ static_text_data_1.child_ids = {some_text_data.id};
+ container_data.child_ids = {static_text_data_2.id};
+ static_text_data_2.child_ids = {preserved_newline_data.id, more_text_data.id};
+ root_data.child_ids = {static_text_data_1.id, container_data.id};
SetTree(CreateAXTree({root_data, static_text_data_1, some_text_data,
container_data, static_text_data_2,
preserved_newline_data, more_text_data}));
+ // Text position "some tex<t>\nmore text".
TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
GetTreeID(), root_data.id, 8 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position1->AtEndOfParagraph());
EXPECT_FALSE(text_position1->AtStartOfParagraph());
+ // Text position "some text<\n>more text".
TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
GetTreeID(), root_data.id, 9 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position2->AtEndOfParagraph());
EXPECT_FALSE(text_position2->AtStartOfParagraph());
+ // Text position "some text<\n>more text".
TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
GetTreeID(), root_data.id, 9 /* text_offset */,
ax::mojom::TextAffinity::kUpstream);
EXPECT_FALSE(text_position3->AtEndOfParagraph());
EXPECT_FALSE(text_position3->AtStartOfParagraph());
+ // Text position "some text\n<m>ore text".
TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
GetTreeID(), root_data.id, 10 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position4->AtEndOfParagraph());
EXPECT_TRUE(text_position4->AtStartOfParagraph());
+ // Text position "some text\n<m>ore text".
TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
GetTreeID(), root_data.id, 10 /* text_offset */,
ax::mojom::TextAffinity::kUpstream);
EXPECT_TRUE(text_position5->AtEndOfParagraph());
EXPECT_FALSE(text_position5->AtStartOfParagraph());
+ // Text position "<\n>more text".
TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
GetTreeID(), container_data.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position6->AtEndOfParagraph());
EXPECT_FALSE(text_position6->AtStartOfParagraph());
+ // Text position "\n<m>ore text".
TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
GetTreeID(), container_data.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position7->AtEndOfParagraph());
EXPECT_TRUE(text_position7->AtStartOfParagraph());
+ // Text position "\n<m>ore text".
TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
GetTreeID(), container_data.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kUpstream);
EXPECT_TRUE(text_position8->AtEndOfParagraph());
EXPECT_FALSE(text_position8->AtStartOfParagraph());
+ // Text position "\n<m>ore text".
TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position9->AtEndOfParagraph());
EXPECT_TRUE(text_position9->AtStartOfParagraph());
+ // Text position "\n<m>ore text".
TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kUpstream);
@@ -2173,23 +1971,340 @@ TEST_F(
EXPECT_EQ(5, test_position->text_offset());
}
+TEST_F(AXPositionTest, AtStartOrEndOfParagraphOnAListMarker) {
+ // "AtStartOfParagraph" should return true before a list marker, either a
+ // Legacy Layout or an NG Layout one. It should return false on the next
+ // sibling of the list marker, i.e., before the list item's actual text
+ // contents.
+ //
+ // There are two list markers in the following test tree. The first one is a
+ // Legacy Layout one and the second an NG Layout one.
+ // ++1 kRootWebArea
+ // ++++2 kStaticText "Before list."
+ // ++++++3 kInlineTextBox "Before list."
+ // ++++4 kList
+ // ++++++5 kListItem
+ // ++++++++6 kListMarker
+ // ++++++++++7 kStaticText "1. "
+ // ++++++++++++8 kInlineTextBox "1. "
+ // ++++++++9 kStaticText "First item."
+ // ++++++++++10 kInlineTextBox "First item."
+ // ++++++11 kListItem
+ // ++++++++12 kListMarker "2. "
+ // ++++++++13 kStaticText "Second item."
+ // ++++++++++14 kInlineTextBox "Second item."
+ // ++15 kStaticText "After list."
+ // ++++16 kInlineTextBox "After list."
+
+ AXNodeData root;
+ AXNodeData list;
+ AXNodeData list_item1;
+ AXNodeData list_item2;
+ AXNodeData list_marker_legacy;
+ AXNodeData list_marker_ng;
+ AXNodeData static_text1;
+ AXNodeData static_text2;
+ AXNodeData static_text3;
+ AXNodeData static_text4;
+ AXNodeData static_text5;
+ AXNodeData inline_box1;
+ AXNodeData inline_box2;
+ AXNodeData inline_box3;
+ AXNodeData inline_box4;
+ AXNodeData inline_box5;
+
+ root.id = 1;
+ static_text1.id = 2;
+ inline_box1.id = 3;
+ list.id = 4;
+ list_item1.id = 5;
+ list_marker_legacy.id = 6;
+ static_text2.id = 7;
+ inline_box2.id = 8;
+ static_text3.id = 9;
+ inline_box3.id = 10;
+ list_item2.id = 11;
+ list_marker_ng.id = 12;
+ static_text4.id = 13;
+ inline_box4.id = 14;
+ static_text5.id = 15;
+ inline_box5.id = 16;
+
+ root.role = ax::mojom::Role::kRootWebArea;
+ root.child_ids = {static_text1.id, list.id, static_text5.id};
+ root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+
+ static_text1.role = ax::mojom::Role::kStaticText;
+ static_text1.child_ids = {inline_box1.id};
+ static_text1.SetName("Before list.");
+
+ inline_box1.role = ax::mojom::Role::kInlineTextBox;
+ inline_box1.SetName("Before list.");
+
+ list.role = ax::mojom::Role::kList;
+ list.child_ids = {list_item1.id, list_item2.id};
+
+ list_item1.role = ax::mojom::Role::kListItem;
+ list_item1.child_ids = {list_marker_legacy.id, static_text3.id};
+ list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+ true);
+
+ list_marker_legacy.role = ax::mojom::Role::kListMarker;
+ list_marker_legacy.child_ids = {static_text2.id};
+
+ static_text2.role = ax::mojom::Role::kStaticText;
+ static_text2.child_ids = {inline_box2.id};
+ static_text2.SetName("1. ");
+
+ inline_box2.role = ax::mojom::Role::kInlineTextBox;
+ inline_box2.SetName("1. ");
+ inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
+ inline_box3.id);
+
+ static_text3.role = ax::mojom::Role::kStaticText;
+ static_text3.child_ids = {inline_box3.id};
+ static_text3.SetName("First item.");
+
+ inline_box3.role = ax::mojom::Role::kInlineTextBox;
+ inline_box3.SetName("First item.");
+ inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
+ inline_box2.id);
+
+ list_item2.role = ax::mojom::Role::kListItem;
+ list_item2.child_ids = {list_marker_ng.id, static_text4.id};
+ list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+ true);
+
+ list_marker_ng.role = ax::mojom::Role::kListMarker;
+ list_marker_ng.SetName("2. ");
+ list_marker_ng.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
+ inline_box4.id);
+
+ static_text4.role = ax::mojom::Role::kStaticText;
+ static_text4.child_ids = {inline_box4.id};
+ static_text4.SetName("Second item.");
+
+ inline_box4.role = ax::mojom::Role::kInlineTextBox;
+ inline_box4.SetName("Second item.");
+ inline_box4.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
+ list_marker_ng.id);
+
+ static_text5.role = ax::mojom::Role::kStaticText;
+ static_text5.child_ids = {inline_box5.id};
+ static_text5.SetName("After list.");
+
+ inline_box5.role = ax::mojom::Role::kInlineTextBox;
+ inline_box5.SetName("After list.");
+
+ SetTree(CreateAXTree({root, static_text1, inline_box1, list, list_item1,
+ list_marker_legacy, static_text2, inline_box2,
+ static_text3, inline_box3, list_item2, list_marker_ng,
+ static_text4, inline_box4, static_text5, inline_box5}));
+
+ // A text position after the text "Before list.". It should not be equivalent
+ // to a position that is before the list itself, or before the first list
+ // bullet / item.
+ TestPositionType text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text1.id, 12 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position after the text "Before list.". It should not be equivalent
+ // to a position that is before the list itself, or before the first list
+ // bullet / item.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box1.id, 12 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position before the list.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A downstream text position after the list. It should resolve to a leaf
+ // position before the paragraph that comes after the list, so it should be
+ // "AtStartOfParagraph".
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list.id, 14 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // An upstream text position after the list. It should be "AtEndOfParagraph".
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list.id, 14 /* text_offset */,
+ ax::mojom::TextAffinity::kUpstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position before the first list bullet (the Legacy Layout one).
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list_marker_legacy.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list_marker_legacy.id, 1 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the first list bullet (the Legacy Layout one).
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text2.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text2.id, 2 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the first list bullet (the Legacy Layout one).
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box2.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box2.id, 3 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the second list bullet (the NG Layout one).
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list_marker_ng.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), list_marker_ng.id, 3 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the text contents of the first list item - not the
+ // bullet.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text3.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the text contents of the first list item - not the
+ // bullet.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box3.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position after the text contents of the first list item.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text3.id, 11 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position after the text contents of the first list item.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box3.id, 11 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position before the text contents of the second list item - not the
+ // bullet.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text4.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position before the text contents of the second list item - not the
+ // bullet.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box4.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+
+ // A text position after the text contents of the second list item.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), static_text4.id, 12 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position after the text contents of the second list item.
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box4.id, 12 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_FALSE(text_position->AtStartOfParagraph());
+ EXPECT_TRUE(text_position->AtEndOfParagraph());
+
+ // A text position before the text "After list.".
+ text_position = AXNodePosition::CreateTextPosition(
+ GetTreeID(), inline_box5.id, 0 /* text_offset */,
+ ax::mojom::TextAffinity::kDownstream);
+ ASSERT_NE(nullptr, text_position);
+ EXPECT_TRUE(text_position->AtStartOfParagraph());
+ EXPECT_FALSE(text_position->AtEndOfParagraph());
+}
+
TEST_F(AXPositionTest,
AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
- // This test updates the tree structure to test a specific edge case -
- // At{Start|End}OfParagraph when an ancestor position can resolve to a
- // preserved newline descendant.
+ // This test ensures that "At{Start|End}OfParagraph" work correctly when a
+ // text position is on a preserved newline character.
+ //
+ // Newline characters are used to separate paragraphs. If there is a series of
+ // newline characters, a paragraph should start after the last newline
+ // character.
// ++1 kRootWebArea isLineBreakingObject
// ++++2 kGenericContainer isLineBreakingObject
- // ++++++3 kStaticText
+ // ++++++3 kStaticText "\n"
// ++++++++4 kInlineTextBox "\n" isLineBreakingObject
// ++++5 kGenericContainer isLineBreakingObject
- // ++++++6 kStaticText
+ // ++++++6 kStaticText "some text"
// ++++++++7 kInlineTextBox "some"
// ++++++++8 kInlineTextBox " "
// ++++++++9 kInlineTextBox "text"
// ++++10 kGenericContainer isLineBreakingObject
- // ++++++11 kStaticText
+ // ++++++11 kStaticText "\n"
// ++++++++12 kInlineTextBox "\n" isLineBreakingObject
+
AXNodeData root_data;
root_data.id = 1;
root_data.role = ax::mojom::Role::kRootWebArea;
@@ -2275,60 +2390,74 @@ TEST_F(AXPositionTest,
inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
inline_text_data_b_3, inline_text_data_c}));
+ // Before the first "\n".
TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position1->AtEndOfParagraph());
EXPECT_TRUE(text_position1->AtStartOfParagraph());
+ // After the first "\n".
+ //
+ // Since the position is an "after text" position, it is similar to pressing
+ // the End key, (or Cmd-Right on Mac), while the caret is on the line break,
+ // so it should not be "AtStartOfParagraph".
TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_a.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_TRUE(text_position2->AtEndOfParagraph());
EXPECT_FALSE(text_position2->AtStartOfParagraph());
+ // Before "some".
TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position3->AtEndOfParagraph());
EXPECT_TRUE(text_position3->AtStartOfParagraph());
+ // After "some".
TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position4->AtEndOfParagraph());
EXPECT_FALSE(text_position4->AtStartOfParagraph());
+ // Before " ".
TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position5->AtEndOfParagraph());
EXPECT_FALSE(text_position5->AtStartOfParagraph());
+ // After " ".
TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position6->AtEndOfParagraph());
EXPECT_FALSE(text_position6->AtStartOfParagraph());
+ // Before "text".
TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position7->AtEndOfParagraph());
EXPECT_FALSE(text_position7->AtStartOfParagraph());
+ // After "text".
TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position8->AtEndOfParagraph());
EXPECT_FALSE(text_position8->AtStartOfParagraph());
+ // Before the second "\n".
TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position9->AtEndOfParagraph());
EXPECT_FALSE(text_position9->AtStartOfParagraph());
+ // After the second "\n".
TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_c.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
@@ -2337,21 +2466,25 @@ TEST_F(AXPositionTest,
}
TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
- // This test updates the tree structure to test a specific edge case -
- // At{Start|End}OfParagraph when there are ignored nodes present near
- // a paragraph boundary.
+ // This test ensures that "At{Start|End}OfParagraph" work correctly when there
+ // are ignored nodes present near a paragraph boundary.
+ //
+ // An ignored node that is between a given position and a paragraph boundary
+ // should not be taken into consideration. The position should be interpreted
+ // as being on the boundary.
// ++1 kRootWebArea isLineBreakingObject
- // ++++2 kGenericContainer ignored
- // ++++++3 kStaticText ignored
- // ++++++++4 kInlineTextBox "ignored text" ignored
- // ++++5 kGenericContainer
- // ++++++6 kStaticText
+ // ++++2 kGenericContainer ignored isLineBreakingObject
+ // ++++++3 kStaticText ignored "ignored text"
+ // ++++++++4 kInlineTextBox ignored "ignored text"
+ // ++++5 kGenericContainer isLineBreakingObject
+ // ++++++6 kStaticText "some text"
// ++++++++7 kInlineTextBox "some"
// ++++++++8 kInlineTextBox " "
// ++++++++9 kInlineTextBox "text"
- // ++++10 kGenericContainer ignored
- // ++++++11 kStaticText ignored
- // ++++++++12 kInlineTextBox "ignored text" ignored
+ // ++++10 kGenericContainer ignored isLineBreakingObject
+ // ++++++11 kStaticText ignored "ignored text"
+ // ++++++++12 kInlineTextBox ignored "ignored text"
+
AXNodeData root_data;
root_data.id = 1;
root_data.role = ax::mojom::Role::kRootWebArea;
@@ -2362,6 +2495,8 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
container_data_a.id = 2;
container_data_a.role = ax::mojom::Role::kGenericContainer;
container_data_a.AddState(ax::mojom::State::kIgnored);
+ container_data_a.AddBoolAttribute(
+ ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
AXNodeData static_text_data_a;
static_text_data_a.id = 3;
@@ -2378,6 +2513,8 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
AXNodeData container_data_b;
container_data_b.id = 5;
container_data_b.role = ax::mojom::Role::kGenericContainer;
+ container_data_b.AddBoolAttribute(
+ ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
AXNodeData static_text_data_b;
static_text_data_b.id = 6;
@@ -2403,6 +2540,8 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
container_data_c.id = 10;
container_data_c.role = ax::mojom::Role::kGenericContainer;
container_data_c.AddState(ax::mojom::State::kIgnored);
+ container_data_c.AddBoolAttribute(
+ ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
AXNodeData static_text_data_c;
static_text_data_c.id = 11;
@@ -2433,60 +2572,75 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
inline_text_data_b_3, inline_text_data_c}));
+ // Before "ignored text".
TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position1->AtEndOfParagraph());
EXPECT_FALSE(text_position1->AtStartOfParagraph());
+ // After "ignored text".
+ //
+ // Since the position is an "after text" position, it is similar to pressing
+ // the End key, (or Cmd-Right on Mac), while the caret is on "ignored text",
+ // so it should not be "AtStartOfParagraph". In practice, this situation
+ // should not arise in accessibility, because the node is ignored.
TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_a.id, 12 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position2->AtEndOfParagraph());
EXPECT_FALSE(text_position2->AtStartOfParagraph());
+ // Before "some".
TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position3->AtEndOfParagraph());
EXPECT_TRUE(text_position3->AtStartOfParagraph());
+ // After "some".
TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position4->AtEndOfParagraph());
EXPECT_FALSE(text_position4->AtStartOfParagraph());
+ // Before " ".
TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position5->AtEndOfParagraph());
EXPECT_FALSE(text_position5->AtStartOfParagraph());
+ // After " ".
TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position6->AtEndOfParagraph());
EXPECT_FALSE(text_position6->AtStartOfParagraph());
+ // Before "text".
TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position7->AtEndOfParagraph());
EXPECT_FALSE(text_position7->AtStartOfParagraph());
+ // After "text".
TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_TRUE(text_position8->AtEndOfParagraph());
EXPECT_FALSE(text_position8->AtStartOfParagraph());
+ // Before "ignored text" - the second version.
TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position9->AtEndOfParagraph());
EXPECT_FALSE(text_position9->AtStartOfParagraph());
+ // After "ignored text" - the second version.
TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_text_data_c.id, 12 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
@@ -2497,16 +2651,20 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
- // This test updates the tree structure to test a specific edge case -
- // At{Start|End}OfParagraph when there are ignored nodes present near
- // a paragraph boundary.
+ // This test ensures that "At{Start|End}OfParagraph" work correctly when there
+ // are embedded objects present near a paragraph boundary.
+ //
+ // Nodes represented by an embedded object character, such as a plain text
+ // field or a check box, should create an implicit paragraph boundary for
+ // assistive software.
// ++1 kRootWebArea isLineBreakingObject
// ++++2 kLink
- // ++++++3 kStaticText
+ // ++++++3 kStaticText "hello"
// ++++++++4 kInlineTextBox "hello"
// ++++++5 kImage
- // ++++++6 kStaticText
+ // ++++++6 kStaticText "world"
// ++++++++7 kInlineTextBox "world"
+
AXNodeData root_1;
AXNodeData link_2;
AXNodeData static_text_3;
@@ -2525,6 +2683,8 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
root_1.role = ax::mojom::Role::kRootWebArea;
root_1.child_ids = {link_2.id};
+ root_1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+ true);
link_2.role = ax::mojom::Role::kLink;
link_2.child_ids = {static_text_3.id, image_5.id, static_text_6.id};
@@ -2537,6 +2697,7 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
inline_box_4.SetName("Hello");
image_5.role = ax::mojom::Role::kImage;
+ // The image's inner text should be an embedded object character.
static_text_6.role = ax::mojom::Role::kStaticText;
static_text_6.child_ids = {inline_box_7.id};
@@ -2548,36 +2709,47 @@ TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
SetTree(CreateAXTree({root_1, link_2, static_text_3, inline_box_4, image_5,
static_text_6, inline_box_7}));
+ // Before "hello".
TestPositionType text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_box_4.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position->AtEndOfParagraph());
EXPECT_TRUE(text_position->AtStartOfParagraph());
+ // After "hello".
+ //
+ // Note that even though this position and a position before the image's
+ // embedded object character are conceptually equivalent, in practice they
+ // should result from two different ancestor positions. The former should have
+ // been an upstream position, whilst the latter a downstream one.
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_box_4.id, 5 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_TRUE(text_position->AtEndOfParagraph());
EXPECT_FALSE(text_position->AtStartOfParagraph());
+ // Before the image's embedded object character.
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), image_5.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position->AtEndOfParagraph());
EXPECT_TRUE(text_position->AtStartOfParagraph());
+ // After the image's embedded object character.
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), image_5.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_TRUE(text_position->AtEndOfParagraph());
EXPECT_FALSE(text_position->AtStartOfParagraph());
+ // Before "world".
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_box_7.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
EXPECT_FALSE(text_position->AtEndOfParagraph());
EXPECT_TRUE(text_position->AtStartOfParagraph());
+ // After "world".
text_position = AXNodePosition::CreateTextPosition(
GetTreeID(), inline_box_7.id, 5 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
diff --git a/chromium/ui/accessibility/ax_position.h b/chromium/ui/accessibility/ax_position.h
index dfeed6edfde..c76e3fb8624 100644
--- a/chromium/ui/accessibility/ax_position.h
+++ b/chromium/ui/accessibility/ax_position.h
@@ -823,6 +823,38 @@ class AXPosition {
}
}
+ bool AtStartOfAXTree() const {
+ if (IsNullPosition())
+ return false;
+
+ if (AtStartOfAnchor()) {
+ AXPositionInstance previous_anchor = CreatePreviousAnchorPosition();
+
+ // Consider the start of the document as the start of an AXTree.
+ if (previous_anchor->IsNullPosition())
+ return true;
+ else
+ return previous_anchor->tree_id() != tree_id();
+ }
+ return false;
+ }
+
+ bool AtEndOfAXTree() const {
+ if (IsNullPosition())
+ return false;
+
+ if (AtEndOfAnchor()) {
+ AXPositionInstance next_anchor = CreateNextAnchorPosition();
+
+ // Consider the end of the document as the end of an AXTree.
+ if (next_anchor->IsNullPosition())
+ return true;
+ else
+ return next_anchor->tree_id() != tree_id();
+ }
+ return false;
+ }
+
AXBoundaryType GetFormatStartBoundaryType() const {
// Since formats are stored on text anchors, the start of a format boundary
// must be at the start of an anchor.
@@ -1669,6 +1701,63 @@ class AXPosition {
return CreateNullPosition();
}
+ AXPositionInstance CreatePositionAtStartOfAXTree() const {
+ if (IsNullPosition() || AtStartOfAXTree())
+ return Clone();
+
+ // First check for positions on nodes which are AXTree boundaries, but where
+ // the text position on that node is not at the start of the anchor.
+ if (CreatePositionAtStartOfAnchor()->AtStartOfAXTree())
+ return CreatePositionAtStartOfAnchor();
+
+ // Iterate over tree positions until a boundary is reached.
+ AXPositionInstance previous_position = AsTreePosition();
+ do {
+ previous_position = previous_position->CreatePreviousAnchorPosition();
+ } while (!previous_position->AtStartOfAXTree());
+
+ // This method should not cross tree boundaries.
+ DCHECK_EQ(previous_position->tree_id(), tree_id());
+
+ if (IsTextPosition())
+ previous_position = previous_position->AsTextPosition();
+ return previous_position;
+ }
+
+ AXPositionInstance CreatePositionAtEndOfAXTree() const {
+ if (IsNullPosition() || AtEndOfAXTree())
+ return Clone();
+
+ // First check for positions on nodes which are AXTree boundaries, but where
+ // the text position on that node is not at the end of the anchor.
+ if (CreatePositionAtEndOfAnchor()->AtEndOfAXTree())
+ return CreatePositionAtEndOfAnchor();
+
+ // Iterate over tree positions until a boundary is reached.
+ AXPositionInstance next_position = AsTreePosition();
+ do {
+ next_position = next_position->CreateNextAnchorPosition()
+ ->CreatePositionAtEndOfAnchor();
+ } while (!next_position->AtEndOfAXTree());
+
+ // This method should not cross tree boundaries.
+ DCHECK_EQ(next_position->tree_id(), tree_id());
+
+ if (IsTextPosition())
+ next_position = next_position->AsTextPosition();
+ return next_position->CreatePositionAtEndOfAnchor();
+ }
+
+ // "document" is defined here as a single, top-level, navigatable unit from
+ // a user's perspective. This means that all iframes are part of a single
+ // "document" that contains the top-level navigatable page. So this method
+ // will break out of an iframe and return a position at the start of the
+ // top-level document.
+ //
+ // Note that this definition is different than HTML's definition of
+ // "document", where each iframe has its own document object. For a similar
+ // method that stops at iframe boundaries, see
+ // CreatePositionAtStartOfAXTree().
AXPositionInstance CreatePositionAtStartOfDocument() const {
AXPositionInstance position =
AsTreePosition()->CreateDocumentAncestorPosition();
@@ -1680,6 +1769,15 @@ class AXPosition {
return position;
}
+ // "document" is defined here as a single, top-level, navigatable unit from
+ // a user's perspective. This means that all iframes are part of a single
+ // "document" that contains the top-level navigatable page. So this method
+ // will break out of an iframe and return a position at the end of the
+ // top-level document.
+ //
+ // Note that this definition is different than HTML's definition of
+ // "document", where each iframe has its own document object. For a similar
+ // method that stops at iframe boundaries, see CreatePositionAtEndOfAXTree().
AXPositionInstance CreatePositionAtEndOfDocument() const {
AXPositionInstance position =
AsTreePosition()->CreateDocumentAncestorPosition();
@@ -3262,7 +3360,7 @@ class AXPosition {
// Returns whether or not this anchor is represented in their parent with a
// single embedded object character.
- virtual bool IsEmbeddedObjectInParent() const { return false; }
+ virtual bool IsEmbeddedObjectInParent() const = 0;
// Determines if the anchor containing this position produces a hard line
// break in the text representation, e.g. a block level element or a <br>.
diff --git a/chromium/ui/accessibility/ax_range.h b/chromium/ui/accessibility/ax_range.h
index 62db9d3c0ff..2d263e1c7ea 100644
--- a/chromium/ui/accessibility/ax_range.h
+++ b/chromium/ui/accessibility/ax_range.h
@@ -131,6 +131,12 @@ class AXRange {
: AXRange(anchor_->Clone(), focus_->Clone());
}
+ AXRange AsBackwardRange() const {
+ return (CompareEndpoints(anchor(), focus()).value_or(0) < 0)
+ ? AXRange(focus_->Clone(), anchor_->Clone())
+ : AXRange(anchor_->Clone(), focus_->Clone());
+ }
+
bool IsCollapsed() const { return !IsNull() && *anchor_ == *focus_; }
// We define a "leaf text range" as an AXRange whose endpoints are leaf text
diff --git a/chromium/ui/accessibility/ax_role_properties.cc b/chromium/ui/accessibility/ax_role_properties.cc
index 60115f31327..b9756d9ce23 100644
--- a/chromium/ui/accessibility/ax_role_properties.cc
+++ b/chromium/ui/accessibility/ax_role_properties.cc
@@ -403,6 +403,16 @@ bool IsPresentational(const ax::mojom::Role role) {
}
}
+bool IsRadio(const ax::mojom::Role role) {
+ switch (role) {
+ case ax::mojom::Role::kRadioButton:
+ case ax::mojom::Role::kMenuItemRadio:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool IsRangeValueSupported(const ax::mojom::Role role) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-valuenow
// https://www.w3.org/TR/wai-aria-1.1/#aria-valuetext
@@ -433,7 +443,6 @@ bool IsReadOnlySupported(const ax::mojom::Role role) {
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kInputTime:
case ax::mojom::Role::kListBox:
- case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kMenuListPopup:
diff --git a/chromium/ui/accessibility/ax_role_properties.h b/chromium/ui/accessibility/ax_role_properties.h
index 60e8e570e62..724de523b7d 100644
--- a/chromium/ui/accessibility/ax_role_properties.h
+++ b/chromium/ui/accessibility/ax_role_properties.h
@@ -113,6 +113,9 @@ AX_BASE_EXPORT bool IsMenuRelated(const ax::mojom::Role role);
// API.
AX_BASE_EXPORT bool IsPresentational(const ax::mojom::Role role);
+// Returns true if the provided role belongs to a radio.
+AX_BASE_EXPORT bool IsRadio(const ax::mojom::Role role);
+
// Returns true if the provided role supports a range-based value, such as a
// slider.
AX_BASE_EXPORT bool IsRangeValueSupported(const ax::mojom::Role role);
diff --git a/chromium/ui/accessibility/ax_table_fuzzer.cc b/chromium/ui/accessibility/ax_table_fuzzer.cc
index 1ee955ee38f..b3b42248e8b 100644
--- a/chromium/ui/accessibility/ax_table_fuzzer.cc
+++ b/chromium/ui/accessibility/ax_table_fuzzer.cc
@@ -115,7 +115,7 @@ void TestTableAPIs(const ui::AXNode* node) {
ignore_result(node->IsTableRow());
ignore_result(node->GetTableRowRowIndex());
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
ignore_result(node->IsTableColumn());
ignore_result(node->GetTableColColIndex());
#endif
diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc
index 003f2770723..81ecc9ca156 100644
--- a/chromium/ui/accessibility/ax_tree.cc
+++ b/chromium/ui/accessibility/ax_tree.cc
@@ -2092,7 +2092,8 @@ void AXTree::ComputeSetSizePosInSetAndCache(const AXNode& node,
// would like it to inherit the SetSize from the kMenuListPopUp it wraps. To
// do this, we treat the kMenuListPopUp as the ordered_set and eventually
// assign its SetSize value to the kPopUpButton.
- if (node.data().role == ax::mojom::Role::kPopUpButton) {
+ if (node.data().role == ax::mojom::Role::kPopUpButton &&
+ node.GetUnignoredChildCount() > 0) {
// kPopUpButtons are only allowed to contain one kMenuListPopUp.
// The single element is guaranteed to be a kMenuListPopUp because that is
// the only item role that matches the ordered set role of kPopUpButton.
@@ -2208,6 +2209,12 @@ void AXTree::ComputeSetSizePosInSetAndCacheHelper(
}
base::Optional<int> AXTree::GetPosInSet(const AXNode& node) {
+ if (node.data().role == ax::mojom::Role::kPopUpButton &&
+ node.GetUnignoredChildCount() == 0 &&
+ node.HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) {
+ return node.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
+ }
+
if (node_set_size_pos_in_set_info_map_.find(node.id()) !=
node_set_size_pos_in_set_info_map_.end()) {
// If item's id is in the cache, return stored PosInSet value.
@@ -2237,6 +2244,12 @@ base::Optional<int> AXTree::GetPosInSet(const AXNode& node) {
}
base::Optional<int> AXTree::GetSetSize(const AXNode& node) {
+ if (node.data().role == ax::mojom::Role::kPopUpButton &&
+ node.GetUnignoredChildCount() == 0 &&
+ node.HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) {
+ return node.GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
+ }
+
if (node_set_size_pos_in_set_info_map_.find(node.id()) !=
node_set_size_pos_in_set_info_map_.end()) {
// If item's id is in the cache, return stored SetSize value.
@@ -2262,6 +2275,23 @@ base::Optional<int> AXTree::GetSetSize(const AXNode& node) {
if (!ordered_set)
return base::nullopt;
+ // For popup buttons that control a single element, inherit the controlled
+ // item's SetSize. Skip this block if the popup button controls itself.
+ if (node.data().role == ax::mojom::Role::kPopUpButton) {
+ const auto& controls_ids = node.data().GetIntListAttribute(
+ ax::mojom::IntListAttribute::kControlsIds);
+ if (controls_ids.size() == 1 && GetFromId(controls_ids[0]) &&
+ controls_ids[0] != node.id()) {
+ const AXNode& controlled_item = *GetFromId(controls_ids[0]);
+
+ base::Optional<int> controlled_item_set_size =
+ GetSetSize(controlled_item);
+ node_set_size_pos_in_set_info_map_[node.id()].set_size =
+ controlled_item_set_size;
+ return controlled_item_set_size;
+ }
+ }
+
// Compute, cache, then return.
ComputeSetSizePosInSetAndCache(node, ordered_set);
base::Optional<int> set_size =
diff --git a/chromium/ui/accessibility/ax_tree_combiner.cc b/chromium/ui/accessibility/ax_tree_combiner.cc
index 23200e1d907..9c251d1e757 100644
--- a/chromium/ui/accessibility/ax_tree_combiner.cc
+++ b/chromium/ui/accessibility/ax_tree_combiner.cc
@@ -17,6 +17,11 @@ AXTreeCombiner::~AXTreeCombiner() {
}
void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) {
+ if (tree.tree_data.tree_id == AXTreeIDUnknown()) {
+ LOG(WARNING) << "Skipping AXTreeID because its tree ID is unknown";
+ return;
+ }
+
trees_.push_back(tree);
if (is_root) {
DCHECK_EQ(root_tree_id_, AXTreeIDUnknown());
diff --git a/chromium/ui/accessibility/ax_tree_data.h b/chromium/ui/accessibility/ax_tree_data.h
index 031eee5a60b..4f94423ec58 100644
--- a/chromium/ui/accessibility/ax_tree_data.h
+++ b/chromium/ui/accessibility/ax_tree_data.h
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
+#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
diff --git a/chromium/ui/accessibility/ax_tree_id.cc b/chromium/ui/accessibility/ax_tree_id.cc
index 3cb2335c798..d829f43da91 100644
--- a/chromium/ui/accessibility/ax_tree_id.cc
+++ b/chromium/ui/accessibility/ax_tree_id.cc
@@ -43,6 +43,11 @@ AXTreeID AXTreeID::FromString(const std::string& string) {
}
// static
+AXTreeID AXTreeID::FromToken(const base::UnguessableToken& token) {
+ return AXTreeID(token.ToString());
+}
+
+// static
AXTreeID AXTreeID::CreateNewAXTreeID() {
return AXTreeID(ax::mojom::AXTreeIDType::kToken);
}
diff --git a/chromium/ui/accessibility/ax_tree_id.h b/chromium/ui/accessibility/ax_tree_id.h
index 4908d69ea31..354ef7c6643 100644
--- a/chromium/ui/accessibility/ax_tree_id.h
+++ b/chromium/ui/accessibility/ax_tree_id.h
@@ -9,8 +9,8 @@
#include "base/no_destructor.h"
#include "base/unguessable_token.h"
+#include "ui/accessibility/ax_base_export.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
-#include "ui/accessibility/ax_export.h"
namespace mojo {
template <typename DataViewType, typename T>
@@ -26,7 +26,7 @@ class AXTreeIDDataView;
namespace ui {
// A unique ID representing an accessibility tree.
-class AX_EXPORT AXTreeID {
+class AX_BASE_EXPORT AXTreeID {
public:
// Create an Unknown AXTreeID.
AXTreeID();
@@ -43,6 +43,9 @@ class AX_EXPORT AXTreeID {
// automation API.
static AXTreeID FromString(const std::string& string);
+ // Convenience method to unserialize an AXTreeID from an UnguessableToken.
+ static AXTreeID FromToken(const base::UnguessableToken& token);
+
AXTreeID& operator=(const AXTreeID& other);
std::string ToString() const;
@@ -70,14 +73,15 @@ class AX_EXPORT AXTreeID {
};
// For use in std::unordered_map.
-struct AXTreeIDHash {
+struct AX_BASE_EXPORT AXTreeIDHash {
size_t operator()(const ui::AXTreeID& tree_id) const;
};
-AX_EXPORT std::ostream& operator<<(std::ostream& stream, const AXTreeID& value);
+AX_BASE_EXPORT std::ostream& operator<<(std::ostream& stream,
+ const AXTreeID& value);
// The value to use when an AXTreeID is unknown.
-AX_EXPORT extern const AXTreeID& AXTreeIDUnknown();
+AX_BASE_EXPORT extern const AXTreeID& AXTreeIDUnknown();
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_tree_id_registry.cc b/chromium/ui/accessibility/ax_tree_id_registry.cc
index da654466e7c..ca706c3d6a2 100644
--- a/chromium/ui/accessibility/ax_tree_id_registry.cc
+++ b/chromium/ui/accessibility/ax_tree_id_registry.cc
@@ -6,7 +6,7 @@
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
-#include "ui/accessibility/ax_action_handler.h"
+#include "ui/accessibility/ax_action_handler_base.h"
namespace ui {
@@ -44,23 +44,29 @@ AXTreeID AXTreeIDRegistry::GetAXTreeID(AXTreeIDRegistry::FrameID frame_id) {
return ui::AXTreeIDUnknown();
}
-AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID(AXActionHandler* handler) {
+AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID(AXActionHandlerBase* handler) {
for (auto it : id_to_action_handler_) {
if (it.second == handler)
return it.first;
}
AXTreeID new_id = AXTreeID::CreateNewAXTreeID();
- id_to_action_handler_[new_id] = handler;
+ SetAXTreeID(new_id, handler);
return new_id;
}
-AXActionHandler* AXTreeIDRegistry::GetActionHandler(AXTreeID ax_tree_id) {
+AXActionHandlerBase* AXTreeIDRegistry::GetActionHandler(AXTreeID ax_tree_id) {
auto it = id_to_action_handler_.find(ax_tree_id);
if (it == id_to_action_handler_.end())
return nullptr;
return it->second;
}
+void AXTreeIDRegistry::SetAXTreeID(const ui::AXTreeID& id,
+ AXActionHandlerBase* action_handler) {
+ DCHECK(id_to_action_handler_.find(id) == id_to_action_handler_.end());
+ id_to_action_handler_[id] = action_handler;
+}
+
void AXTreeIDRegistry::RemoveAXTreeID(AXTreeID ax_tree_id) {
auto frame_it = ax_tree_to_frame_id_map_.find(ax_tree_id);
if (frame_it != ax_tree_to_frame_id_map_.end()) {
diff --git a/chromium/ui/accessibility/ax_tree_id_registry.h b/chromium/ui/accessibility/ax_tree_id_registry.h
index 44405bd7c69..8ccbfee3d84 100644
--- a/chromium/ui/accessibility/ax_tree_id_registry.h
+++ b/chromium/ui/accessibility/ax_tree_id_registry.h
@@ -10,6 +10,7 @@
#include <utility>
#include "base/macros.h"
+#include "ui/accessibility/ax_action_handler.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_id.h"
@@ -20,12 +21,12 @@ struct DefaultSingletonTraits;
namespace ui {
-class AXActionHandler;
+class AXActionHandlerBase;
// This class generates and saves a runtime id for an accessibility tree.
// It provides a few distinct forms of generating an id:
// - from a frame id (which consists of a process and routing id)
-// - from a backing |AXActionHandler| object
+// - from a backing |AXActionHandlerBase| object
//
// The first form allows underlying instances to change but refer to the same
// frame.
@@ -43,8 +44,8 @@ class AX_EXPORT AXTreeIDRegistry {
// Gets an ax tree id from a frame id.
AXTreeID GetAXTreeID(FrameID frame_id);
- // Retrieve an |AXActionHandler| based on an ax tree id.
- AXActionHandler* GetActionHandler(AXTreeID ax_tree_id);
+ // Retrieve an |AXActionHandlerBase| based on an ax tree id.
+ AXActionHandlerBase* GetActionHandler(AXTreeID ax_tree_id);
// Removes an ax tree id, and its associated delegate and frame id (if it
// exists).
@@ -57,9 +58,14 @@ class AX_EXPORT AXTreeIDRegistry {
private:
friend struct base::DefaultSingletonTraits<AXTreeIDRegistry>;
friend AXActionHandler;
+ friend AXActionHandlerBase;
// Get or create a ax tree id keyed on |handler|.
- AXTreeID GetOrCreateAXTreeID(AXActionHandler* handler);
+ AXTreeID GetOrCreateAXTreeID(AXActionHandlerBase* handler);
+
+ // Set a mapping between an AXTreeID and AXActionHandlerBase explicitly.
+ void SetAXTreeID(const AXTreeID& ax_tree_id,
+ AXActionHandlerBase* action_handler);
AXTreeIDRegistry();
virtual ~AXTreeIDRegistry();
@@ -71,7 +77,7 @@ class AX_EXPORT AXTreeIDRegistry {
std::map<FrameID, AXTreeID> frame_to_ax_tree_id_map_;
// Maps an id to its handler.
- std::map<AXTreeID, AXActionHandler*> id_to_action_handler_;
+ std::map<AXTreeID, AXActionHandlerBase*> id_to_action_handler_;
DISALLOW_COPY_AND_ASSIGN(AXTreeIDRegistry);
};
diff --git a/chromium/ui/accessibility/ax_tree_serializer.h b/chromium/ui/accessibility/ax_tree_serializer.h
index f4fb10a7c7b..38f34a5fb64 100644
--- a/chromium/ui/accessibility/ax_tree_serializer.h
+++ b/chromium/ui/accessibility/ax_tree_serializer.h
@@ -108,6 +108,11 @@ class AXTreeSerializer {
void ChangeTreeSourceForTesting(
AXTreeSource<AXSourceNode, AXNodeData, AXTreeData>* new_tree);
+ // Returns the number of nodes in the client tree. After a serialization
+ // operation this should be an accurate representation of the tree source
+ // as explored by the serializer.
+ size_t ClientTreeNodeCount() const;
+
private:
// Return the least common ancestor of a node in the source tree
// and a node in the client tree, or nullptr if there is no such node.
@@ -221,6 +226,9 @@ AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::AXTreeSerializer(
template <typename AXSourceNode, typename AXNodeData, typename AXTreeData>
AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::~AXTreeSerializer() {
+ // Clear |tree_| to prevent any additional calls to the tree source
+ // during teardown.
+ tree_ = nullptr;
Reset();
}
@@ -238,8 +246,11 @@ void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::InternalReset() {
// but Reset() needs to work even if the tree is in a broken state.
// Instead, iterate over |client_id_map_| to ensure we clear all nodes and
// start from scratch.
- for (auto&& item : client_id_map_)
+ for (auto&& item : client_id_map_) {
+ if (tree_)
+ tree_->SerializerClearedNode(item.first);
delete item.second;
+ }
client_id_map_.clear();
client_root_ = nullptr;
}
@@ -252,6 +263,13 @@ void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::
}
template <typename AXSourceNode, typename AXNodeData, typename AXTreeData>
+size_t
+AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::ClientTreeNodeCount()
+ const {
+ return client_id_map_.size();
+}
+
+template <typename AXSourceNode, typename AXNodeData, typename AXTreeData>
AXSourceNode
AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::LeastCommonAncestor(
AXSourceNode node,
@@ -483,6 +501,7 @@ void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::
Reset(); // Do not try to reuse a bad root later.
} else {
DeleteDescendants(client_node);
+ tree_->SerializerClearedNode(client_node->id);
client_id_map_.erase(client_node->id);
delete client_node;
}
diff --git a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
index e4ad28503ac..079035623e1 100644
--- a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
+++ b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc
@@ -11,11 +11,14 @@
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_serializable_tree.h"
#include "ui/accessibility/ax_tree.h"
+using testing::UnorderedElementsAre;
+
namespace ui {
using BasicAXTreeSerializer =
@@ -468,4 +471,159 @@ TEST_F(AXTreeSerializerTest, ResetWorksWithNewRootId) {
EXPECT_TRUE(dst_tree.Unserialize(update)) << dst_tree.error();
}
+// Wraps an AXTreeSource and provides access to the results of the
+// SerializerClearedNode callback.
+class AXTreeSourceTestWrapper
+ : public AXTreeSource<const AXNode*, AXNodeData, AXTreeData> {
+ public:
+ explicit AXTreeSourceTestWrapper(
+ AXTreeSource<const AXNode*, AXNodeData, AXTreeData>* tree_source)
+ : tree_source_(tree_source) {}
+ ~AXTreeSourceTestWrapper() override = default;
+
+ // Override SerializerClearedNode and provide a way to access it.
+ void SerializerClearedNode(int32_t node_id) override {
+ cleared_node_ids_.insert(node_id);
+ }
+
+ void ClearClearedNodeIds() { cleared_node_ids_.clear(); }
+ std::set<int32_t>& cleared_node_ids() { return cleared_node_ids_; }
+
+ // The rest of the AXTreeSource implementation just calls through to
+ // tree_source_.
+ bool GetTreeData(AXTreeData* data) const override {
+ return tree_source_->GetTreeData(data);
+ }
+ const AXNode* GetRoot() const override { return tree_source_->GetRoot(); }
+ const AXNode* GetFromId(int32_t id) const override {
+ return tree_source_->GetFromId(id);
+ }
+ int32_t GetId(const AXNode* node) const override {
+ return tree_source_->GetId(node);
+ }
+ void GetChildren(const AXNode* node,
+ std::vector<const AXNode*>* out_children) const override {
+ return tree_source_->GetChildren(node, out_children);
+ }
+ const AXNode* GetParent(const AXNode* node) const override {
+ return tree_source_->GetParent(node);
+ }
+ bool IsIgnored(const AXNode* node) const override {
+ return tree_source_->IsIgnored(node);
+ }
+ bool IsValid(const AXNode* node) const override {
+ return tree_source_->IsValid(node);
+ }
+ bool IsEqual(const AXNode* node1, const AXNode* node2) const override {
+ return tree_source_->IsEqual(node1, node2);
+ }
+ const AXNode* GetNull() const override { return tree_source_->GetNull(); }
+ void SerializeNode(const AXNode* node, AXNodeData* out_data) const override {
+ tree_source_->SerializeNode(node, out_data);
+ }
+
+ private:
+ AXTreeSource<const AXNode*, AXNodeData, AXTreeData>* tree_source_;
+ std::set<int32_t> cleared_node_ids_;
+};
+
+TEST_F(AXTreeSerializerTest, TestClearedNodesWhenUpdatingRoot) {
+ // (1 (2 (3 (4))))
+ treedata0_.root_id = 1;
+ treedata0_.nodes.resize(4);
+ treedata0_.nodes[0].id = 1;
+ treedata0_.nodes[0].child_ids.push_back(2);
+ treedata0_.nodes[1].id = 2;
+ treedata0_.nodes[1].child_ids.push_back(3);
+ treedata0_.nodes[2].id = 3;
+ treedata0_.nodes[2].child_ids.push_back(4);
+ treedata0_.nodes[3].id = 4;
+
+ // (5 (2 (3 (4))))
+ treedata1_.root_id = 5;
+ treedata1_.nodes.resize(4);
+ treedata1_.nodes[0].id = 5;
+ treedata1_.nodes[0].child_ids.push_back(2);
+ treedata1_.nodes[1].id = 2;
+ treedata1_.nodes[1].child_ids.push_back(3);
+ treedata1_.nodes[2].id = 3;
+ treedata1_.nodes[2].child_ids.push_back(4);
+ treedata1_.nodes[3].id = 4;
+
+ // Similar sequence to CreateTreeSerializer, but using
+ // AXTreeSourceTestWrapper instead.
+ tree0_ = std::make_unique<AXSerializableTree>(treedata0_);
+ tree1_ = std::make_unique<AXSerializableTree>(treedata1_);
+ tree0_source_.reset(tree0_->CreateTreeSource());
+ AXTreeSourceTestWrapper tree0_source_wrapper(tree0_source_.get());
+ serializer_ = std::make_unique<BasicAXTreeSerializer>(&tree0_source_wrapper);
+ AXTreeUpdate unused_update;
+ ASSERT_TRUE(serializer_->SerializeChanges(tree0_->root(), &unused_update));
+ tree1_source_.reset(tree1_->CreateTreeSource());
+ AXTreeSourceTestWrapper tree1_source_wrapper(tree1_source_.get());
+ serializer_->ChangeTreeSourceForTesting(&tree1_source_wrapper);
+ ASSERT_EQ(4U, serializer_->ClientTreeNodeCount());
+
+ // If we swap out the root, all of the node IDs should have
+ // SerializerClearedNode called on them.
+ tree1_source_wrapper.ClearClearedNodeIds();
+ ASSERT_TRUE(serializer_->SerializeChanges(tree1_->root(), &unused_update));
+ EXPECT_THAT(tree1_source_wrapper.cleared_node_ids(),
+ UnorderedElementsAre(1, 2, 3, 4));
+
+ // Destroy the serializer first so that the AXTreeSources it points to
+ // don't go out of scope first.
+ serializer_.reset();
+}
+
+TEST_F(AXTreeSerializerTest, TestClearedNodesWhenUpdatingBranch) {
+ // (1 (2 (3 (4))))
+ treedata0_.root_id = 1;
+ treedata0_.nodes.resize(4);
+ treedata0_.nodes[0].id = 1;
+ treedata0_.nodes[0].child_ids.push_back(2);
+ treedata0_.nodes[1].id = 2;
+ treedata0_.nodes[1].child_ids.push_back(3);
+ treedata0_.nodes[2].id = 3;
+ treedata0_.nodes[2].child_ids.push_back(4);
+ treedata0_.nodes[3].id = 4;
+
+ // (1 (2 (5 (6))))
+ treedata1_.root_id = 1;
+ treedata1_.nodes.resize(4);
+ treedata1_.nodes[0].id = 1;
+ treedata1_.nodes[0].child_ids.push_back(2);
+ treedata1_.nodes[1].id = 2;
+ treedata1_.nodes[1].child_ids.push_back(5);
+ treedata1_.nodes[2].id = 5;
+ treedata1_.nodes[2].child_ids.push_back(6);
+ treedata1_.nodes[3].id = 6;
+
+ // Similar sequence to CreateTreeSerializer, but using
+ // AXTreeSourceTestWrapper instead.
+ tree0_ = std::make_unique<AXSerializableTree>(treedata0_);
+ tree1_ = std::make_unique<AXSerializableTree>(treedata1_);
+ tree0_source_.reset(tree0_->CreateTreeSource());
+ AXTreeSourceTestWrapper tree0_source_wrapper(tree0_source_.get());
+ serializer_ = std::make_unique<BasicAXTreeSerializer>(&tree0_source_wrapper);
+ AXTreeUpdate unused_update;
+ ASSERT_TRUE(serializer_->SerializeChanges(tree0_->root(), &unused_update));
+ tree1_source_.reset(tree1_->CreateTreeSource());
+ AXTreeSourceTestWrapper tree1_source_wrapper(tree1_source_.get());
+ serializer_->ChangeTreeSourceForTesting(&tree1_source_wrapper);
+ ASSERT_EQ(4U, serializer_->ClientTreeNodeCount());
+
+ // If we replace one branch with another, we should get calls to
+ // SerializerClearedNode with all of the node IDs no longer in the tree.
+ tree1_source_wrapper.ClearClearedNodeIds();
+ ASSERT_TRUE(
+ serializer_->SerializeChanges(tree1_->GetFromId(2), &unused_update));
+ EXPECT_THAT(tree1_source_wrapper.cleared_node_ids(),
+ UnorderedElementsAre(3, 4));
+
+ // Destroy the serializer first so that the AXTreeSources it points to
+ // don't go out of scope first.
+ serializer_.reset();
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/ax_tree_source.h b/chromium/ui/accessibility/ax_tree_source.h
index 160c416e32f..357bfccf6f4 100644
--- a/chromium/ui/accessibility/ax_tree_source.h
+++ b/chromium/ui/accessibility/ax_tree_source.h
@@ -68,6 +68,12 @@ class AXTreeSource {
return node_data.ToString();
}
+ // This is called by AXTreeSerializer when it serializes a tree and
+ // discovers that a node previously in the tree is no longer part of
+ // the tree. It can be used to allow an AXTreeSource to keep a cache
+ // indexed by node ID and delete nodes when they're no longer needed.
+ virtual void SerializerClearedNode(int32_t node_id) {}
+
protected:
AXTreeSource() {}
};
diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc
index 8f2c3ce8541..0adce655607 100644
--- a/chromium/ui/accessibility/ax_tree_unittest.cc
+++ b/chromium/ui/accessibility/ax_tree_unittest.cc
@@ -4400,6 +4400,88 @@ TEST(AXTreeTest, SetSizePosInSetHidden) {
EXPECT_OPTIONAL_EQ(4, option4->GetSetSize());
}
+// Tests that we get the correct PosInSet and SetSize values when using an
+// aria-controls relationship.
+TEST(AXTreeTest, SetSizePosInSetControls) {
+ std::vector<int32_t> three;
+ three.push_back(3);
+ std::vector<int32_t> hundred;
+ hundred.push_back(100);
+ std::vector<int32_t> eight;
+ eight.push_back(8);
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(8);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
+ tree_update.nodes[0].child_ids = {2, 3, 7, 8};
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kPopUpButton; // SetSize = 3
+ tree_update.nodes[1].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kControlsIds, three);
+ tree_update.nodes[1].SetHasPopup(ax::mojom::HasPopup::kMenu);
+ tree_update.nodes[2].id = 3;
+ tree_update.nodes[2].role = ax::mojom::Role::kMenu; // SetSize = 3
+ tree_update.nodes[2].child_ids = {4, 5, 6};
+ tree_update.nodes[3].id = 4;
+ tree_update.nodes[3].role = ax::mojom::Role::kMenuItem; // 1 of 3
+ tree_update.nodes[4].id = 5;
+ tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 2 of 3
+ tree_update.nodes[5].id = 6;
+ tree_update.nodes[5].role = ax::mojom::Role::kMenuItem; // 3 of 3
+ tree_update.nodes[6].id = 7;
+ tree_update.nodes[6].role =
+ ax::mojom::Role::kPopUpButton; // Test an invalid controls id.
+ tree_update.nodes[6].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kControlsIds, hundred);
+ // GetSetSize should handle self-references e.g. if a popup button controls
+ // itself.
+ tree_update.nodes[7].id = 8;
+ tree_update.nodes[7].role = ax::mojom::Role::kPopUpButton;
+ tree_update.nodes[7].AddIntListAttribute(
+ ax::mojom::IntListAttribute::kControlsIds, eight);
+ AXTree tree(tree_update);
+
+ AXNode* button = tree.GetFromId(2);
+ EXPECT_OPTIONAL_EQ(3, button->GetSetSize());
+ EXPECT_FALSE(button->GetPosInSet());
+ AXNode* menu = tree.GetFromId(3);
+ EXPECT_OPTIONAL_EQ(3, menu->GetSetSize());
+ AXNode* item = tree.GetFromId(4);
+ EXPECT_OPTIONAL_EQ(1, item->GetPosInSet());
+ EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
+ item = tree.GetFromId(5);
+ EXPECT_OPTIONAL_EQ(2, item->GetPosInSet());
+ EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
+ item = tree.GetFromId(6);
+ EXPECT_OPTIONAL_EQ(3, item->GetPosInSet());
+ EXPECT_OPTIONAL_EQ(3, item->GetSetSize());
+ button = tree.GetFromId(7);
+ EXPECT_OPTIONAL_EQ(0, button->GetSetSize());
+ button = tree.GetFromId(8);
+ EXPECT_OPTIONAL_EQ(0, button->GetSetSize());
+}
+
+// Tests GetPosInSet and GetSetSize return the assigned int attribute values
+// when a pop-up button is a leaf node.
+TEST(AXTreeTest, SetSizePosInSetLeafPopUpButton) {
+ AXTreeUpdate tree_update;
+ tree_update.root_id = 1;
+ tree_update.nodes.resize(2);
+ tree_update.nodes[0].id = 1;
+ tree_update.nodes[0].role = ax::mojom::Role::kGenericContainer;
+ tree_update.nodes[0].child_ids = {2};
+ tree_update.nodes[1].id = 2;
+ tree_update.nodes[1].role = ax::mojom::Role::kPopUpButton;
+ tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 3);
+ tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 77);
+ AXTree tree(tree_update);
+
+ AXNode* pop_up_button = tree.GetFromId(2);
+ EXPECT_OPTIONAL_EQ(3, pop_up_button->GetPosInSet());
+ EXPECT_OPTIONAL_EQ(77, pop_up_button->GetSetSize());
+}
+
TEST(AXTreeTest, OnNodeWillBeDeletedHasValidUnignoredParent) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/api.js b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/api.js
index 0b0bf75051e..30947603953 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/api.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/api.js
@@ -474,10 +474,10 @@ if (typeof(goog) != 'undefined' && goog.require) {
callback(cvox.ChromeVoxKbHandler.handlerKeyMap.hasKey(keySeq));
} else {
var strippedKeyEvent = {};
- /* Blacklist these props so we can safely stringify. */
- var BLACK_LIST_PROPS = ['target', 'srcElement', 'currentTarget', 'view'];
+ /* Denylist these props so we can safely stringify. */
+ var DENY_LIST_PROPS = ['target', 'srcElement', 'currentTarget', 'view'];
for (var prop in keyEvent) {
- if (BLACK_LIST_PROPS.indexOf(prop) === -1) {
+ if (DENY_LIST_PROPS.indexOf(prop) === -1) {
strippedKeyEvent[prop] = keyEvent[prop];
}
}
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher_test.unitjs b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher_test.unitjs
index 2bc9eed2c9d..a7ef1a5f8dc 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher_test.unitjs
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/event_watcher_test.unitjs
@@ -3,7 +3,7 @@
// found in the LICENSE file.
// Include test fixture.
-GEN_INCLUDE(['//chrome/browser/resources/chromeos/accessibility/chromevox/../testing/chromevox_unittest_base.js']);
+GEN_INCLUDE(['//ui/accessibility/extensions/chromevoxclassic/testing/chromevox_unittest_base.js']);
/**
* Test fixture.
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/script_installer.js b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/script_installer.js
index d2306b7edd8..27d4dfc1893 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/script_installer.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/script_installer.js
@@ -15,7 +15,7 @@ goog.provide('cvox.ScriptInstaller');
* URL pattern where we do not allow script installation.
* @type {RegExp}
*/
-cvox.ScriptInstaller.blacklistPattern = /chrome:\/\/|chrome-extension:\/\//;
+cvox.ScriptInstaller.denylistPattern = /chrome:\/\/|chrome-extension:\/\//;
/**
* Installs a script in the web page.
@@ -31,7 +31,7 @@ cvox.ScriptInstaller.blacklistPattern = /chrome:\/\/|chrome-extension:\/\//;
*/
cvox.ScriptInstaller.installScript = function(
srcs, uid, opt_onload, opt_chromevoxScriptBase) {
- if (cvox.ScriptInstaller.blacklistPattern.test(document.URL)) {
+ if (cvox.ScriptInstaller.denylistPattern.test(document.URL)) {
return false;
}
if (document.querySelector('script[' + uid + ']')) {
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
index 1a449755f03..2369e28a3b4 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands.js
@@ -60,7 +60,7 @@ cvox.ChromeVoxUserCommands.init_ = function() {
} else {
cvox.ChromeVoxUserCommands.commands = {};
}
- for (var cmd in cvox.CommandStore.CMD_WHITELIST) {
+ for (var cmd in cvox.CommandStore.CMD_ALLOWLIST) {
cvox.ChromeVoxUserCommands.commands[cmd] =
cvox.ChromeVoxUserCommands.createCommand_(cmd);
}
@@ -260,7 +260,7 @@ cvox.ChromeVoxUserCommands.doCommand_ = function(cmdStruct) {
return true;
}
- if (cmdStruct.disallowOOBE && document.URL.match(/^chrome:\/\/oobe/i)) {
+ if (cmdStruct.denyOOBE && document.URL.match(/^chrome:\/\/oobe/i)) {
return true;
}
@@ -270,7 +270,7 @@ cvox.ChromeVoxUserCommands.doCommand_ = function(cmdStruct) {
cvox.ChromeVoxEventSuspender.enterSuspendEvents();
}
- if (cmdStruct.disallowContinuation) {
+ if (cmdStruct.denyContinuation) {
cvox.ChromeVox.navigationManager.stopReading(true);
}
@@ -773,7 +773,7 @@ cvox.ChromeVoxUserCommands.doCommand_ = function(cmdStruct) {
cvox.QueueMode.FLUSH,
cvox.AbstractTts.PERSONALITY_ANNOTATION);
} else if (cvox.ChromeVox.navigationManager.isReading()) {
- if (cmdStruct.disallowContinuation) {
+ if (cmdStruct.denyContinuation) {
cvox.ChromeVox.navigationManager.stopReading(true);
} else if (cmd != 'readFromHere') {
cvox.ChromeVox.navigationManager.skip();
@@ -815,7 +815,7 @@ cvox.ChromeVoxUserCommands.handleChromeVoxUserEvent = function(cvoxUserEvent) {
* @private
*/
cvox.ChromeVoxUserCommands.lookupCommand_ = function(cmd, opt_kwargs) {
- var cmdStruct = cvox.CommandStore.CMD_WHITELIST[cmd];
+ var cmdStruct = cvox.CommandStore.CMD_ALLOWLIST[cmd];
if (!cmdStruct) {
throw 'Invalid command: ' + cmd;
}
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
index e3f4fbb61d1..d16b477e3de 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/chromevox/injected/user_commands_test.unitjs
@@ -88,7 +88,7 @@ SYNC_TEST_F('ChromeVoxUserCommandsUnitTest', 'FindNextMap', function() {
* @export
*/
SYNC_TEST_F('ChromeVoxUserCommandsUnitTest', 'CommandsMap', function() {
- var cmdMap = cvox.ChromeVoxUserCommands.CMD_WHITELIST_;
+ var cmdMap = cvox.ChromeVoxUserCommands.CMD_ALLOWLIST_;
var findMap = cvox.ChromeVoxUserCommands.FIND_NEXT_MAP_;
for (var cmd in cmdMap) {
assertTrue(
@@ -106,9 +106,9 @@ SYNC_TEST_F('ChromeVoxUserCommandsUnitTest', 'CommandsMap', function() {
j == 'nodeList' ||
j == 'platformFilter' ||
j == 'skipInput' ||
- j == 'disallowOOBE' ||
+ j == 'denyOOBE' ||
j == 'allowEvents' ||
- j == 'disallowContinuation');
+ j == 'denyContinuation');
}
if (cmd.findNext) {
assertTrue(
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/common/chromevox.js b/chromium/ui/accessibility/extensions/chromevoxclassic/common/chromevox.js
index d861167faf1..3be7da63f66 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/common/chromevox.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/common/chromevox.js
@@ -223,7 +223,7 @@ cvox.ChromeVox.executeUserCommand = function(commandName) {};
/**
* True if the document body has aria-hidden='true' when we first load.
- * ChromeVox will disallow any navigation and not eat any keystrokes.
+ * ChromeVox will deny any navigation and not eat any keystrokes.
* @type {boolean}
*/
cvox.ChromeVox.entireDocumentIsHidden = false;
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/common/command_store.js b/chromium/ui/accessibility/extensions/chromevoxclassic/common/command_store.js
index ec4d4995a1e..afdbc3e0856 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/common/command_store.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/common/command_store.js
@@ -12,7 +12,7 @@
*
* If you are looking to add a user command, follow the below steps for best
* integration with existing components:
- * 1. Add a command below in cvox.CommandStore.CMD_WHITELIST. Pick a
+ * 1. Add a command below in cvox.CommandStore.CMD_ALLOWLIST. Pick a
* programmatic name and fill in each of the relevant JSON keys.
* Be sure to add a msg id and define it in chromevox/messages/messages.js which
* describes the command. Please also add a category msg id so that the command
@@ -42,8 +42,8 @@ goog.require('cvox.PlatformFilter');
*/
cvox.CommandStore.categories = function() {
var categorySet = {};
- for (var cmd in cvox.CommandStore.CMD_WHITELIST) {
- var struct = cvox.CommandStore.CMD_WHITELIST[cmd];
+ for (var cmd in cvox.CommandStore.CMD_ALLOWLIST) {
+ var struct = cvox.CommandStore.CMD_ALLOWLIST[cmd];
if (struct.category) {
categorySet[struct.category] = true;
}
@@ -62,7 +62,7 @@ cvox.CommandStore.categories = function() {
* @return {string|undefined} The message id, if any.
*/
cvox.CommandStore.messageForCommand = function(command) {
- return (cvox.CommandStore.CMD_WHITELIST[command] || {}).msgId;
+ return (cvox.CommandStore.CMD_ALLOWLIST[command] || {}).msgId;
};
@@ -72,7 +72,7 @@ cvox.CommandStore.messageForCommand = function(command) {
* @return {string|undefined} The command, if any.
*/
cvox.CommandStore.categoryForCommand = function(command) {
- return (cvox.CommandStore.CMD_WHITELIST[command] || {}).category;
+ return (cvox.CommandStore.CMD_ALLOWLIST[command] || {}).category;
};
@@ -83,8 +83,8 @@ cvox.CommandStore.categoryForCommand = function(command) {
*/
cvox.CommandStore.commandsForCategory = function(category) {
var ret = [];
- for (var cmd in cvox.CommandStore.CMD_WHITELIST) {
- var struct = cvox.CommandStore.CMD_WHITELIST[cmd];
+ for (var cmd in cvox.CommandStore.CMD_ALLOWLIST) {
+ var struct = cvox.CommandStore.CMD_ALLOWLIST[cmd];
if (category == struct.category) {
ret.push(cmd);
}
@@ -106,7 +106,7 @@ cvox.CommandStore.commandsForCategory = function(category) {
* platformFilter: (undefined|cvox.PlatformFilter),
* skipInput: (undefined|boolean),
* allowEvents: (undefined|boolean),
- * disallowContinuation: (undefined|boolean)}>}
+ * denyContinuation: (undefined|boolean)}>}
* forward: Whether this command points forward.
* backward: Whether this command points backward. If neither forward or
* backward are specified, it stays facing in the current direction.
@@ -124,29 +124,29 @@ cvox.CommandStore.commandsForCategory = function(category) {
* undefined, the command applies to all platforms.
* skipInput: Explicitly skips this command when text input has focus.
* Defaults to false.
- * disallowOOBE: Explicitly disallows this command when on chrome://oobe/*.
+ * denyOOBE: Explicitly denies this command when on chrome://oobe/*.
* Defaults to false.
* allowEvents: Allows EventWatcher to continue processing events which can
* trump TTS.
- * disallowContinuation: Disallows continuous read to proceed. Defaults to
+ * denyContinuation: Denies continuous read to proceed. Defaults to
* false.
*/
-cvox.CommandStore.CMD_WHITELIST = {
+cvox.CommandStore.CMD_ALLOWLIST = {
'toggleStickyMode': {announce: false,
msgId: 'toggle_sticky_mode',
- 'disallowOOBE': true,
+ denyOOBE: true,
category: 'modifier_keys'},
'toggleKeyPrefix': {announce: false,
skipInput: true,
msgId: 'prefix_key',
- 'disallowOOBE': true,
+ denyOOBE: true,
category: 'modifier_keys'},
'passThroughMode': {announce: false,
msgId: 'pass_through_key_description',
category: 'modifier_keys'},
'stopSpeech': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
doDefault: true,
msgId: 'stop_speech_key',
category: 'controlling_speech'},
@@ -193,12 +193,12 @@ cvox.CommandStore.CMD_WHITELIST = {
'handleTab': {
allowEvents: true,
msgId: 'handle_tab_next',
- disallowContinuation: true,
+ denyContinuation: true,
category: 'navigation'},
'handleTabPrev': {
allowEvents: true,
msgId: 'handle_tab_prev',
- disallowContinuation: true,
+ denyContinuation: true,
category: 'navigation'},
'forward': {forward: true,
announce: true,
@@ -300,19 +300,19 @@ cvox.CommandStore.CMD_WHITELIST = {
msgId: 'read_from_here',
category: 'navigation'},
- 'performDefaultAction': {disallowContinuation: true,
+ 'performDefaultAction': {denyContinuation: true,
msgId: 'perform_default_action',
doDefault: true,
skipInput: true,
category: 'navigation'},
'forceClickOnCurrentItem': {announce: true,
- disallowContinuation: true,
+ denyContinuation: true,
allowEvents: true,
msgId: 'force_click_on_current_item',
category: 'navigation'},
'forceDoubleClickOnCurrentItem': {announce: true,
allowEvents: true,
- disallowContinuation: true},
+ denyContinuation: true},
'readLinkURL': {announce: false,
msgId: 'read_link_url',
@@ -335,54 +335,54 @@ cvox.CommandStore.CMD_WHITELIST = {
category: 'information'},
'toggleSearchWidget': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'toggle_search_widget',
category: 'information'},
'toggleKeyboardHelp': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_power_key',
category: 'help_commands'},
'help': {announce: false,
msgId: 'help',
- 'disallowOOBE': true,
- disallowContinuation: true,
+ denyOOBE: true,
+ denyContinuation: true,
category: 'help_commands'},
'contextMenu': {announce: false,
msgId: 'show_context_menu',
- disallowContinuation: true},
+ denyContinuation: true},
'showOptionsPage': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_options_page',
- 'disallowOOBE': true,
+ denyOOBE: true,
category: 'help_commands'},
'showKbExplorerPage': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_kb_explorer_page',
- 'disallowOOBE': true,
+ denyOOBE: true,
category: 'help_commands'},
'showFormsList': {announce: false,
- disallowContinuation: true,
+ denyContinuation: true,
nodeList: 'formField',
msgId: 'show_forms_list',
category: 'overview'},
'showHeadingsList': {announce: false, nodeList: 'heading',
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_headings_list',
category: 'overview'},
'showLandmarksList': {announce: false, nodeList: 'landmark',
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_landmarks_list',
category: 'overview'},
'showLinksList': {announce: false, nodeList: 'link',
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_links_list',
category: 'overview'},
'showTablesList': {announce: false, nodeList: 'table',
- disallowContinuation: true,
+ denyContinuation: true,
msgId: 'show_tables_list',
category: 'overview'},
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/common/math_semantic_tree_test.unitjs b/chromium/ui/accessibility/extensions/chromevoxclassic/common/math_semantic_tree_test.unitjs
index 106b7723bd2..308d19116c0 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/common/math_semantic_tree_test.unitjs
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/common/math_semantic_tree_test.unitjs
@@ -26,7 +26,7 @@ CvoxSemanticTreeUnitTest.prototype = {
/** @override */
setUp: function() {
this.nodeCounter = 0;
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
this.brief = true;
this.setupAttributes();
},
@@ -57,11 +57,11 @@ CvoxSemanticTreeUnitTest.prototype = {
},
/**
- * Removes XML nodes according to the XPath elements in the blacklist.
+ * Removes XML nodes according to the XPath elements in the denylist.
* @param {Node} xml Xml representation of the semantic node.
*/
customizeXml: function(xml) {
- this.xpathBlacklist.forEach(
+ this.xpathDenylist.forEach(
function(xpath) {
var removes = cvox.XpathUtil.evalXPath(xpath, xml);
removes.forEach(
@@ -1133,7 +1133,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFences', function() {
*/
TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFencesWithBars', function() {
this.brief = true;
- this.xpathBlacklist = ['descendant::punctuated/content'];
+ this.xpathDenylist = ['descendant::punctuated/content'];
// Set notation.
this.executeTreeTest(
'<mrow><mo>{</mo><mo>(</mo><mi>x</mi><mo>,</mo><mi>y</mi><mo>,</mo>' +
@@ -1358,7 +1358,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFencesWithBars', function() {
'<identifier>z</identifier>' +
'</children>' +
'</punctuated>');
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
});
@@ -1367,7 +1367,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFencesWithBars', function() {
*/
TEST_F('CvoxSemanticTreeUnitTest', 'StreeOpeningFencesOnly', function() {
this.brief = true;
- this.xpathBlacklist = ['descendant::punctuated/content'];
+ this.xpathDenylist = ['descendant::punctuated/content'];
// Single.
this.executeTreeTest(
'<mrow><mo>[</mo></mrow>',
@@ -1460,7 +1460,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeOpeningFencesOnly', function() {
'<identifier>f</identifier>' +
'</children>' +
'</punctuated>');
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
});
@@ -1469,7 +1469,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeOpeningFencesOnly', function() {
*/
TEST_F('CvoxSemanticTreeUnitTest', 'StreeClosingFencesOnly', function() {
this.brief = true;
- this.xpathBlacklist = ['descendant::punctuated/content'];
+ this.xpathDenylist = ['descendant::punctuated/content'];
// Single.
this.executeTreeTest(
'<mrow><mo>]</mo></mrow>',
@@ -1555,7 +1555,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeClosingFencesOnly', function() {
'<identifier>f</identifier>' +
'</children>' +
'</punctuated>');
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
});
@@ -1564,7 +1564,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeClosingFencesOnly', function() {
*/
TEST_F('CvoxSemanticTreeUnitTest', 'StreeNeutralFencesOnly', function() {
this.brief = true;
- this.xpathBlacklist = ['descendant::punctuated/content'];
+ this.xpathDenylist = ['descendant::punctuated/content'];
// Single.
this.executeTreeTest(
'<mrow><mo>|</mo></mrow>',
@@ -1662,7 +1662,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeNeutralFencesOnly', function() {
'<identifier>z</identifier>' +
'</children>' +
'</punctuated>');
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
});
@@ -1671,7 +1671,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeNeutralFencesOnly', function() {
*/
TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedUnmatchedFences', function() {
this.brief = true;
- this.xpathBlacklist = ['descendant::punctuated/content'];
+ this.xpathDenylist = ['descendant::punctuated/content'];
// Close, neutral, open.
this.executeTreeTest(
'<mrow><mo>]</mo><mo>&#x2016;</mo><mi>b</mi><mo>|</mo><mi>c</mi>' +
@@ -1908,7 +1908,7 @@ TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedUnmatchedFences', function() {
'</prefixop>' +
'</children>' +
'</punctuated>');
- this.xpathBlacklist = [];
+ this.xpathDenylist = [];
});
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/background.js b/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/background.js
index 8652cb751a6..f6ba2c1185d 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/background.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/background.js
@@ -48,22 +48,22 @@ Background = function() {
* @type {!Array<string>}
* @private
*/
- this.whitelist_ = ['chromevox_next_test'];
+ this.allowlist_ = ['chromevox_next_test'];
/**
- * A list of site substring patterns to blacklist ChromeVox Classic,
+ * A list of site substring patterns to denylist ChromeVox Classic,
* putting ChromeVox into Compat mode.
* @type {!Set<string>}
* @private
*/
- this.classicBlacklist_ = new Set();
+ this.classicDenylist_ = new Set();
/**
- * Regular expression for blacklisting classic.
+ * Regular expression for denylisting classic.
* @type {RegExp}
* @private
*/
- this.classicBlacklistRegExp_ = Background.globsToRegExp_(
+ this.classicDenylistRegExp_ = Background.globsToRegExp_(
chrome.runtime.getManifest()['content_scripts'][0]['exclude_globs']);
/**
@@ -225,9 +225,9 @@ Background.prototype = {
AutomationUtil.getTopLevelRoot(/** @type {!AutomationNode} */ (target));
if (!root)
return ChromeVoxMode.COMPAT;
- if (this.isWhitelistedForCompat_(root.docUrl))
+ if (this.isAllowlistedForCompat_(root.docUrl))
return ChromeVoxMode.COMPAT;
- else if (this.isWhitelistedForNext_(root.docUrl))
+ else if (this.isAllowlistedForNext_(root.docUrl))
return ChromeVoxMode.NEXT;
else
return ChromeVoxMode.CLASSIC;
@@ -965,17 +965,17 @@ Background.prototype = {
*/
shouldEnableClassicForUrl_: function(url) {
return this.mode != ChromeVoxMode.FORCE_NEXT &&
- !this.isBlacklistedForClassic_(url) && !this.isWhitelistedForNext_(url);
+ !this.isDenylistedForClassic_(url) && !this.isAllowlistedForNext_(url);
},
/**
* Compat mode is on if any of the following are true:
- * 1. a url is blacklisted for Classic.
+ * 1. a url is denylisted for Classic.
* 2. the current range is not within web content.
* @param {string} url
*/
- isWhitelistedForCompat_: function(url) {
- return this.isBlacklistedForClassic_(url) ||
+ isAllowlistedForCompat_: function(url) {
+ return this.isDenylistedForClassic_(url) ||
(this.getCurrentRange() && !this.getCurrentRange().isWebRange() &&
this.getCurrentRange().start.node.state.focused);
},
@@ -985,20 +985,20 @@ Background.prototype = {
* @return {boolean}
* @private
*/
- isBlacklistedForClassic_: function(url) {
- if (this.classicBlacklistRegExp_.test(url))
+ isDenylistedForClassic_: function(url) {
+ if (this.classicDenylistRegExp_.test(url))
return true;
url = url.substring(0, url.indexOf('#')) || url;
- return this.classicBlacklist_.has(url);
+ return this.classicDenylist_.has(url);
},
/**
* @param {string} url
- * @return {boolean} Whether the given |url| is whitelisted.
+ * @return {boolean} Whether the given |url| is allowlisted.
* @private
*/
- isWhitelistedForNext_: function(url) {
- return this.whitelist_.some(function(item) {
+ isAllowlistedForNext_: function(url) {
+ return this.allowlist_.some(function(item) {
return url.indexOf(item) != -1;
});
},
@@ -1072,7 +1072,7 @@ Background.prototype = {
{target: 'next', isClassicEnabled: isClassicEnabled});
} else if (action == 'enableCompatForUrl') {
var url = msg['url'];
- this.classicBlacklist_.add(url);
+ this.classicDenylist_.add(url);
if (this.currentRange_ && this.currentRange_.start.node)
this.setCurrentRange(this.currentRange_);
} else if (action == 'onCommand') {
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/sre_browser.js b/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/sre_browser.js
index b44010da654..fb93433549b 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/sre_browser.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/cvox2/background/sre_browser.js
@@ -151,7 +151,7 @@ goog.DEBUG = !0;
goog.LOCALE = 'en';
goog.TRUSTED_SITE = !0;
goog.STRICT_MODE_COMPATIBLE = !1;
-goog.DISALLOW_TEST_ONLY_CODE = COMPILED && !goog.DEBUG;
+goog.DENY_TEST_ONLY_CODE = COMPILED && !goog.DEBUG;
goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = !1;
goog.provide = function(a) {
if (goog.isInModuleLoader_())
@@ -212,7 +212,7 @@ goog.module.declareLegacyNamespace = function() {
goog.moduleLoaderState_.declareLegacyNamespace = !0
};
goog.setTestOnly = function(a) {
- if (goog.DISALLOW_TEST_ONLY_CODE)
+ if (goog.DENY_TEST_ONLY_CODE)
throw a = a || '',
Error(
'Importing test-only code into non-debug environment' +
diff --git a/chromium/ui/accessibility/extensions/chromevoxclassic/walkers/walker_unittest_base.js b/chromium/ui/accessibility/extensions/chromevoxclassic/walkers/walker_unittest_base.js
index 15280aa44f1..987225b2604 100644
--- a/chromium/ui/accessibility/extensions/chromevoxclassic/walkers/walker_unittest_base.js
+++ b/chromium/ui/accessibility/extensions/chromevoxclassic/walkers/walker_unittest_base.js
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-GEN_INCLUDE(['//chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_unittest_base.js']);
+GEN_INCLUDE(['//ui/accessibility/extensions/chromevoxclassic/testing/chromevox_unittest_base.js']);
/**
* Base class for walker test fixtures.
@@ -50,7 +50,7 @@ ChromeVoxWalkerUnitTestBase.prototype = {
* selection, then asserts that for all the parameters passed in desc,
* the new selection matches. Returns the new selections if assertion passes.
* NOTE: If you change the parameters here, you should also change the
- * whitelist above.
+ * allowlist above.
* @param {!cvox.CursorSelection} sel The selection.
* @param {!string|!cvox.CursorSelection} opt_cmdOrDest The command to
* execute, or the override returned selection.
@@ -82,7 +82,8 @@ ChromeVoxWalkerUnitTestBase.prototype = {
if (opt_cmdOrDest instanceof cvox.CursorSelection) {
var ret = opt_cmdOrDest;
} else {
- if (ChromeVoxWalkerUnitTestBase.CMD_WHITELIST.indexOf(opt_cmdOrDest) == -1) {
+ if (ChromeVoxWalkerUnitTestBase.CMD_ALLOWLIST.indexOf(
+ opt_cmdOrDest) == -1) {
// Intentionally fail the test if there's a typo.
throw 'Got an invalid command: "' + opt_cmdOrDest + '".';
}
@@ -100,7 +101,7 @@ ChromeVoxWalkerUnitTestBase.prototype = {
}
for (var key in desc) {
- if (ChromeVoxWalkerUnitTestBase.DESC_WHITELIST.indexOf(key) == -1) {
+ if (ChromeVoxWalkerUnitTestBase.DESC_ALLOWLIST.indexOf(key) == -1) {
throw 'Invalid key in desc parameter: "' + key + '".';
}
}
@@ -151,18 +152,19 @@ ChromeVoxWalkerUnitTestBase.prototype = {
};
/**
- * Whitelist for the commands that are allowed to be executed with go().
+ * Allowlist for the commands that are allowed to be executed with go().
* @type {Array.string}
* @const
*/
-ChromeVoxWalkerUnitTestBase.CMD_WHITELIST = ['next', 'sync', 'nextRow', 'nextCol'];
+ChromeVoxWalkerUnitTestBase.CMD_ALLOWLIST = [
+ 'next', 'sync', 'nextRow', 'nextCol'];
/**
- * Whitelist for the properties that can be asserted with go().
+ * Allowlist for the properties that can be asserted with go().
* @type {Array.string}
* @const
*/
-ChromeVoxWalkerUnitTestBase.DESC_WHITELIST = ['selText', 'selNodeId',
+ChromeVoxWalkerUnitTestBase.DESC_ALLOWLIST = ['selText', 'selNodeId',
'selParentNodeId', 'selStartIndex', 'selEndIndex', 'selReversed', 'descText',
'descContext', 'descAnnotation', 'descUserValue', 'descPersonality'];
diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb
index 92d3d360f0d..07f5dc1df42 100644
--- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb
+++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb
@@ -7,18 +7,18 @@
<translation id="1555130319947370107">Blau</translation>
<translation id="1588438908519853928">Normal</translation>
<translation id="1591070050619849194">Desactiveu totes les animacions d'imatge.</translation>
-<translation id="1703735871906654364">Navegació amb cursor</translation>
+<translation id="1703735871906654364">Navegació amb cursor de text</translation>
<translation id="1791496371305830581">Permeteu totes les imatges animades.</translation>
<translation id="1996252509865389616">Activa</translation>
<translation id="2079545284768500474">Desfés</translation>
<translation id="2179565792157161713">Obre la descripció llarga en una pestanya nova</translation>
<translation id="2223143012868735942">Un filtre de color personalitzable que s'aplica en pàgines web per millorar la percepció del color.</translation>
-<translation id="2394933097471027016">Proveu-ho ara. La navegació amb cursor està sempre activada en aquesta pàgina.</translation>
+<translation id="2394933097471027016">Proveu-ho ara. La navegació amb cursor de text està sempre activada en aquesta pàgina.</translation>
<translation id="2471847333270902538">Paleta de colors per a <ph name="SITE" />:</translation>
<translation id="2648340354586434750">Manteniu premuda la tecla d'&lt;span class='key'&gt;opció&lt;/span&gt; per desplaçar-vos per les paraules.</translation>
<translation id="2795227192542594043">Aquesta extensió proporciona un cursor mòbil a la pàgina web que permet seleccionar el text amb el teclat.</translation>
<translation id="2808027189040546825">Pas 1: seleccioneu la fila amb les estrelles més tènues</translation>
-<translation id="2965611304828530558">&lt;p&gt;Quan aneu a un enllaç o control, se centra automàticament. Premeu &lt;span class='key'&gt;Retorn&lt;/span&gt; per fer clic en un enllaç o botó. &lt;/p&gt; &lt;p&gt; Quan un control enfocat (com un quadre de text o un quadre de llista) capti les tecles de fletxa, premeu la tecla &lt;span class='key'&gt;Esc&lt;/span&gt; seguida de la fletxa esquerra o dreta per continuar amb la navegació amb cursor. &lt;/p&gt; &lt;p&gt; També podeu prémer la tecla &lt;span class='key'&gt;Tab&lt;/span&gt; per anar al control següent que es pugui enfocar. &lt;/p&gt;</translation>
+<translation id="2965611304828530558">&lt;p&gt;Quan aneu a un enllaç o control, se centra automàticament. Premeu &lt;span class='key'&gt;Retorn&lt;/span&gt; per fer clic en un enllaç o botó. &lt;/p&gt; &lt;p&gt; Quan un control enfocat (com un quadre de text o un quadre de llista) capti les tecles de fletxa, premeu la tecla &lt;span class='key'&gt;Esc&lt;/span&gt; seguida de la fletxa esquerra o dreta per continuar amb la navegació amb cursor de text. &lt;/p&gt; &lt;p&gt; També podeu prémer la tecla &lt;span class='key'&gt;Tab&lt;/span&gt; per anar al control següent que es pugui enfocar. &lt;/p&gt;</translation>
<translation id="3252573918265662711">Configuració</translation>
<translation id="3410969471888629217">Oblida les personalitzacions del lloc</translation>
<translation id="3435896845095436175">Activa</translation>
@@ -31,8 +31,8 @@
<translation id="4769065380738716500">Les imatges s'han substituït pel seu text alternatiu.</translation>
<translation id="4896660567607030658">Sense suggeriments, només mostra el cursor</translation>
<translation id="4937901943818762779">Permeteu les imatges animades, però només una vegada.</translation>
-<translation id="4949131196216960195">Prem &lt;span class='key'&gt;Cerca&lt;/span&gt;+&lt;img src='increase_brightness.png'&gt; (la tecla per augmentar la brillantor o F7) per activar la navegació amb cursor. Per desactivar-la, torna a prémer les mateixes tecles.</translation>
-<translation id="4954450790315188152">Quan la navegació amb cursor està activada:</translation>
+<translation id="4949131196216960195">Prem &lt;span class='key'&gt;Cerca&lt;/span&gt;+&lt;img src='increase_brightness.png'&gt; (la tecla per augmentar la brillantor o F7) per activar la navegació amb cursor de text. Per desactivar-la, torna a prémer les mateixes tecles.</translation>
+<translation id="4954450790315188152">Quan la navegació amb cursor de text està activada:</translation>
<translation id="5041932793799765940">Ajust del color</translation>
<translation id="5094574508723441140">Contrast augmentat</translation>
<translation id="5173942593318174089">Destaqueu la posició del cursor amb una animació.</translation>
@@ -61,9 +61,9 @@
<translation id="7658239707568436148">Cancel·la</translation>
<translation id="786423340267544509">Afegeix una vora als elements amb atributs "aria-describedat" o "longdesc".</translation>
<translation id="7942349550061667556">Vermell</translation>
-<translation id="8260673944985561857">Opcions de navegació amb cursor</translation>
+<translation id="8260673944985561857">Opcions de navegació amb cursor de text</translation>
<translation id="8321034316479930120">Política d'animació</translation>
<translation id="8480209185614411573">Contrast alt</translation>
-<translation id="8609925175482059018">Premeu &lt;span class='key'&gt;F7&lt;/span&gt; per activar la navegació amb cursor. Torneu a prémer F7 per desactivar-la.</translation>
+<translation id="8609925175482059018">Premeu &lt;span class='key'&gt;F7&lt;/span&gt; per activar la navegació amb cursor de text. Torneu a prémer F7 per desactivar-la.</translation>
<translation id="894241283505723656">Descripcions llargues al menú contextual</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_da.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_da.xtb
index 017c14e08ca..197f40eb4d7 100644
--- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_da.xtb
+++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_da.xtb
@@ -7,18 +7,18 @@
<translation id="1555130319947370107">Blå</translation>
<translation id="1588438908519853928">Normal</translation>
<translation id="1591070050619849194">Deaktiver alle billedanimationer.</translation>
-<translation id="1703735871906654364">Tastebrowsing</translation>
+<translation id="1703735871906654364">Tastenavigation</translation>
<translation id="1791496371305830581">Tillad alle animerede billeder.</translation>
<translation id="1996252509865389616">Vil du aktivere?</translation>
<translation id="2079545284768500474">Fortryd</translation>
<translation id="2179565792157161713">Åbn lang beskrivelse på en ny fane</translation>
<translation id="2223143012868735942">Et justerbart farvefilter anvendes på websider for at forbedre farveopfattelsen.</translation>
-<translation id="2394933097471027016">Prøv det nu. Tastebrowsing er altid aktiveret på denne side.</translation>
+<translation id="2394933097471027016">Prøv det nu. Tastenavigation er altid aktiveret på denne side.</translation>
<translation id="2471847333270902538">Farvetema for <ph name="SITE" />:</translation>
<translation id="2648340354586434750">Hold &lt;span class='key'&gt;Alt&lt;/span&gt; nede for at flytte efter ord.</translation>
<translation id="2795227192542594043">Denne udvidelse giver dig en bevægelig markør på websiden, så du kan vælge tekst med tastaturet.</translation>
<translation id="2808027189040546825">Trin 1: Vælg rækken med de mest utydelige stjerner:</translation>
-<translation id="2965611304828530558">&lt;p&gt;Når du når til et link eller et kontrolelement, fremhæves det automatisk. Tryk på &lt;span class='key'&gt;Enter&lt;/span&gt; for at klikke på et link eller en knap. &lt;/p&gt; &lt;p&gt; Når et fremhævet kontrolelement (som f.eks. et tekstfelt eller et listefelt) griber piletaster, skal du trykke på &lt;span class='key'&gt;Esc&lt;/span&gt; efterfulgt af venstre eller højre pil for at fortsætte tastebrowsing. &lt;/p&gt; &lt;p&gt; Alternativt kan du også trykke på &lt;span class='key'&gt;Tab&lt;/span&gt; for at gå til det næste kontrolelement, der kan fremhæves. &lt;/p&gt;</translation>
+<translation id="2965611304828530558">&lt;p&gt;Når du når til et link eller et kontrolelement, fremhæves det automatisk. Tryk på &lt;span class='key'&gt;Enter&lt;/span&gt; for at klikke på et link eller en knap. &lt;/p&gt; &lt;p&gt; Når et fremhævet kontrolelement (som f.eks. et tekstfelt eller et listefelt) griber piletaster, skal du trykke på &lt;span class='key'&gt;Esc&lt;/span&gt; efterfulgt af venstre eller højre pil for at fortsætte Tastenavigation. &lt;/p&gt; &lt;p&gt; Alternativt kan du også trykke på &lt;span class='key'&gt;Tab&lt;/span&gt; for at gå til det næste kontrolelement, der kan fremhæves. &lt;/p&gt;</translation>
<translation id="3252573918265662711">Konfiguration</translation>
<translation id="3410969471888629217">Glem websitets tilpasninger</translation>
<translation id="3435896845095436175">Aktivér</translation>
@@ -31,8 +31,8 @@
<translation id="4769065380738716500">Billeder er blevet erstattet af deres alternative tekst.</translation>
<translation id="4896660567607030658">Ingen feedback – vis kun markøren.</translation>
<translation id="4937901943818762779">Tillad animerede billeder, men kun én gang.</translation>
-<translation id="4949131196216960195">Tryk på &lt;span class='key'&gt;Søg&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tasten, som øger lysstyrken, eller F7) for at aktivere tastebrowsing. Tryk på den igen for at deaktivere funktionen.</translation>
-<translation id="4954450790315188152">Når tastebrowsing er aktiveret:</translation>
+<translation id="4949131196216960195">Tryk på &lt;span class='key'&gt;Søg&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tasten, som øger lysstyrken, eller F7) for at aktivere tastenavigation. Tryk på den igen for at deaktivere funktionen.</translation>
+<translation id="4954450790315188152">Når Tastenavigation er aktiveret:</translation>
<translation id="5041932793799765940">Farvejustering</translation>
<translation id="5094574508723441140">Øget kontrast</translation>
<translation id="5173942593318174089">Fremhæv markørens position med en animation.</translation>
@@ -52,7 +52,7 @@
<translation id="633394792577263429">Hold &lt;span class='key'&gt;Ctrl&lt;/span&gt; nede for at flytte efter ord.</translation>
<translation id="6550675742724504774">Valgmuligheder</translation>
<translation id="6838518108677880446">Konfiguration:</translation>
-<translation id="690628312087070417">Når markøren springer med stor afstand:</translation>
+<translation id="690628312087070417">Når tekstmarkøren springer med stor afstand:</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="7379645913608427028">Grad</translation>
<translation id="7384431257964758081">Stor kontrast er aktiveret</translation>
@@ -61,9 +61,9 @@
<translation id="7658239707568436148">Annuller</translation>
<translation id="786423340267544509">Føj en grænse til elementer med aria-describedat- eller longdesc-attributter.</translation>
<translation id="7942349550061667556">Rød</translation>
-<translation id="8260673944985561857">Muligheder for tastebrowsing</translation>
+<translation id="8260673944985561857">Muligheder for Tastenavigation</translation>
<translation id="8321034316479930120">Animationspolitik</translation>
<translation id="8480209185614411573">Stor kontrast</translation>
-<translation id="8609925175482059018">Tryk på &lt;span class='key'&gt;F7&lt;/span&gt; for at aktivere tastebrowsing. Tryk igen for at deaktivere.</translation>
+<translation id="8609925175482059018">Tryk på &lt;span class='key'&gt;F7&lt;/span&gt; for at aktivere tastenavigation. Tryk igen for at deaktivere.</translation>
<translation id="894241283505723656">Lange beskrivelser i genvejsmenuen</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_eu.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_eu.xtb
index c288793020f..212d5529c8a 100644
--- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_eu.xtb
+++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_eu.xtb
@@ -7,18 +7,18 @@
<translation id="1555130319947370107">Urdina</translation>
<translation id="1588438908519853928">Arrunta</translation>
<translation id="1591070050619849194">Desgaitu animazio guztiak.</translation>
-<translation id="1703735871906654364">Tartekatze-ikurraren bidez arakatzeko aplikazioa</translation>
+<translation id="1703735871906654364">Testu-kurtsorearen bidez arakatzeko aplikazioa</translation>
<translation id="1791496371305830581">Onartu animazio guztiak.</translation>
<translation id="1996252509865389616">Gaitu nahi duzu?</translation>
<translation id="2079545284768500474">Desegin</translation>
<translation id="2179565792157161713">Ireki azalpen luzea beste fitxa batean</translation>
<translation id="2223143012868735942">Web-orrietan kolorearen pertzepzioa hobetzeko aplikatzen den kolore-iragazki pertsonalizatua.</translation>
-<translation id="2394933097471027016">Proba ezazu! Caret Browsing beti gaituta dago orri honetan!</translation>
+<translation id="2394933097471027016">Proba ezazu! Testu-kurtsorearen bidez arakatzeko eginbidea beti gaituta dago orri honetan!</translation>
<translation id="2471847333270902538"><ph name="SITE" /> webgunearen kolore-eskema:</translation>
<translation id="2648340354586434750">Eduki sakatuta &lt;span class='key'&gt;Aukera&lt;/span&gt; hitz batetik bestera mugitzeko.</translation>
<translation id="2795227192542594043">Luzapenak kurtsore mugikorra jartzen du webgunean testua teklatuarekin hautatu ahal izateko.</translation>
<translation id="2808027189040546825">1. urratsa: hautatu izar lausoenak dituen errenkada:</translation>
-<translation id="2965611304828530558">&lt;p&gt;Estekara edo kontrolera iristean, automatikoki fokuratzen da. Sakatu &lt;span class='key'&gt;Sartu&lt;/span&gt; estekan edo botoian klik egitean. &lt;/p&gt; &lt;p&gt;Fokuratutako kontrolak (adibidez, testu-koadroak edo zerrenda-koadroak) gezi-teklak hartzen dituenean, sakatu &lt;span class='key'&gt;Esc&lt;/span&gt; eta, ondoren, Ezkerrera edo Eskuinera gezia, tartekatze-ikurraren bidez arakatzen jarraitzeko. &lt;/p&gt; &lt;p&gt;Horren ordez, sakatu &lt;span class='key'&gt;Fitxa&lt;/span&gt; fokura daitekeen hurrengo kontrolera joateko. &lt;/p&gt;</translation>
+<translation id="2965611304828530558">&lt;p&gt;Estekara edo kontrolera iristean, automatikoki fokuratzen da. Sakatu &lt;span class='key'&gt;Sartu&lt;/span&gt; estekan edo botoian klik egitean. &lt;/p&gt; &lt;p&gt;Fokuratutako kontrolak (adibidez, testu-koadroak edo zerrenda-koadroak) gezi-teklak hartzen dituenean, sakatu &lt;span class='key'&gt;Esc&lt;/span&gt; eta, ondoren, Ezkerrera edo Eskuinera gezia, testu-kurtsorearen bidez arakatzen jarraitzeko. &lt;/p&gt; &lt;p&gt;Horren ordez, sakatu &lt;span class='key'&gt;Fitxa&lt;/span&gt; fokura daitekeen hurrengo kontrolera joateko. &lt;/p&gt;</translation>
<translation id="3252573918265662711">Konfigurazioa</translation>
<translation id="3410969471888629217">Ahaztu webgunearen pertsonalizazioak</translation>
<translation id="3435896845095436175">Gaitu</translation>
@@ -31,8 +31,8 @@
<translation id="4769065380738716500">Irudien ordez, haien alt testua jarri da.</translation>
<translation id="4896660567607030658">Ez bistaratu azalpenik; besterik gabe, erakutsi kurtsorea.</translation>
<translation id="4937901943818762779">Gaitu animazioak, baina behin soilik.</translation>
-<translation id="4949131196216960195">Sakatu &lt;span class='key'&gt;Bilatu&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (distira handiagotzeko tekla edo F7) teklatu bidezko arakatzea aktibatzeko. Saka ezazu berriro desaktibatzeko.</translation>
-<translation id="4954450790315188152">Tartekatze-ikurraren bidez arakatzeko aukera aktibatuta dagoenean:</translation>
+<translation id="4949131196216960195">Sakatu &lt;span class='key'&gt;Bilatu&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (distira handiagotzeko tekla edo F7) testu-kurtsorearen bidez arakatzeko eginbidea aktibatzeko. Saka ezazu berriro desaktibatzeko.</translation>
+<translation id="4954450790315188152">Testu-kurtsorearen bidez arakatzeko eginbidea aktibatuta dagoenean:</translation>
<translation id="5041932793799765940">Kolore doikuntza</translation>
<translation id="5094574508723441140">Kontraste handitua</translation>
<translation id="5173942593318174089">Nabarmendu kurtsorearen posizioa animazioarekin.</translation>
@@ -52,7 +52,7 @@
<translation id="633394792577263429">Eduki sakatuta &lt;span class='key'&gt;Kontrola&lt;/span&gt; hitz batetik bestera mugitzeko.</translation>
<translation id="6550675742724504774">Aukerak</translation>
<translation id="6838518108677880446">Konfigurazioa:</translation>
-<translation id="690628312087070417">Tartekatze-ikurrak urrun salto egiten duenean:</translation>
+<translation id="690628312087070417">Testu-kurtsoreak urrun salto egiten duenean:</translation>
<translation id="6965382102122355670">Ados</translation>
<translation id="7379645913608427028">Maila</translation>
<translation id="7384431257964758081">Kontraste handia gaituta dago</translation>
@@ -60,9 +60,9 @@
<translation id="7658239707568436148">Utzi</translation>
<translation id="786423340267544509">Gehitu ertza aria-describedat edo longdesc atributuak dituen elementuan.</translation>
<translation id="7942349550061667556">Gorria</translation>
-<translation id="8260673944985561857">Tartekatze-ikurraren bidez arakatzeko aukerak</translation>
+<translation id="8260673944985561857">Testu-kurtsorearen bidez arakatzeko eginbidearen aukerak</translation>
<translation id="8321034316479930120">Animazioen gidalerroak</translation>
<translation id="8480209185614411573">Kontraste handia</translation>
-<translation id="8609925175482059018">Sakatu &lt;span class='key'&gt;F7&lt;/span&gt; tartekatze-ikurraren bidez arakatzeko aukera aktibatzeko. Saka ezazu berriro desaktibatzeko.</translation>
+<translation id="8609925175482059018">Sakatu &lt;span class='key'&gt;F7&lt;/span&gt; testu-kurtsorearen bidez arakatzeko eginbidea aktibatzeko. Saka ezazu berriro desaktibatzeko.</translation>
<translation id="894241283505723656">Laster-menuko azalpen luzeak</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb
index 8574d4ecc7a..fe79bc36f7f 100644
--- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb
+++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb
@@ -7,18 +7,18 @@
<translation id="1555130319947370107">Biru</translation>
<translation id="1588438908519853928">Normal</translation>
<translation id="1591070050619849194">Nonaktifkan semua animasi gambar.</translation>
-<translation id="1703735871906654364">Penjelajahan Caret</translation>
+<translation id="1703735871906654364">Penjelajahan dengan Keyboard</translation>
<translation id="1791496371305830581">Izinkan semua gambar animasi.</translation>
<translation id="1996252509865389616">Aktifkan?</translation>
<translation id="2079545284768500474">Urungkan</translation>
<translation id="2179565792157161713">Buka Deskripsi Panjang di Tab Baru</translation>
<translation id="2223143012868735942">Filter warna yang disesuaikan diterapkan ke halaman web untuk meningkatkan persepsi warna.</translation>
-<translation id="2394933097471027016">Cobalah sekarang - Penjelajahan Caret selalu diaktifkan pada halaman ini!</translation>
+<translation id="2394933097471027016">Cobalah sekarang - Penjelajahan dengan Keyboard selalu diaktifkan pada halaman ini.</translation>
<translation id="2471847333270902538">Skema warna untuk <ph name="SITE" />:</translation>
<translation id="2648340354586434750">Tahan &lt;span class='key'&gt;Opsi&lt;/span&gt; untuk berpindah berdasarkan kata.</translation>
<translation id="2795227192542594043">Ekstensi ini memberi Anda kursor yang dapat digerakkan di halaman web, memungkinkan Anda memilih teks dengan keyboard.</translation>
<translation id="2808027189040546825">Langkah 1: Pilih baris dengan bintang paling redup:</translation>
-<translation id="2965611304828530558">&lt;p&gt;Saat Anda mengjangkau link atau kontrol, link atau kontrol tersebut akan otomatis terfokus. Tekan &lt;span class='key'&gt;Enter&lt;/span&gt; untuk mengeklik link atau tombol. &lt;/p&gt; &lt;p&gt; Saat kontrol terfokus (seperti kotak teks atau kotak daftar) menangkap tombol panah, tekan &lt;span class='key'&gt;Esc&lt;/span&gt; diikuti dengan tanda panah kiri atau kanan untuk melanjutkan Penjelajahan Caret. &lt;/p&gt; &lt;p&gt; Atau, tekan &lt;span class='key'&gt;Tab&lt;/span&gt; untuk berpindah ke kontrol yang dapat difokuskan berikutnya. &lt;/p&gt;</translation>
+<translation id="2965611304828530558">&lt;p&gt;Saat Anda mengjangkau link atau kontrol, link atau kontrol tersebut akan otomatis terfokus. Tekan &lt;span class='key'&gt;Enter&lt;/span&gt; untuk mengeklik link atau tombol. &lt;/p&gt; &lt;p&gt; Saat kontrol terfokus (seperti kotak teks atau kotak daftar) menangkap tombol panah, tekan &lt;span class='key'&gt;Esc&lt;/span&gt; diikuti dengan tanda panah kiri atau kanan untuk melanjutkan Penjelajahan dengan Keyboard. &lt;/p&gt; &lt;p&gt; Atau, tekan &lt;span class='key'&gt;Tab&lt;/span&gt; untuk berpindah ke kontrol yang dapat difokuskan berikutnya. &lt;/p&gt;</translation>
<translation id="3252573918265662711">Penyiapan</translation>
<translation id="3410969471888629217">Lupakan penyesuaian situs</translation>
<translation id="3435896845095436175">Aktifkan</translation>
@@ -31,8 +31,8 @@
<translation id="4769065380738716500">Gambar telah diganti dengan teks alternatif.</translation>
<translation id="4896660567607030658">Tidak ada masukan, cukup tampilkan kursor.</translation>
<translation id="4937901943818762779">Izinkan gambar animasi, tapi hanya satu kali.</translation>
-<translation id="4949131196216960195">Tekan &lt;span class='key'&gt;Telusuri&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tombol Tingkatkan Kecerahan atau F7) untuk mengaktifkan Penjelajahan Caret. Tekan lagi untuk menonaktifkannya.</translation>
-<translation id="4954450790315188152">Saat Penjelajahan Caret diaktifkan:</translation>
+<translation id="4949131196216960195">Tekan &lt;span class='key'&gt;Telusuri&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tombol Tingkatkan Kecerahan atau F7) untuk mengaktifkan Penjelajahan dengan Keyboard. Tekan lagi untuk menonaktifkannya.</translation>
+<translation id="4954450790315188152">Saat Penjelajahan dengan Keyboard diaktifkan:</translation>
<translation id="5041932793799765940">Penyesuaian warna</translation>
<translation id="5094574508723441140">Kontras Ditingkatkan</translation>
<translation id="5173942593318174089">Sorot posisi kursor dengan animasi.</translation>
@@ -61,9 +61,9 @@
<translation id="7658239707568436148">Batal</translation>
<translation id="786423340267544509">Tambahkan pembatas ke elemen yang memiliki atribut aria-describedat atau longdesc.</translation>
<translation id="7942349550061667556">Merah</translation>
-<translation id="8260673944985561857">Opsi Penjelajahan Caret</translation>
+<translation id="8260673944985561857">Opsi Penjelajahan dengan Keyboard</translation>
<translation id="8321034316479930120">Kebijakan Animasi</translation>
<translation id="8480209185614411573">Kontras Tinggi</translation>
-<translation id="8609925175482059018">Tekan &lt;span class='key'&gt;F7&lt;/span&gt; untuk mengaktifkan Penjelajahan Caret. Tekan lagi untuk menonaktifkannya.</translation>
+<translation id="8609925175482059018">Tekan &lt;span class='key'&gt;F7&lt;/span&gt; untuk mengaktifkan Penjelajahan dengan Keyboard. Tekan lagi untuk menonaktifkannya.</translation>
<translation id="894241283505723656">Deskripsi Panjang pada Menu Konteks</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_sq.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_sq.xtb
index 454d53e6e15..78ae8d74d68 100644
--- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_sq.xtb
+++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_sq.xtb
@@ -13,12 +13,12 @@
<translation id="2079545284768500474">Zhbëj</translation>
<translation id="2179565792157161713">Hap përshkrimin e gjatë në skedën e re</translation>
<translation id="2223143012868735942">Në filtër i personalizueshëm ngjyrash i aplikuar në faqet e uebit për të përmirësuar perceptimin e ngjyrave.</translation>
-<translation id="2394933097471027016">Provoje tani - Shfletimi "Caret" është gjithmonë i aktivizuar në këtë faqe!</translation>
+<translation id="2394933097471027016">Provoje tani - Shfletimi me kursorin e tekstit është gjithmonë i aktivizuar në këtë faqe!</translation>
<translation id="2471847333270902538">Skema e ngjyrave për <ph name="SITE" />:</translation>
<translation id="2648340354586434750">Mbaj të shtypur &lt;span class='key'&gt;Opsioni&lt;/span&gt; për të lëvizur sipas fjalëve.</translation>
<translation id="2795227192542594043">Kjo shtesë të jep një kursor të lëvizshëm në faqen e uebit, duke të lejuar ta zgjedhësh tekstin me tastierë.</translation>
<translation id="2808027189040546825">Hapi 1: Zgjidh radhën me yjet më të zbehtë:</translation>
-<translation id="2965611304828530558">&lt;p&gt;Kur arrin te një lidhje ose një kontroll, ai fokusohet automatikisht. Shtyp &lt;span class='key'&gt;Enter&lt;/span&gt; për të klikuar te një lidhje ose një buton. &lt;/p&gt; &lt;p&gt; Kur një kontroll i fokusuar (si p.sh. një kuti teksti ose një kuti liste) kap tastet e shigjetave, shtyp tastin &lt;span class='key'&gt;Esc&lt;/span&gt; të pasuar nga shigjeta e majtë ose e djathtë për të vazhduar me shfletimin me tastierë. &lt;/p&gt; &lt;p&gt; Si alternativë, shtyp tastin &lt;span class='key'&gt;Tab&lt;/span&gt; për të lëvizur te kontrolli tjetër që mund të fokusohet. &lt;/p&gt;</translation>
+<translation id="2965611304828530558">&lt;p&gt;Kur arrin te një lidhje ose një kontroll, ai fokusohet automatikisht. Shtyp &lt;span class='key'&gt;Enter&lt;/span&gt; për të klikuar te një lidhje ose një buton. &lt;/p&gt; &lt;p&gt; Kur një kontroll i fokusuar (si p.sh. një kuti teksti ose një kuti liste) kap tastet e shigjetave, shtyp tastin &lt;span class='key'&gt;Esc&lt;/span&gt; të pasuar nga shigjeta e majtë ose e djathtë për të vazhduar me "Shfletimin me kursorin e tekstit". &lt;/p&gt; &lt;p&gt; Si alternativë, shtyp tastin &lt;span class='key'&gt;Tab&lt;/span&gt; për të lëvizur te kontrolli tjetër që mund të fokusohet. &lt;/p&gt;</translation>
<translation id="3252573918265662711">Konfigurimi</translation>
<translation id="3410969471888629217">Harro personalizimet e sajtit</translation>
<translation id="3435896845095436175">Aktivizo</translation>
@@ -31,7 +31,7 @@
<translation id="4769065380738716500">Imazhet janë zëvendësuar me tekstet e tyre alternative.</translation>
<translation id="4896660567607030658">Nuk ka komente, thjesht trego kursorin.</translation>
<translation id="4937901943818762779">Lejo imazhet e animuara, por vetëm një herë.</translation>
-<translation id="4949131196216960195">Shtyp &lt;span class='key'&gt;Kërko&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tasti i rritjes së ndriçimit ose F7) për të aktivizuar shfletimin "Caret". Shtype përsëri për ta çaktivizuar.</translation>
+<translation id="4949131196216960195">Shtyp &lt;span class='key'&gt;Kërko&lt;/span&gt; + &lt;img src='increase_brightness.png'&gt; (tasti i rritjes së ndriçimit ose F7) për të aktivizuar "Shfletimin me kursorin e tekstit". Shtype përsëri për ta çaktivizuar.</translation>
<translation id="4954450790315188152">Kur shfletimi me kursorin e tekstit është i aktivizuar:</translation>
<translation id="5041932793799765940">Rregullimi i ngjyrave</translation>
<translation id="5094574508723441140">Kontrasti i rritur</translation>
@@ -52,7 +52,7 @@
<translation id="633394792577263429">Mbaj shtypur poshtë &lt;span class='key'&gt;Kontrollo&lt;/span&gt; për të lëvizur sipas fjalëve.</translation>
<translation id="6550675742724504774">Opsionet</translation>
<translation id="6838518108677880446">Konfigurimi:</translation>
-<translation id="690628312087070417">Kur shenja e vendosjes kërcen me një distancë të madhe:</translation>
+<translation id="690628312087070417">Kur kursori i tekstit kërcen me një distancë të madhe:</translation>
<translation id="6965382102122355670">Në rregull</translation>
<translation id="7379645913608427028">Shkalla</translation>
<translation id="7384431257964758081">Kontrasti i lartë është i aktivizuar</translation>
diff --git a/chromium/ui/accessibility/platform/BUILD.gn b/chromium/ui/accessibility/platform/BUILD.gn
index 691a701457d..732f9af354d 100644
--- a/chromium/ui/accessibility/platform/BUILD.gn
+++ b/chromium/ui/accessibility/platform/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
import("//mojo/public/tools/bindings/mojom.gni")
@@ -108,7 +107,7 @@ source_set("platform") {
"ax_platform_node_mac.mm",
]
- libs = [
+ frameworks = [
"AppKit.framework",
"Foundation.framework",
]
diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux.cc b/chromium/ui/accessibility/platform/atk_util_auralinux.cc
index fb4e4250e8e..08f09382480 100644
--- a/chromium/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/chromium/ui/accessibility/platform/atk_util_auralinux.cc
@@ -56,10 +56,6 @@ static KeySnoopFuncMap& GetActiveKeySnoopFunctions() {
static guint AtkUtilAuraLinuxAddKeyEventListener(
AtkKeySnoopFunc key_snoop_function,
gpointer data) {
- if (!ui::AXPlatformNode::GetAccessibilityMode().has_mode(
- ui::AXMode::kNativeAPIs))
- return 0;
-
static guint current_key_event_listener_id = 0;
current_key_event_listener_id++;
@@ -131,7 +127,8 @@ DiscardAtkKeyEvent AtkUtilAuraLinux::HandleAtkKeyEvent(
AtkKeyEventStruct* key_event) {
DCHECK(key_event);
- if (!GetInstance()->ShouldEnableAccessibility())
+ if (!ui::AXPlatformNode::GetAccessibilityMode().has_mode(
+ ui::AXMode::kNativeAPIs))
return DiscardAtkKeyEvent::Retain;
GetInstance()->InitializeAsync();
diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux_unittest.cc b/chromium/ui/accessibility/platform/atk_util_auralinux_unittest.cc
index c68c338d67d..3d5f300a81d 100644
--- a/chromium/ui/accessibility/platform/atk_util_auralinux_unittest.cc
+++ b/chromium/ui/accessibility/platform/atk_util_auralinux_unittest.cc
@@ -17,6 +17,7 @@ namespace ui {
class AtkUtilAuraLinuxTest : public AXPlatformNodeTest {
public:
AtkUtilAuraLinuxTest() {
+ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
// We need to create a platform node in order to install it as the root
// ATK node. The ATK bridge will complain if we try to use it without a
// root node installed.
@@ -65,6 +66,25 @@ TEST_F(AtkUtilAuraLinuxTest, KeySnooping) {
AtkUtilAuraLinux* atk_util = AtkUtilAuraLinux::GetInstance();
atk_util->HandleAtkKeyEvent(&atk_key_event);
+ // AX mode is enabled and Key snooping works.
+ EXPECT_EQ(keyval_seen, 55);
+
+ TestAXNodeWrapper* wrapper =
+ TestAXNodeWrapper::GetOrCreate(GetTree(), GetRootAsAXNode());
+ DCHECK(wrapper);
+ AXMode prev_mode = wrapper->ax_platform_node()->ax_mode_;
+ // Disables AX mode.
+ wrapper->ax_platform_node()->ax_mode_ = 0;
+ keyval_seen = 0;
+ atk_util->HandleAtkKeyEvent(&atk_key_event);
+ // When AX mode is not enabled, Key snooping doesn't work.
+ EXPECT_EQ(keyval_seen, 0);
+
+ // Restores the previous AX mode.
+ wrapper->ax_platform_node()->ax_mode_ = prev_mode;
+ keyval_seen = 0;
+ atk_util->HandleAtkKeyEvent(&atk_key_event);
+ // AX mode is set again, Key snooping works.
EXPECT_EQ(keyval_seen, 55);
atk_remove_key_event_listener(listener_id);
diff --git a/chromium/ui/accessibility/platform/ax_fragment_root_win.cc b/chromium/ui/accessibility/platform/ax_fragment_root_win.cc
index 163fb8446dd..381d6c9596a 100644
--- a/chromium/ui/accessibility/platform/ax_fragment_root_win.cc
+++ b/chromium/ui/accessibility/platform/ax_fragment_root_win.cc
@@ -53,16 +53,10 @@ class AXFragmentRootPlatformNodeWin : public AXPlatformNodeWin,
UIA_VALIDATE_CALL_1_ARG(result);
*result = nullptr;
- // We currently only support the custom UIA property ID for unique id and we
- // ignore |start_after_element|.
+ // We currently only support the custom UIA property ID for unique id.
if (property_id ==
UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId() &&
value.vt == VT_BSTR) {
- // TODO: We should support the case when |start_after_element| isn't
- // nullptr for unique id (https://crbug.com/1098160).
- if (start_after_element)
- return E_INVALIDARG;
-
int32_t ax_unique_id;
if (!base::StringToInt(value.bstrVal, &ax_unique_id))
return S_OK;
@@ -70,12 +64,22 @@ class AXFragmentRootPlatformNodeWin : public AXPlatformNodeWin,
// In the Windows accessibility platform implementation, id 0 represents
// self; a positive id represents the immediate descendants; and a
// negative id represents a unique id that can be mapped to any node.
- if (AXPlatformNodeWin* node_win =
+ if (AXPlatformNodeWin* result_platform_node =
static_cast<AXPlatformNodeWin*>(GetFromUniqueId(-ax_unique_id))) {
- node_win->QueryInterface(IID_PPV_ARGS(result));
+ if (start_after_element) {
+ Microsoft::WRL::ComPtr<AXPlatformNodeWin> start_after_platform_node;
+ if (!SUCCEEDED(start_after_element->QueryInterface(
+ IID_PPV_ARGS(&start_after_platform_node))))
+ return E_INVALIDARG;
+
+ // We want |result| to be nullptr if it comes before or is equal to
+ // |start_after_element|.
+ if (start_after_platform_node->CompareTo(*result_platform_node) >= 0)
+ return S_OK;
+ }
+
+ return result_platform_node->QueryInterface(IID_PPV_ARGS(result));
}
-
- return S_OK;
}
return E_INVALIDARG;
diff --git a/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc b/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
index e26ad0446c1..e0e03afa16c 100644
--- a/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc
@@ -43,7 +43,7 @@ class AXFragmentRootTest : public AXPlatformNodeWinTest {
AXFragmentRootTest& operator=(const AXFragmentRootTest&) = delete;
};
-TEST_F(AXFragmentRootTest, UIAFindItemByProperty) {
+TEST_F(AXFragmentRootTest, UIAFindItemByPropertyUniqueId) {
AXNodeData root;
root.id = 1;
root.role = ax::mojom::Role::kRootWebArea;
@@ -69,64 +69,196 @@ TEST_F(AXFragmentRootTest, UIAFindItemByProperty) {
Init(root, text1, button, text2);
InitFragmentRoot();
- ComPtr<IRawElementProviderSimple> raw_element_provider_simple;
+ ComPtr<IRawElementProviderSimple> root_raw_element_provider_simple;
ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
- IID_PPV_ARGS(&raw_element_provider_simple));
+ IID_PPV_ARGS(&root_raw_element_provider_simple));
+ ComPtr<IRawElementProviderSimple> text1_raw_element_provider_simple =
+ GetIRawElementProviderSimpleFromChildIndex(0);
+ ComPtr<IRawElementProviderSimple> button_raw_element_provider_simple =
+ GetIRawElementProviderSimpleFromChildIndex(1);
+
+ AXNode* text1_node = GetRootAsAXNode()->children()[0];
+ AXNode* button_node = GetRootAsAXNode()->children()[1];
ComPtr<IItemContainerProvider> item_container_provider;
- EXPECT_HRESULT_SUCCEEDED(raw_element_provider_simple->GetPatternProvider(
+ EXPECT_HRESULT_SUCCEEDED(root_raw_element_provider_simple->GetPatternProvider(
UIA_ItemContainerPatternId, &item_container_provider));
ASSERT_NE(nullptr, item_container_provider.Get());
- // Fetch the AxUniqueId of "root", and verify we can retrieve its
- // corresponding IRawElementProviderSimple through FindItemByProperty().
ScopedVariant unique_id_variant;
- int32_t unique_id = AXPlatformNodeFromNode(GetRootAsAXNode())->GetUniqueId();
- unique_id_variant.Set(
- SysAllocString(base::NumberToString16(-unique_id).c_str()));
+ int32_t unique_id;
ComPtr<IRawElementProviderSimple> result;
- EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
- nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
- unique_id_variant, &result));
- EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"root");
- result.Reset();
- unique_id_variant.Release();
-
- // Fetch the AxUniqueId of "text1", and verify we can retrieve its
+
+ // When |start_after_element| is an invalid element, we should fail at finding
+ // the item.
+ {
+ unique_id = AXPlatformNodeFromNode(GetRootAsAXNode())->GetUniqueId();
+ unique_id_variant.Set(
+ SysAllocString(base::NumberToString16(-unique_id).c_str()));
+
+ ComPtr<IRawElementProviderSimple> invalid_element_provider_simple;
+ EXPECT_HRESULT_SUCCEEDED(
+ MockIRawElementProviderSimple::CreateMockIRawElementProviderSimple(
+ &invalid_element_provider_simple));
+
+ EXPECT_HRESULT_FAILED(item_container_provider->FindItemByProperty(
+ invalid_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ result.Reset();
+ unique_id_variant.Release();
+ }
+
+ // Fetch the AxUniqueId of "root", and verify we can retrieve its
// corresponding IRawElementProviderSimple through FindItemByProperty().
- unique_id =
- AXPlatformNodeFromNode(GetRootAsAXNode()->children()[0])->GetUniqueId();
- unique_id_variant.Set(
- SysAllocString(base::NumberToString16(-unique_id).c_str()));
- EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
- nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
- unique_id_variant, &result));
- EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text1");
- result.Reset();
- unique_id_variant.Release();
+ {
+ unique_id = AXPlatformNodeFromNode(GetRootAsAXNode())->GetUniqueId();
+ unique_id_variant.Set(
+ SysAllocString(base::NumberToString16(-unique_id).c_str()));
+
+ // When |start_after_element| of FindItemByProperty() is nullptr, we should
+ // be able to find "text1".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"root");
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "text1", there
+ // should be no element found, since "text1" comes after the element we are
+ // looking for.
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ text1_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_EQ(nullptr, result.Get());
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "button", there
+ // should be no element found, since "button" comes after the element we are
+ // looking for.
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ button_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_EQ(nullptr, result.Get());
+
+ result.Reset();
+ unique_id_variant.Release();
+ }
+
+ // Fetch the AxUniqueId of "text1", and verify if we can retrieve its
+ // corresponding IRawElementProviderSimple through FindItemByProperty().
+ {
+ unique_id = AXPlatformNodeFromNode(text1_node)->GetUniqueId();
+ unique_id_variant.Set(
+ SysAllocString(base::NumberToString16(-unique_id).c_str()));
+
+ // When |start_after_element| of FindItemByProperty() is nullptr, we should
+ // be able to find "text1".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text1");
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "text1", there
+ // should be no element found, since "text1" equals the element we are
+ // looking for.
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ text1_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_EQ(nullptr, result.Get());
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "button", there
+ // should be no element found, since "button" comes after the element we are
+ // looking for.
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ button_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_EQ(nullptr, result.Get());
+ result.Reset();
+ unique_id_variant.Release();
+ }
// Fetch the AxUniqueId of "button", and verify we can retrieve its
// corresponding IRawElementProviderSimple through FindItemByProperty().
- AXNode* button_node = GetRootAsAXNode()->children()[1];
- unique_id = AXPlatformNodeFromNode(button_node)->GetUniqueId();
- unique_id_variant.Set(
- SysAllocString(base::NumberToString16(-unique_id).c_str()));
- EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
- nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
- unique_id_variant, &result));
- EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"button");
- result.Reset();
- unique_id_variant.Release();
+ {
+ unique_id = AXPlatformNodeFromNode(button_node)->GetUniqueId();
+ unique_id_variant.Set(
+ SysAllocString(base::NumberToString16(-unique_id).c_str()));
+
+ // When |start_after_element| of FindItemByProperty() is nullptr, we should
+ // be able to find "button".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"button");
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "text1", we should
+ // be able to find "button".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ text1_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"button");
+ result.Reset();
+
+ // When |start_after_element| of FindItemByProperty() is "button", there
+ // should be no element found, since "button" equals the element we are
+ // looking for.
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ button_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_EQ(nullptr, result.Get());
+ result.Reset();
+ unique_id_variant.Release();
+ }
// Fetch the AxUniqueId of "text2", and verify we can retrieve its
// corresponding IRawElementProviderSimple through FindItemByProperty().
- unique_id = AXPlatformNodeFromNode(button_node->children()[0])->GetUniqueId();
- unique_id_variant.Set(
- SysAllocString(base::NumberToString16(-unique_id).c_str()));
- EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
- nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
- unique_id_variant, &result));
- EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text2");
+ {
+ unique_id =
+ AXPlatformNodeFromNode(button_node->children()[0])->GetUniqueId();
+ unique_id_variant.Set(
+ SysAllocString(base::NumberToString16(-unique_id).c_str()));
+
+ // When |start_after_element| of FindItemByProperty() is nullptr, we should
+ // be able to find "text2".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ nullptr, UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text2");
+
+ // When |start_after_element| of FindItemByProperty() is root, we should
+ // be able to find "text2".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ root_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text2");
+
+ // When |start_after_element| of FindItemByProperty() is "text1", we should
+ // be able to find "text2".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ text1_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text2");
+
+ // When |start_after_element| of FindItemByProperty() is "button", we should
+ // be able to find "text2".
+ EXPECT_HRESULT_SUCCEEDED(item_container_provider->FindItemByProperty(
+ button_raw_element_provider_simple.Get(),
+ UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId(),
+ unique_id_variant, &result));
+ EXPECT_UIA_BSTR_EQ(result, UIA_NamePropertyId, L"text2");
+ }
}
TEST_F(AXFragmentRootTest, TestUIAGetFragmentRoot) {
diff --git a/chromium/ui/accessibility/platform/ax_platform_node.cc b/chromium/ui/accessibility/platform/ax_platform_node.cc
index 6bf1a7ace16..4f8d6305164 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node.cc
@@ -62,6 +62,14 @@ int32_t AXPlatformNode::GetUniqueId() const {
return GetDelegate() ? GetDelegate()->GetUniqueId().Get() : -1;
}
+void AXPlatformNode::SetIsPrimaryWebContentsForWindow(bool is_primary) {
+ is_primary_web_contents_for_window_ = is_primary;
+}
+
+bool AXPlatformNode::IsPrimaryWebContentsForWindow() const {
+ return is_primary_web_contents_for_window_;
+}
+
std::string AXPlatformNode::ToString() {
return GetDelegate() ? GetDelegate()->ToString() : "No delegate";
}
diff --git a/chromium/ui/accessibility/platform/ax_platform_node.h b/chromium/ui/accessibility/platform/ax_platform_node.h
index 9621fcb9a5c..e8ad7edc608 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node.h
@@ -81,7 +81,7 @@ class AX_EXPORT AXPlatformNode {
// this object.
virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type) = 0;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Fire a platform-specific notification to announce |text|.
virtual void AnnounceText(const base::string16& text) = 0;
#endif
@@ -92,6 +92,10 @@ class AX_EXPORT AXPlatformNode {
// Return true if this object is equal to or a descendant of |ancestor|.
virtual bool IsDescendantOf(AXPlatformNode* ancestor) const = 0;
+ // Set |this| as the primary web contents for the window.
+ void SetIsPrimaryWebContentsForWindow(bool is_primary);
+ bool IsPrimaryWebContentsForWindow() const;
+
// Return the unique ID.
int32_t GetUniqueId() const;
@@ -109,6 +113,8 @@ class AX_EXPORT AXPlatformNode {
virtual ~AXPlatformNode();
private:
+ FRIEND_TEST_ALL_PREFIXES(AtkUtilAuraLinuxTest, KeySnooping);
+
// Global ObserverList for AXMode changes.
static base::LazyInstance<
base::ObserverList<AXModeObserver>::Unchecked>::Leaky ax_mode_observers_;
@@ -123,6 +129,8 @@ class AX_EXPORT AXPlatformNode {
// underlying content.
static gfx::NativeViewAccessible popup_focus_override_;
+ bool is_primary_web_contents_for_window_ = false;
+
DISALLOW_COPY_AND_ASSIGN(AXPlatformNode);
};
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc
index a4554aaac25..04125c6fdf5 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -1024,6 +1024,18 @@ gunichar GetCharacterAtOffset(AtkText* atk_text, int offset) {
return code_point;
}
+gint GetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType coords) {
+ g_return_val_if_fail(ATK_IS_TEXT(text), -1);
+
+ AtkObject* atk_object = ATK_OBJECT(text);
+ AXPlatformNodeAuraLinux* obj =
+ AXPlatformNodeAuraLinux::FromAtkObject(atk_object);
+ if (!obj)
+ return -1;
+
+ return obj->GetTextOffsetAtPoint(x, y, coords);
+}
+
// This function returns a single character as a UTF-8 encoded C string because
// the character may be encoded into more than one byte.
char* GetCharacter(AtkText* atk_text,
@@ -1447,6 +1459,7 @@ void Init(AtkTextIface* iface) {
iface->get_text = GetText;
iface->get_character_count = GetCharacterCount;
iface->get_character_at_offset = GetCharacterAtOffset;
+ iface->get_offset_at_point = GetOffsetAtPoint;
iface->get_text_after_offset = GetTextAfterOffset;
iface->get_text_before_offset = GetTextBeforeOffset;
iface->get_text_at_offset = GetTextAtOffset;
@@ -2059,7 +2072,6 @@ const gchar* GetName(AtkObject* atk_object) {
const gchar* AtkGetName(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetName);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetName(atk_object);
}
@@ -2077,7 +2089,6 @@ const gchar* GetDescription(AtkObject* atk_object) {
const gchar* AtkGetDescription(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetDescription);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetDescription(atk_object);
}
@@ -2094,7 +2105,6 @@ gint GetNChildren(AtkObject* atk_object) {
gint AtkGetNChildren(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetNChildren);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetNChildren(atk_object);
}
@@ -2117,7 +2127,6 @@ AtkObject* RefChild(AtkObject* atk_object, gint index) {
AtkObject* AtkRefChild(AtkObject* atk_object, gint index) {
RecordAccessibilityAtkApi(UmaAtkApi::kRefChild);
- AXPlatformNodeAuraLinux::EnableAXMode();
return RefChild(atk_object, index);
}
@@ -2134,7 +2143,6 @@ gint GetIndexInParent(AtkObject* atk_object) {
gint AtkGetIndexInParent(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetIndexInParent);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetIndexInParent(atk_object);
}
@@ -2151,7 +2159,6 @@ AtkObject* GetParent(AtkObject* atk_object) {
AtkObject* AtkGetParent(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetParent);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetParent(atk_object);
}
@@ -2167,6 +2174,9 @@ AtkRelationSet* RefRelationSet(AtkObject* atk_object) {
AtkRelationSet* AtkRefRelationSet(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kRefRelationSet);
+ // Enables AX mode. Most AT does not call AtkRefRelationSet, but Orca does,
+ // which is why it's a good signal to enable accessibility for Orca users
+ // without too many false positives.
AXPlatformNodeAuraLinux::EnableAXMode();
return RefRelationSet(atk_object);
}
@@ -2184,6 +2194,9 @@ AtkAttributeSet* GetAttributes(AtkObject* atk_object) {
AtkAttributeSet* AtkGetAttributes(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetAttributes);
+ // Enables AX mode. Most AT does not call AtkGetAttributes, but Orca does,
+ // which is why it's a good signal to enable accessibility for Orca users
+ // without too many false positives.
AXPlatformNodeAuraLinux::EnableAXMode();
return GetAttributes(atk_object);
}
@@ -2200,7 +2213,6 @@ AtkRole GetRole(AtkObject* atk_object) {
AtkRole AtkGetRole(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kGetRole);
- AXPlatformNodeAuraLinux::EnableAXMode();
return GetRole(atk_object);
}
@@ -2223,7 +2235,6 @@ AtkStateSet* RefStateSet(AtkObject* atk_object) {
AtkStateSet* AtkRefStateSet(AtkObject* atk_object) {
RecordAccessibilityAtkApi(UmaAtkApi::kRefStateSet);
- AXPlatformNodeAuraLinux::EnableAXMode();
return RefStateSet(atk_object);
}
@@ -2486,7 +2497,10 @@ void AXPlatformNodeAuraLinux::SetDocumentParentOnFrameIfNecessary() {
frame->SetDocumentParent(parent_atk_object);
}
-AtkObject* AXPlatformNodeAuraLinux::FindFirstWebContentDocument() {
+AtkObject* AXPlatformNodeAuraLinux::FindPrimaryWebContentDocument() {
+ // It could get multiple web contents since additional web content is added,
+ // when the DevTools window is opened.
+ std::vector<AtkObject*> web_content_candidates;
for (auto child_iterator_ptr = GetDelegate()->ChildrenBegin();
*child_iterator_ptr != *GetDelegate()->ChildrenEnd();
++(*child_iterator_ptr)) {
@@ -2498,14 +2512,39 @@ AtkObject* AXPlatformNodeAuraLinux::FindFirstWebContentDocument() {
continue;
if (child_node->GetAtkRole() != ATK_ROLE_DOCUMENT_WEB)
continue;
- return child;
+ web_content_candidates.push_back(child);
}
+ if (web_content_candidates.empty())
+ return nullptr;
+
+ // If it finds just one web content, return it.
+ if (web_content_candidates.size() == 1)
+ return web_content_candidates[0];
+
+ for (auto* object : web_content_candidates) {
+ auto* child_node = AXPlatformNodeAuraLinux::FromAtkObject(object);
+ // If it is a primary web contents, return it.
+ if (child_node->IsPrimaryWebContentsForWindow())
+ return object;
+ }
return nullptr;
}
+bool AXPlatformNodeAuraLinux::IsWebDocumentForRelations() {
+ AtkObject* atk_object = GetOrCreateAtkObject();
+ if (!atk_object)
+ return false;
+ AXPlatformNodeAuraLinux* parent = FromAtkObject(GetParent());
+ if (!parent || !GetDelegate()->IsWebContent() ||
+ GetAtkRole() != ATK_ROLE_DOCUMENT_WEB)
+ return false;
+ return parent->FindPrimaryWebContentDocument() == atk_object;
+}
+
AtkObject* AXPlatformNodeAuraLinux::CreateAtkObject() {
if (GetData().role != ax::mojom::Role::kApplication &&
+ !GetDelegate()->IsToplevelBrowserWindow() &&
!GetAccessibilityMode().has_mode(AXMode::kNativeAPIs))
return nullptr;
if (GetDelegate()->IsChildOfLeaf())
@@ -2593,10 +2632,7 @@ void AXPlatformNodeAuraLinux::StaticInitialize() {
// static
void AXPlatformNodeAuraLinux::EnableAXMode() {
- // TODO(https://crbug.com/1086506): After figuring out what API calls are
- // giving us false positives, enable it again. For now, don't activate AX
- // through ATK calls.
- // AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
+ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
}
AtkRole AXPlatformNodeAuraLinux::GetAtkRole() const {
@@ -2819,7 +2855,7 @@ AtkRole AXPlatformNodeAuraLinux::GetAtkRole() const {
case ax::mojom::Role::kMenu:
return ATK_ROLE_MENU;
case ax::mojom::Role::kMenuButton:
- return ATK_ROLE_MENU;
+ return ATK_ROLE_MENU_ITEM;
case ax::mojom::Role::kMenuBar:
return ATK_ROLE_MENU_BAR;
case ax::mojom::Role::kMenuItem:
@@ -3051,7 +3087,7 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) {
atk_state_set_add_state(atk_state_set, ATK_STATE_BUSY);
if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kModal))
atk_state_set_add_state(atk_state_set, ATK_STATE_MODAL);
- if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+ if (data.IsSelectable())
atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE);
if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED);
@@ -3174,7 +3210,7 @@ AtkRelationSet* AXPlatformNodeAuraLinux::GetAtkRelations() {
AtkRelationSet* relation_set = atk_relation_set_new();
- if (GetDelegate()->IsWebContent() && GetAtkRole() == ATK_ROLE_DOCUMENT_WEB) {
+ if (IsWebDocumentForRelations()) {
AtkObject* parent_frame = FindAtkObjectParentFrame(atk_object);
if (parent_frame) {
atk_relation_set_add_relation_by_type(
@@ -3183,7 +3219,7 @@ AtkRelationSet* AXPlatformNodeAuraLinux::GetAtkRelations() {
}
if (auto* document_parent = FromAtkObject(document_parent_)) {
- AtkObject* document = document_parent->FindFirstWebContentDocument();
+ AtkObject* document = document_parent->FindPrimaryWebContentDocument();
if (document) {
atk_relation_set_add_relation_by_type(relation_set, ATK_RELATION_EMBEDS,
document);
@@ -3297,8 +3333,12 @@ void AXPlatformNodeAuraLinux::OnActiveDescendantChanged() {
// Active-descendant-changed notifications are typically only relevant when
// the change is within the focused widget.
- if (atk_object != g_current_focused)
+ if (!g_current_focused)
return;
+ if (auto* focused_node = FromAtkObject(g_current_focused)) {
+ if (!focused_node->IsDescendantOf(this))
+ return;
+ }
AtkObject* descendant = GetActiveDescendantOfCurrentFocused();
if (descendant == g_current_active_descendant)
@@ -3341,6 +3381,16 @@ void AXPlatformNodeAuraLinux::OnCheckedStateChanged() {
GetData().GetCheckedState() != ax::mojom::CheckedState::kFalse);
}
+void AXPlatformNodeAuraLinux::OnEnabledChanged() {
+ AtkObject* obj = GetOrCreateAtkObject();
+ if (!obj)
+ return;
+
+ atk_object_notify_state_change(
+ obj, ATK_STATE_ENABLED,
+ GetData().GetRestriction() != ax::mojom::Restriction::kDisabled);
+}
+
void AXPlatformNodeAuraLinux::OnExpandedStateChanged(bool is_expanded) {
// When a list box is expanded, it becomes visible. This means that it might
// now have a different role (the role for hidden Views is kUnknown). We
@@ -3630,6 +3680,15 @@ bool AXPlatformNodeAuraLinux::SelectionAndFocusAreTheSame() {
}
bool AXPlatformNodeAuraLinux::EmitsAtkTextEvents() const {
+ // Objects which do not implement AtkText cannot emit AtkText events.
+ if (!atk_object_ || !ATK_IS_TEXT(atk_object_))
+ return false;
+
+ // Objects which do implement AtkText, but are ignored or invisible should not
+ // emit AtkText events.
+ if (IsInvisibleOrIgnored())
+ return false;
+
// If this node is not a static text node, it supports the full AtkText
// interface.
if (GetAtkRole() != kStaticRole)
@@ -3733,6 +3792,21 @@ void AXPlatformNodeAuraLinux::EmitCaretChangedSignal() {
UTF16ToUnicodeOffsetInText(selection.second));
}
+void AXPlatformNodeAuraLinux::OnTextAttributesChanged() {
+ if (!EmitsAtkTextEvents()) {
+ if (auto* parent = FromAtkObject(GetParent()))
+ parent->OnTextAttributesChanged();
+ return;
+ }
+
+ AtkObject* atk_object = GetOrCreateAtkObject();
+ if (!atk_object)
+ return;
+
+ DCHECK(ATK_IS_TEXT(atk_object));
+ g_signal_emit_by_name(atk_object, "text-attributes-changed");
+}
+
void AXPlatformNodeAuraLinux::OnTextSelectionChanged() {
int32_t anchor_node_id, focus_node_id;
int anchor_offset, focus_offset;
@@ -3791,13 +3865,17 @@ void AXPlatformNodeAuraLinux::OnDescriptionChanged() {
}
void AXPlatformNodeAuraLinux::OnSortDirectionChanged() {
- // TODO(crbug.com/1074380) fire correct event for aria-sort change.
- // AtkObject* atk_object = GetOrCreateAtkObject();
- // if (!atk_object)
- // return;
+ AXPlatformNodeBase* table = GetTable();
+ if (!table)
+ return;
+
+ AtkObject* atk_table = table->GetNativeViewAccessible();
+ DCHECK(ATK_IS_TABLE(atk_table));
- // std::string sort;
- // GetStringAttribute(ax::mojom::StringAttribute::kSortDirection, &sort);
+ if (GetData().role == ax::mojom::Role::kColumnHeader)
+ g_signal_emit_by_name(atk_table, "row-reordered");
+ else if (GetData().role == ax::mojom::Role::kRowHeader)
+ g_signal_emit_by_name(atk_table, "column-reordered");
}
void AXPlatformNodeAuraLinux::OnValueChanged() {
@@ -4120,6 +4198,28 @@ size_t AXPlatformNodeAuraLinux::UnicodeToUTF16OffsetInText(int unicode_offset) {
return utf16_offset;
}
+int AXPlatformNodeAuraLinux::GetTextOffsetAtPoint(int x,
+ int y,
+ AtkCoordType atk_coord_type) {
+ if (!GetExtentsRelativeToAtkCoordinateType(atk_coord_type).Contains(x, y))
+ return -1;
+
+ AtkObject* atk_object = GetOrCreateAtkObject();
+ if (!atk_object)
+ return -1;
+
+ int count = atk_text::GetCharacterCount(ATK_TEXT(atk_object));
+ for (int i = 0; i < count; i++) {
+ int out_x, out_y, out_width, out_height;
+ atk_text::GetCharacterExtents(ATK_TEXT(atk_object), i, &out_x, &out_y,
+ &out_width, &out_height, atk_coord_type);
+ gfx::Rect rect(out_x, out_y, out_width, out_height);
+ if (rect.Contains(x, y))
+ return i;
+ }
+ return -1;
+}
+
gfx::Vector2d AXPlatformNodeAuraLinux::GetParentOriginInScreenCoordinates()
const {
AtkObject* parent = GetParent();
@@ -4324,7 +4424,7 @@ bool AXPlatformNodeAuraLinux::
if (!child)
continue;
- if (child->IsTextOnlyObject()) {
+ if (child->IsText()) {
current_offset += child->GetName().size();
} else {
// Add an offset for the embedded character.
@@ -4793,6 +4893,10 @@ const TextAttributeList& AXPlatformNodeAuraLinux::GetTextAttributes(
UTF16ToUnicodeOffsetInText(style_start));
SetIntPointerValueIfNotNull(end_offset,
UTF16ToUnicodeOffsetInText(style_end));
+
+ if (iterator == offset_to_text_attributes_.end())
+ return default_text_attributes_;
+
return iterator->second;
}
@@ -4849,7 +4953,7 @@ AXPlatformNodeAuraLinux::GetHypertextExtentsOfChild(
// If this object is a text only object, it is included directly into this
// node's hypertext, otherwise it is represented as an embedded object
// character.
- int size = child->IsTextOnlyObject() ? child->GetName().size() : 1;
+ int size = child->IsText() ? child->GetName().size() : 1;
if (child == child_to_find)
return std::make_pair(current_offset, current_offset + size);
current_offset += size;
@@ -4869,7 +4973,7 @@ void AXPlatformNodeAuraLinux::ActivateFindInPageInParent(int start_offset,
if (!extents_in_parent.has_value())
return;
- DCHECK(IsTextOnlyObject());
+ DCHECK(IsText());
parent->ActivateFindInPageResult(extents_in_parent->first + start_offset,
extents_in_parent->first + end_offset);
}
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h
index 610b2041b97..6c63a193e82 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -201,6 +201,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
// Event helpers
void OnActiveDescendantChanged();
void OnCheckedStateChanged();
+ void OnEnabledChanged();
void OnExpandedStateChanged(bool is_expanded);
void OnFocused();
void OnWindowActivated();
@@ -210,6 +211,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
void OnAllMenusEnded();
void OnSelected();
void OnSelectedChildrenChanged();
+ void OnTextAttributesChanged();
void OnTextSelectionChanged();
void OnValueChanged();
void OnNameChanged();
@@ -246,6 +248,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
const base::OffsetAdjuster::Adjustments& GetHypertextAdjustments();
size_t UTF16ToUnicodeOffsetInText(size_t utf16_offset);
size_t UnicodeToUTF16OffsetInText(int unicode_offset);
+ int GetTextOffsetAtPoint(int x, int y, AtkCoordType atk_coord_type);
// Called on a toplevel frame to set the document parent, which is the parent
// of the toplevel document. This is used to properly express the ATK embeds
@@ -337,8 +340,11 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
// the toplevel frame which contains the node.
void SetDocumentParentOnFrameIfNecessary();
- // Find the first child which is a document containing web content.
- AtkObject* FindFirstWebContentDocument();
+ // Find the child which is a document containing the primary web content.
+ AtkObject* FindPrimaryWebContentDocument();
+
+ // Returns true if it is a web content for the relations.
+ bool IsWebDocumentForRelations();
// If a selection that intersects this node get the full selection
// including start and end node ids.
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 706471c0a84..2415e6e1177 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -145,9 +145,7 @@ static void TestAtkObjectStringAttribute(
EnsureAtkObjectDoesNotHaveAttribute(atk_object, attribute_name);
const char* tests[] = {
- "",
- "a string with spaces"
- "a string with , a comma",
+ "", "a string with spaces", "a string with , a comma",
"\xE2\x98\xBA", // The smiley emoji.
};
@@ -2556,4 +2554,81 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestDialogActiveWhenChildFocused) {
EXPECT_FALSE(AtkObjectHasState(dialog_obj, ATK_STATE_ACTIVE));
}
+// Tests if kActiveDescendantChanged on unfocused node triggers a focused event.
+TEST_F(AXPlatformNodeAuraLinuxTest,
+ TestActiveDescendantChangedOnUnfocusedNode) {
+ AXNodeData menu;
+ menu.id = 1;
+ menu.role = ax::mojom::Role::kMenu;
+ menu.AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId, 4);
+ menu.child_ids = {2, 3};
+
+ AXNodeData input;
+ input.id = 2;
+ input.role = ax::mojom::Role::kTextField;
+ input.AddState(ax::mojom::State::kFocusable);
+ input.AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId, 4);
+
+ AXNodeData container;
+ container.id = 3;
+ container.role = ax::mojom::Role::kGenericContainer;
+ container.child_ids = {4, 5};
+
+ AXNodeData menu_item_1;
+ menu_item_1.id = 4;
+ menu_item_1.role = ax::mojom::Role::kMenuItemCheckBox;
+
+ AXNodeData menu_item_2;
+ menu_item_2.id = 5;
+ menu_item_2.role = ax::mojom::Role::kMenuItemCheckBox;
+
+ Init(menu, input, container, menu_item_1, menu_item_2);
+ TestAXNodeWrapper::SetGlobalIsWebContent(true);
+
+ // Creates TestAXNodeWrapper for the first menu item to keep the current
+ // active descendant.
+ AtkObjectFromNode(GetRootAsAXNode()->children()[1]->children()[0]);
+
+ // Sets focus to the input node.
+ AXNode* input_node = GetRootAsAXNode()->children()[0];
+ GetPlatformNode(input_node)
+ ->NotifyAccessibilityEvent(ax::mojom::Event::kFocus);
+
+ bool saw_active_focus_state_change = false;
+ AtkObject* menu_2_atk_object =
+ AtkObjectFromNode(GetRootAsAXNode()->children()[1]->children()[1]);
+ EXPECT_TRUE(ATK_IS_OBJECT(menu_2_atk_object));
+ g_object_ref(menu_2_atk_object);
+ // Registers callback to get focus event on |menu_2_atk_object|.
+ g_signal_connect(menu_2_atk_object, "state-change",
+ G_CALLBACK(+[](AtkObject* atkobject, gchar* state_changed,
+ gboolean new_value, bool* flag) {
+ if (!g_strcmp0(state_changed, "focused") && new_value)
+ *flag = true;
+ }),
+ &saw_active_focus_state_change);
+
+ // Updates the active descendant node from the node id 4 to the node id 5;
+ AXNode* menu_node = GetRootAsAXNode();
+ AXNodeData menu_new_data(menu);
+ menu_new_data.AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
+ 5);
+ menu_node->SetData(menu_new_data);
+
+ AXNodeData input_new_data(input);
+ input_new_data.AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
+ 5);
+ input_node->SetData(input_new_data);
+
+ // Notifies active descendant is changed.
+ GetPlatformNode(menu_node)->NotifyAccessibilityEvent(
+ ax::mojom::Event::kActiveDescendantChanged);
+ // The current active descendant node, |menu_2_atk_object|, should get the
+ // focused event.
+ EXPECT_TRUE(saw_active_focus_state_change);
+
+ TestAXNodeWrapper::SetGlobalIsWebContent(false);
+ g_object_unref(menu_2_atk_object);
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_base.cc
index 36081b70c14..8f8b2e972c7 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_base.cc
@@ -182,11 +182,105 @@ base::Optional<int> AXPlatformNodeBase::GetIndexInParent() {
if (parent->ChildAtIndex(i) == current)
return i;
}
+
+ // If the parent has a modal dialog, it doesn't count other children.
+ if (parent->delegate_ && parent->delegate_->HasModalDialog())
+ return base::nullopt;
+
NOTREACHED()
<< "Unable to find the child in the list of its parent's children.";
return base::nullopt;
}
+base::stack<gfx::NativeViewAccessible> AXPlatformNodeBase::GetAncestors() {
+ base::stack<gfx::NativeViewAccessible> ancestors;
+ gfx::NativeViewAccessible current_node = GetNativeViewAccessible();
+ while (current_node) {
+ ancestors.push(current_node);
+ current_node = FromNativeViewAccessible(current_node)->GetParent();
+ }
+
+ return ancestors;
+}
+
+base::Optional<int> AXPlatformNodeBase::CompareTo(AXPlatformNodeBase& other) {
+ // We define two node's relative positions in the following way:
+ // 1. this->CompareTo(other) == 0:
+ // - |this| and |other| are the same node.
+ // 2. this->CompareTo(other) < 0:
+ // - |this| is an ancestor of |other|.
+ // - |this|'s first uncommon ancestor comes before |other|'s first uncommon
+ // ancestor. The first uncommon ancestor is defined as the immediate child
+ // of the lowest common anestor of the two nodes. The first uncommon
+ // ancestor of |this| and |other| share the same parent (i.e. lowest common
+ // ancestor), so we can just compare the first uncommon ancestors' child
+ // indices to determine their relative positions.
+ // 3. this->CompareTo(other) == nullopt:
+ // - |this| and |other| are not comparable. E.g. they do not have a common
+ // ancestor.
+ //
+ // Another way to look at the nodes' relative positions/logical orders is that
+ // they are equivalent to pre-order traversal of the tree. If we pre-order
+ // traverse from the root, the node that we visited earlier is always going to
+ // be before (logically less) the node we visit later.
+
+ if (this == &other)
+ return base::Optional<int>(0);
+
+ // Compute the ancestor stacks of both positions and traverse them from the
+ // top most ancestor down, so we can discover the first uncommon ancestors.
+ // The first uncommon ancestor is the immediate child of the lowest common
+ // ancestor.
+ gfx::NativeViewAccessible common_ancestor = nullptr;
+ base::stack<gfx::NativeViewAccessible> our_ancestors = GetAncestors();
+ base::stack<gfx::NativeViewAccessible> other_ancestors = other.GetAncestors();
+
+ // Start at the root and traverse down. Keep going until the |this|'s ancestor
+ // chain and |other|'s ancestor chain disagree. The last node before they
+ // disagree is the lowest common ancestor.
+ while (!our_ancestors.empty() && !other_ancestors.empty() &&
+ our_ancestors.top() == other_ancestors.top()) {
+ common_ancestor = our_ancestors.top();
+ our_ancestors.pop();
+ other_ancestors.pop();
+ }
+
+ // Nodes do not have a common ancestor, they are not comparable.
+ if (!common_ancestor)
+ return base::nullopt;
+
+ // Compute the logical order when the common ancestor is |this| or |other|.
+ auto* common_ancestor_platform_node =
+ FromNativeViewAccessible(common_ancestor);
+ if (common_ancestor_platform_node == this)
+ return base::Optional<int>(-1);
+ if (common_ancestor_platform_node == &other)
+ return base::Optional<int>(1);
+
+ // Compute the logical order of |this| and |other| by using their first
+ // uncommon ancestors.
+ if (!our_ancestors.empty() && !other_ancestors.empty()) {
+ base::Optional<int> this_index_in_parent =
+ FromNativeViewAccessible(our_ancestors.top())->GetIndexInParent();
+ base::Optional<int> other_index_in_parent =
+ FromNativeViewAccessible(other_ancestors.top())->GetIndexInParent();
+
+ if (!this_index_in_parent || !other_index_in_parent)
+ return base::nullopt;
+
+ int this_uncommon_ancestor_index = this_index_in_parent.value();
+ int other_uncommon_ancestor_index = other_index_in_parent.value();
+ DCHECK_NE(this_uncommon_ancestor_index, other_uncommon_ancestor_index)
+ << "Deepest uncommon ancestors should truly be uncommon, i.e. not "
+ "the same.";
+
+ return base::Optional<int>(this_uncommon_ancestor_index -
+ other_uncommon_ancestor_index);
+ }
+
+ return base::nullopt;
+}
+
// AXPlatformNode overrides.
void AXPlatformNodeBase::Destroy() {
@@ -214,7 +308,7 @@ void AXPlatformNodeBase::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
}
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void AXPlatformNodeBase::AnnounceText(const base::string16& text) {}
#endif
@@ -499,52 +593,6 @@ bool AXPlatformNodeBase::IsDocument() const {
return ui::IsDocument(GetData().role);
}
-bool AXPlatformNodeBase::IsTextOnlyObject() const {
- if (!delegate_)
- return false;
-
- // In Legacy Layout, a list marker has no children and is thus represented on
- // all platforms as a leaf node that exposes the marker itself, i.e., it forms
- // part of the AX tree's text representation. In contrast, in Layout NG, a
- // list marker has a static text child.
- if (GetData().role == ax::mojom::Role::kListMarker)
- return !GetChildCount();
- return ui::IsText(GetData().role);
-}
-
-bool AXPlatformNodeBase::IsTextField() const {
- return GetData().IsTextField();
-}
-
-bool AXPlatformNodeBase::IsPlainTextField() const {
- return GetData().IsPlainTextField();
-}
-
-bool AXPlatformNodeBase::IsRichTextField() const {
- return GetData().IsRichTextField();
-}
-
-base::string16 AXPlatformNodeBase::GetHypertext() const {
- if (!delegate_)
- return base::string16();
-
- // Hypertext of platform leaves, which internally are composite objects, are
- // represented with the inner text of the internal composite object. These
- // don't exist on non-web content.
- if (IsChildOfLeaf())
- return GetInnerText();
-
- if (hypertext_.needs_update)
- UpdateComputedHypertext();
- return hypertext_.hypertext;
-}
-
-base::string16 AXPlatformNodeBase::GetInnerText() const {
- if (!delegate_)
- return base::string16();
- return delegate_->GetInnerText();
-}
-
bool AXPlatformNodeBase::IsSelectionItemSupported() const {
switch (GetData().role) {
// An ARIA 1.1+ role of "cell", or a role of "row" inside
@@ -589,6 +637,43 @@ bool AXPlatformNodeBase::IsSelectionItemSupported() const {
}
}
+bool AXPlatformNodeBase::IsTextField() const {
+ return GetData().IsTextField();
+}
+
+bool AXPlatformNodeBase::IsPlainTextField() const {
+ return GetData().IsPlainTextField();
+}
+
+bool AXPlatformNodeBase::IsRichTextField() const {
+ return GetData().IsRichTextField();
+}
+
+bool AXPlatformNodeBase::IsText() const {
+ return delegate_ && delegate_->IsText();
+}
+
+base::string16 AXPlatformNodeBase::GetHypertext() const {
+ if (!delegate_)
+ return base::string16();
+
+ // Hypertext of platform leaves, which internally are composite objects, are
+ // represented with the inner text of the internal composite object. These
+ // don't exist on non-web content.
+ if (IsChildOfLeaf())
+ return GetInnerText();
+
+ if (hypertext_.needs_update)
+ UpdateComputedHypertext();
+ return hypertext_.hypertext;
+}
+
+base::string16 AXPlatformNodeBase::GetInnerText() const {
+ if (!delegate_)
+ return base::string16();
+ return delegate_->GetInnerText();
+}
+
base::string16 AXPlatformNodeBase::GetRangeValueText() const {
float fval;
base::string16 value =
@@ -1165,6 +1250,27 @@ void AXPlatformNodeBase::ComputeAttributes(PlatformAttributeList* attributes) {
AddAttributeToList("src", src, attributes);
}
+ if (GetData().HasIntAttribute(ax::mojom::IntAttribute::kTextAlign)) {
+ auto text_align = static_cast<ax::mojom::TextAlign>(
+ GetData().GetIntAttribute(ax::mojom::IntAttribute::kTextAlign));
+ switch (text_align) {
+ case ax::mojom::TextAlign::kNone:
+ break;
+ case ax::mojom::TextAlign::kLeft:
+ AddAttributeToList("text-align", "left", attributes);
+ break;
+ case ax::mojom::TextAlign::kRight:
+ AddAttributeToList("text-align", "right", attributes);
+ break;
+ case ax::mojom::TextAlign::kCenter:
+ AddAttributeToList("text-align", "center", attributes);
+ break;
+ case ax::mojom::TextAlign::kJustify:
+ AddAttributeToList("text-align", "justify", attributes);
+ break;
+ }
+ }
+
// Text fields need to report the attribute "text-model:a1" to instruct
// screen readers to use IAccessible2 APIs to handle text editing in this
// object (as opposed to treating it like a native Windows text box).
@@ -1255,7 +1361,7 @@ void AXPlatformNodeBase::UpdateComputedHypertext() const {
// Similar to Firefox, we don't expose text-only objects in IA2 and ATK
// hypertext with the embedded object character. We copy all of their text
// instead.
- if (child_iter->IsTextOnlyObject()) {
+ if (child_iter->IsText()) {
hypertext_.hypertext += child_iter->GetNameAsString16();
} else {
int32_t char_offset = static_cast<int32_t>(hypertext_.hypertext.size());
@@ -1393,12 +1499,12 @@ int32_t AXPlatformNodeBase::GetHypertextOffsetFromChild(
// Handle the case when we are dealing with a text-only child.
// Text-only children should not be present at tree roots and so no
// cross-tree traversal is necessary.
- if (child->IsTextOnlyObject()) {
+ if (child->IsText()) {
int32_t hypertext_offset = 0;
for (auto child_iter = AXPlatformNodeChildrenBegin();
child_iter != AXPlatformNodeChildrenEnd() && child_iter.get() != child;
++child_iter) {
- if (child_iter->IsTextOnlyObject()) {
+ if (child_iter->IsText()) {
hypertext_offset +=
static_cast<int32_t>(child_iter->GetHypertext().size());
} else {
@@ -1482,7 +1588,7 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
if (!common_parent)
return -1;
- DCHECK(!(common_parent->IsTextOnlyObject()));
+ DCHECK(!(common_parent->IsText()));
// Case 2. Is the selection endpoint inside a descendant of this object?
//
@@ -1494,7 +1600,7 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
GetHypertextOffsetFromDescendant(endpoint_object);
auto* parent = static_cast<AXPlatformNodeBase*>(
FromNativeViewAccessible(endpoint_object->GetParent()));
- if (parent == this && endpoint_object->IsTextOnlyObject()) {
+ if (parent == this && endpoint_object->IsText()) {
// Due to a historical design decision, the hypertext of the immediate
// parents of text objects includes all their text. We therefore need to
// adjust the hypertext offset in the parent by adding any text offset.
@@ -1697,10 +1803,10 @@ void AXPlatformNodeBase::ComputeHypertextRemovedAndInserted(
*old_len = 0;
*new_len = 0;
- // Do not compute for static text objects, otherwise redundant text change
+ // Do not compute for text objects, otherwise redundant text change
// announcements will occur in live regions, as the parent hypertext also
// changes.
- if (GetData().role == ax::mojom::Role::kStaticText)
+ if (IsText())
return;
const base::string16& old_text = old_hypertext.hypertext;
@@ -1858,7 +1964,7 @@ std::string AXPlatformNodeBase::GetInvalidValue() const {
if (static_cast<ax::mojom::InvalidState>(
target->GetIntAttribute(ax::mojom::IntAttribute::kInvalidState)) ==
ax::mojom::InvalidState::kNone &&
- target->IsTextOnlyObject() && target->GetParent()) {
+ target->IsText() && target->GetParent()) {
// Text nodes need to reflect the invalid state of their parent object,
// otherwise spelling and grammar errors communicated through aria-invalid
// won't be reflected in text attributes.
@@ -1985,21 +2091,21 @@ ui::TextAttributeList AXPlatformNodeBase::ComputeTextAttributes() const {
attributes.push_back(std::make_pair("language", language));
}
- auto text_direction = static_cast<ax::mojom::TextDirection>(
+ auto text_direction = static_cast<ax::mojom::WritingDirection>(
GetIntAttribute(ax::mojom::IntAttribute::kTextDirection));
switch (text_direction) {
- case ax::mojom::TextDirection::kNone:
+ case ax::mojom::WritingDirection::kNone:
break;
- case ax::mojom::TextDirection::kLtr:
+ case ax::mojom::WritingDirection::kLtr:
attributes.push_back(std::make_pair("writing-mode", "lr"));
break;
- case ax::mojom::TextDirection::kRtl:
+ case ax::mojom::WritingDirection::kRtl:
attributes.push_back(std::make_pair("writing-mode", "rl"));
break;
- case ax::mojom::TextDirection::kTtb:
+ case ax::mojom::WritingDirection::kTtb:
attributes.push_back(std::make_pair("writing-mode", "tb"));
break;
- case ax::mojom::TextDirection::kBtt:
+ case ax::mojom::WritingDirection::kBtt:
// Not listed in the IA2 Spec.
attributes.push_back(std::make_pair("writing-mode", "bt"));
break;
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.h b/chromium/ui/accessibility/platform/ax_platform_node_base.h
index c14c8c7e698..fb31b4e8892 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_base.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_base.h
@@ -74,12 +74,24 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
// the list of its parent's children, or its parent doesn't have children.
virtual base::Optional<int> GetIndexInParent();
+ // Returns a stack of ancestors of this node. The node at the top of the stack
+ // is the top most ancestor.
+ base::stack<gfx::NativeViewAccessible> GetAncestors();
+
+ // Returns an optional integer indicating the logical order of this node
+ // compared to another node or returns an empty optional if the nodes
+ // are not comparable.
+ // 0: if this position is logically equivalent to the other node
+ // <0: if this position is logically less than (before) the other node
+ // >0: if this position is logically greater than (after) the other node
+ base::Optional<int> CompareTo(AXPlatformNodeBase& other);
+
// AXPlatformNode.
void Destroy() override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
void NotifyAccessibilityEvent(ax::mojom::Event event_type) override;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void AnnounceText(const base::string16& text) override;
#endif
@@ -235,10 +247,6 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
// Returns true if this node can be scrolled in the vertical direction.
bool IsVerticallyScrollable() const;
- // Returns true if this node has a role of StaticText, LineBreak, or
- // InlineTextBox
- bool IsTextOnlyObject() const;
-
// See AXNodeData::IsTextField().
bool IsTextField() const;
@@ -248,6 +256,9 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
// See AXNodeData::IsRichTextField().
bool IsRichTextField() const;
+ // See AXNode::IsText().
+ bool IsText() const;
+
// Determines whether an element should be exposed with checkable state, and
// possibly the checked state. Examples are check box and radio button.
// Objects that are exposed as toggle buttons use the platform pressed state
@@ -349,6 +360,7 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
protected:
bool IsDocument() const;
+
bool IsSelectionItemSupported() const;
// Get the range value text, which might come from aria-valuetext or
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_base_unittest.cc
index 8df32b83c91..54991d0b866 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_base_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_base_unittest.cc
@@ -400,4 +400,131 @@ TEST(AXPlatformNodeBaseTest, TestSelectedChildrenMixed) {
EXPECT_EQ(fourth_child, fourth_selected_node->GetNativeViewAccessible());
}
+TEST(AXPlatformNodeBaseTest, CompareTo) {
+ // Compare the nodes' logical orders for the following tree. Node name is
+ // denoted according to its id (i.e. "n#" is id#). Nodes that have smaller ids
+ // are always logically less than nodes with bigger ids.
+ //
+ // n1
+ // |
+ // __ n2 ___
+ // / \ \
+ // n3 _ n8 n9
+ // / \ \ \
+ // n4 n5 n6 n10
+ // /
+ // n7
+ AXPlatformNode::NotifyAddAXModeFlags(kAXModeComplete);
+ AXNodeData node1;
+ node1.id = 1;
+ node1.role = ax::mojom::Role::kWebArea;
+ node1.child_ids = {2};
+
+ AXNodeData node2;
+ node2.id = 2;
+ node2.role = ax::mojom::Role::kStaticText;
+ node2.child_ids = {3, 8, 9};
+
+ AXNodeData node3;
+ node3.id = 3;
+ node3.role = ax::mojom::Role::kStaticText;
+ node3.child_ids = {4, 5, 6};
+
+ AXNodeData node4;
+ node4.id = 4;
+ node4.role = ax::mojom::Role::kStaticText;
+
+ AXNodeData node5;
+ node5.id = 5;
+ node5.role = ax::mojom::Role::kStaticText;
+
+ AXNodeData node6;
+ node6.id = 6;
+ node6.role = ax::mojom::Role::kStaticText;
+ node6.child_ids = {7};
+
+ AXNodeData node7;
+ node7.id = 7;
+ node7.role = ax::mojom::Role::kStaticText;
+
+ AXNodeData node8;
+ node8.id = 8;
+ node8.role = ax::mojom::Role::kStaticText;
+
+ AXNodeData node9;
+ node9.id = 9;
+ node9.role = ax::mojom::Role::kStaticText;
+ node9.child_ids = {10};
+
+ AXNodeData node10;
+ node10.id = 10;
+ node10.role = ax::mojom::Role::kStaticText;
+
+ AXTreeUpdate update;
+ update.root_id = 1;
+ update.nodes = {node1, node2, node3, node4, node5,
+ node6, node7, node8, node9, node10};
+
+ AXTree tree(update);
+
+ // Retrieve the nodes in a level-order traversal way.
+ auto* n1 = static_cast<AXPlatformNodeBase*>(
+ TestAXNodeWrapper::GetOrCreate(&tree, tree.root())->ax_platform_node());
+ auto* n2 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n1->ChildAtIndex(0)));
+ auto* n3 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(0)));
+ auto* n8 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(1)));
+ auto* n9 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n2->ChildAtIndex(2)));
+ auto* n4 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(0)));
+ auto* n5 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(1)));
+ auto* n6 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n3->ChildAtIndex(2)));
+ auto* n10 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n9->ChildAtIndex(0)));
+ auto* n7 = static_cast<AXPlatformNodeBase*>(
+ AXPlatformNode::FromNativeViewAccessible(n6->ChildAtIndex(0)));
+
+ // Test for two nodes that do not share the same root. They should not be
+ // comparable.
+ AXPlatformNodeBase detached_node;
+ EXPECT_EQ(base::nullopt, n1->CompareTo(detached_node));
+
+ // Create a test vector of all the tree nodes arranged in a pre-order
+ // traversal way. The node that has a smaller index in the vector should also
+ // be logically less (comes before) the nodes with bigger index.
+ std::vector<AXPlatformNodeBase*> preorder_tree_nodes = {n1, n2, n3, n4, n5,
+ n6, n7, n8, n9, n10};
+ // Test through all permutations of lhs/rhs comparisons of nodes from
+ // |preorder_tree_nodes|.
+ for (auto* lhs : preorder_tree_nodes) {
+ for (auto* rhs : preorder_tree_nodes) {
+ int expected_result = 0;
+ if (lhs->GetData().id < rhs->GetData().id)
+ expected_result = -1;
+ else if (lhs->GetData().id > rhs->GetData().id)
+ expected_result = 1;
+
+ EXPECT_NE(base::nullopt, lhs->CompareTo(*rhs));
+ int actual_result = 0;
+ if (lhs->CompareTo(*rhs) < 0)
+ actual_result = -1;
+ else if (lhs->CompareTo(*rhs) > 0)
+ actual_result = 1;
+
+ SCOPED_TRACE(testing::Message()
+ << "lhs.id=" << base::NumberToString(lhs->GetData().id)
+ << ", rhs.id=" << base::NumberToString(rhs->GetData().id)
+ << ", lhs->CompareTo(*rhs)={actual:"
+ << base::NumberToString(actual_result) << ", expected:"
+ << base::NumberToString(expected_result) << "}");
+
+ EXPECT_EQ(expected_result, actual_result);
+ }
+ }
+}
} // namespace ui
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_delegate.h b/chromium/ui/accessibility/platform/ax_platform_node_delegate.h
index e8ff6876672..6b9c27e9e66 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -115,6 +115,9 @@ class AX_EXPORT AXPlatformNodeDelegate {
// Get the child of a node given a 0-based index.
virtual gfx::NativeViewAccessible ChildAtIndex(int index) = 0;
+ // Returns true if it has a modal dialog.
+ virtual bool HasModalDialog() const = 0;
+
// Gets the first child of a node, or nullptr if no children exist.
virtual gfx::NativeViewAccessible GetFirstChild() = 0;
@@ -142,6 +145,11 @@ class AX_EXPORT AXPlatformNodeDelegate {
// layer.
virtual bool IsLeaf() const = 0;
+ // Returns true if this is a top-level browser window that doesn't have a
+ // parent accessible node, or its parent is the application accessible node on
+ // platforms that have one.
+ virtual bool IsToplevelBrowserWindow() = 0;
+
// If this object is exposed to the platform's accessibility layer, returns
// this object. Otherwise, returns the platform leaf under which this object
// is found.
@@ -252,6 +260,9 @@ class AX_EXPORT AXPlatformNodeDelegate {
// Get whether this node is a minimized window.
virtual bool IsMinimized() const = 0;
+ // See AXNode::IsText().
+ virtual bool IsText() const = 0;
+
// Get whether this node is in web content.
virtual bool IsWebContent() const = 0;
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index 39d55498dbd..2a44e03fafe 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -94,6 +94,10 @@ gfx::NativeViewAccessible AXPlatformNodeDelegateBase::ChildAtIndex(int index) {
return nullptr;
}
+bool AXPlatformNodeDelegateBase::HasModalDialog() const {
+ return false;
+}
+
gfx::NativeViewAccessible AXPlatformNodeDelegateBase::GetFirstChild() {
if (GetChildCount() > 0)
return ChildAtIndex(0);
@@ -141,6 +145,10 @@ bool AXPlatformNodeDelegateBase::IsLeaf() const {
return !GetChildCount();
}
+bool AXPlatformNodeDelegateBase::IsToplevelBrowserWindow() {
+ return false;
+}
+
bool AXPlatformNodeDelegateBase::IsChildOfPlainTextField() const {
return false;
}
@@ -521,6 +529,10 @@ bool AXPlatformNodeDelegateBase::IsMinimized() const {
return false;
}
+bool AXPlatformNodeDelegateBase::IsText() const {
+ return ui::IsText(GetData().role);
+}
+
bool AXPlatformNodeDelegateBase::IsWebContent() const {
return false;
}
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.h b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.h
index 7dd68b928d7..8158fe7da5d 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -62,6 +62,9 @@ class AX_EXPORT AXPlatformNodeDelegateBase : public AXPlatformNodeDelegate {
// Get the child of a node given a 0-based index.
gfx::NativeViewAccessible ChildAtIndex(int index) override;
+ // Returns true if it has a modal dialog.
+ bool HasModalDialog() const override;
+
gfx::NativeViewAccessible GetFirstChild() override;
gfx::NativeViewAccessible GetLastChild() override;
gfx::NativeViewAccessible GetNextSibling() override;
@@ -70,6 +73,7 @@ class AX_EXPORT AXPlatformNodeDelegateBase : public AXPlatformNodeDelegate {
bool IsChildOfLeaf() const override;
bool IsChildOfPlainTextField() const override;
bool IsLeaf() const override;
+ bool IsToplevelBrowserWindow() override;
gfx::NativeViewAccessible GetClosestPlatformObject() const override;
class ChildIteratorBase : public ChildIterator {
@@ -151,6 +155,7 @@ class AX_EXPORT AXPlatformNodeDelegateBase : public AXPlatformNodeDelegate {
// Get whether this node is a minimized window.
bool IsMinimized() const override;
+ bool IsText() const override;
// Get whether this node is in web content.
bool IsWebContent() const override;
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm
index 911344e83a4..5bc70451e1a 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -159,7 +159,7 @@ RoleMap BuildRoleMap() {
{ax::mojom::Role::kMath, NSAccessibilityGroupRole},
{ax::mojom::Role::kMenu, NSAccessibilityMenuRole},
{ax::mojom::Role::kMenuBar, NSAccessibilityMenuBarRole},
- {ax::mojom::Role::kMenuButton, NSAccessibilityButtonRole},
+ {ax::mojom::Role::kMenuButton, NSAccessibilityMenuButtonRole},
{ax::mojom::Role::kMenuItem, NSAccessibilityMenuItemRole},
{ax::mojom::Role::kMenuItemCheckBox, NSAccessibilityMenuItemRole},
{ax::mojom::Role::kMenuItemRadio, NSAccessibilityMenuItemRole},
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
index d86ee466120..579d72e75dc 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
@@ -52,50 +52,6 @@ class AXPlatformNodeTextProviderTest : public AXPlatformNodeWinTest {
}
};
-class MockIRawElementProviderSimple
- : public CComObjectRootEx<CComMultiThreadModel>,
- public IRawElementProviderSimple {
- public:
- BEGIN_COM_MAP(MockIRawElementProviderSimple)
- COM_INTERFACE_ENTRY(IRawElementProviderSimple)
- END_COM_MAP()
-
- MockIRawElementProviderSimple() {}
- ~MockIRawElementProviderSimple() {}
-
- static HRESULT CreateMockIRawElementProviderSimple(
- IRawElementProviderSimple** provider) {
- CComObject<MockIRawElementProviderSimple>* raw_element_provider = nullptr;
- HRESULT hr = CComObject<MockIRawElementProviderSimple>::CreateInstance(
- &raw_element_provider);
- if (SUCCEEDED(hr)) {
- *provider = raw_element_provider;
- }
-
- return hr;
- }
-
- //
- // IRawElementProviderSimple methods.
- //
- IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id,
- IUnknown** result) override {
- return E_NOTIMPL;
- }
-
- IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id,
- VARIANT* result) override {
- return E_NOTIMPL;
- }
-
- IFACEMETHODIMP
- get_ProviderOptions(enum ProviderOptions* ret) override { return E_NOTIMPL; }
-
- IFACEMETHODIMP
- get_HostRawElementProvider(IRawElementProviderSimple** provider) override {
- return E_NOTIMPL;
- }
-};
TEST_F(AXPlatformNodeTextProviderTest, ITextProviderRangeFromChild) {
ui::AXNodeData text_data;
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 379fa000578..3b302c38aec 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -8,7 +8,9 @@
#include <vector>
#include "base/i18n/string_search.h"
+#include "base/win/scoped_safearray.h"
#include "base/win/scoped_variant.h"
+#include "base/win/variant_vector.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
@@ -385,13 +387,14 @@ HRESULT AXPlatformNodeTextRangeProviderWin::FindAttributeRange(
current_platform_node = static_cast<AXPlatformNodeWin*>(
delegate->GetFromNodeID(current_start->GetAnchor()->id()));
- base::win::ScopedVariant current_attribute_value;
+ base::win::VariantVector current_attribute_value;
if (FAILED(current_platform_node->GetTextAttributeValue(
- text_attribute_id, current_attribute_value.Receive())))
+ text_attribute_id, current_start->text_offset(),
+ current_end->text_offset(), &current_attribute_value))) {
return E_FAIL;
+ }
- if (VARCMP_EQ == VarCmp(&attribute_val, current_attribute_value.AsInput(),
- LOCALE_USER_DEFAULT, 0)) {
+ if (!current_attribute_value.Compare(attribute_val)) {
// When we encounter an AXRange instance that matches the attribute
// and its value which we are looking for and no previously matched text
// range exists, we expand or initialize the matched range.
@@ -469,10 +472,11 @@ HRESULT AXPlatformNodeTextRangeProviderWin::GetAttributeValue(
UIA_VALIDATE_TEXTRANGEPROVIDER_CALL_1_OUT(value);
NormalizeTextRange();
- base::win::ScopedVariant attribute_value_variant;
+ base::win::VariantVector attribute_value;
// The range is inclusive, so advance our endpoint to the next position
- auto end = end_->AsLeafTextPosition()->CreateNextAnchorPosition();
+ const auto end_leaf_text_position = end_->AsLeafTextPosition();
+ auto end = end_leaf_text_position->CreateNextAnchorPosition();
// Iterate over anchor positions
for (auto it = start_->AsLeafTextPosition();
@@ -500,25 +504,34 @@ HRESULT AXPlatformNodeTextRangeProviderWin::GetAttributeValue(
DCHECK(platform_node);
}
- base::win::ScopedVariant current_variant;
+ base::win::VariantVector current_value;
+ const bool at_end_leaf_text_anchor =
+ it->anchor_id() == end_leaf_text_position->anchor_id() &&
+ it->tree_id() == end_leaf_text_position->tree_id();
+ const base::Optional<int> start_offset =
+ it->IsTextPosition() ? base::make_optional(it->text_offset())
+ : base::nullopt;
+ const base::Optional<int> end_offset =
+ at_end_leaf_text_anchor
+ ? base::make_optional(end_leaf_text_position->text_offset())
+ : base::nullopt;
HRESULT hr = platform_node->GetTextAttributeValue(
- attribute_id, current_variant.Receive());
+ attribute_id, start_offset, end_offset, &current_value);
if (FAILED(hr))
return E_FAIL;
- if (attribute_value_variant.type() == VT_EMPTY) {
- attribute_value_variant.Reset(current_variant);
- if (attribute_value_variant.type() == VT_UNKNOWN) {
- *value = attribute_value_variant.Release();
- return S_OK;
- }
- } else if (attribute_value_variant.Compare(current_variant)) {
+ if (attribute_value.Type() == VT_EMPTY) {
+ attribute_value = std::move(current_value);
+ } else if (attribute_value != current_value) {
V_VT(value) = VT_UNKNOWN;
return ::UiaGetReservedMixedAttributeValue(&V_UNKNOWN(value));
}
}
- *value = attribute_value_variant.Release();
+ if (ShouldReleaseTextAttributeAsSafearray(attribute_id, attribute_value))
+ *value = attribute_value.ReleaseAsSafearrayVariant();
+ else
+ *value = attribute_value.ReleaseAsScalarVariant();
return S_OK;
}
@@ -787,7 +800,28 @@ HRESULT AXPlatformNodeTextRangeProviderWin::Select() {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXTRANGE_SELECT);
UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
- AXNodeRange new_selection_range(start_->Clone(), end_->Clone());
+ AXPositionInstance selection_start = start_->Clone();
+ AXPositionInstance selection_end = end_->Clone();
+
+ // Blink only supports selections within a single tree. So if start_ and end_
+ // are in different trees, we can't directly pass them to the render process
+ // for selection.
+ if (selection_start->tree_id() != selection_end->tree_id()) {
+ // Prioritize the end position's tree, as a selection's focus object is the
+ // end of a selection.
+ selection_start = selection_end->CreatePositionAtStartOfAXTree();
+ }
+
+ DCHECK(!selection_start->IsNullPosition());
+ DCHECK(!selection_end->IsNullPosition());
+ DCHECK_EQ(selection_start->tree_id(), selection_end->tree_id());
+
+ AXPlatformNodeDelegate* delegate =
+ GetDelegate(selection_start->tree_id(), selection_start->anchor_id());
+ DCHECK(delegate);
+
+ AXNodeRange new_selection_range(std::move(selection_start),
+ std::move(selection_end));
RemoveFocusFromPreviousSelectionIfNeeded(new_selection_range);
AXActionData action_data;
@@ -796,7 +830,8 @@ HRESULT AXPlatformNodeTextRangeProviderWin::Select() {
action_data.focus_node_id = new_selection_range.focus()->anchor_id();
action_data.focus_offset = new_selection_range.focus()->text_offset();
action_data.action = ax::mojom::Action::kSetSelection;
- owner()->GetDelegate()->AccessibilityPerformAction(action_data);
+
+ delegate->AccessibilityPerformAction(action_data);
return S_OK;
}
@@ -1290,4 +1325,49 @@ AXPlatformNodeTextRangeProviderWin::GetLowestAccessibleCommonPlatformNode()
return platform_node->GetLowestAccessibleElement();
}
+// static
+bool AXPlatformNodeTextRangeProviderWin::TextAttributeIsArrayType(
+ TEXTATTRIBUTEID attribute_id) {
+ // https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-textattribute-ids
+ return attribute_id == UIA_AnnotationTypesAttributeId ||
+ attribute_id == UIA_TabsAttributeId;
+}
+
+// static
+bool AXPlatformNodeTextRangeProviderWin::TextAttributeIsUiaReservedValue(
+ const base::win::VariantVector& vector) {
+ // Reserved values are always IUnknown.
+ if (vector.Type() != VT_UNKNOWN)
+ return false;
+
+ base::win::ScopedVariant mixed_attribute_value_variant;
+ {
+ Microsoft::WRL::ComPtr<IUnknown> mixed_attribute_value;
+ HRESULT hr = ::UiaGetReservedMixedAttributeValue(&mixed_attribute_value);
+ DCHECK(SUCCEEDED(hr));
+ mixed_attribute_value_variant.Set(mixed_attribute_value.Get());
+ }
+
+ base::win::ScopedVariant not_supported_value_variant;
+ {
+ Microsoft::WRL::ComPtr<IUnknown> not_supported_value;
+ HRESULT hr = ::UiaGetReservedNotSupportedValue(&not_supported_value);
+ DCHECK(SUCCEEDED(hr));
+ not_supported_value_variant.Set(not_supported_value.Get());
+ }
+
+ return !vector.Compare(mixed_attribute_value_variant) ||
+ !vector.Compare(not_supported_value_variant);
+}
+
+// static
+bool AXPlatformNodeTextRangeProviderWin::ShouldReleaseTextAttributeAsSafearray(
+ TEXTATTRIBUTEID attribute_id,
+ const base::win::VariantVector& attribute_value) {
+ // |vector| may be pre-populated with a UIA reserved value. In such a case, we
+ // must release as a scalar variant.
+ return TextAttributeIsArrayType(attribute_id) &&
+ !TextAttributeIsUiaReservedValue(attribute_value);
+}
+
} // namespace ui
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index c967b191124..d591c89b53f 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -171,6 +171,13 @@ class AX_EXPORT __declspec(uuid("3071e40d-a10d-45ff-a59f-6e8e1138e2c1"))
const AXNodeRange& new_selection);
AXPlatformNodeWin* GetLowestAccessibleCommonPlatformNode() const;
+ static bool TextAttributeIsArrayType(TEXTATTRIBUTEID attribute_id);
+ static bool TextAttributeIsUiaReservedValue(
+ const base::win::VariantVector& vector);
+ static bool ShouldReleaseTextAttributeAsSafearray(
+ TEXTATTRIBUTEID attribute_id,
+ const base::win::VariantVector& vector);
+
Microsoft::WRL::ComPtr<AXPlatformNodeWin> owner_;
AXPositionInstance start_;
AXPositionInstance end_;
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index a698ca3c021..e1925cff631 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -39,49 +39,29 @@ namespace ui {
#define ASSERT_UIA_NOTSUPPORTED(expr) \
ASSERT_EQ(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (expr))
-#define EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(safearray, expected_property_values) \
- { \
- EXPECT_EQ(sizeof(V_R8(LPVARIANT(NULL))), \
- ::SafeArrayGetElemsize(safearray)); \
- ASSERT_EQ(1u, SafeArrayGetDim(safearray)); \
- LONG array_lower_bound; \
- ASSERT_HRESULT_SUCCEEDED( \
- SafeArrayGetLBound(safearray, 1, &array_lower_bound)); \
- LONG array_upper_bound; \
- ASSERT_HRESULT_SUCCEEDED( \
- SafeArrayGetUBound(safearray, 1, &array_upper_bound)); \
- double* array_data; \
- ASSERT_HRESULT_SUCCEEDED(::SafeArrayAccessData( \
- safearray, reinterpret_cast<void**>(&array_data))); \
- size_t count = array_upper_bound - array_lower_bound + 1; \
- ASSERT_EQ(expected_property_values.size(), count); \
- for (size_t i = 0; i < count; ++i) { \
- EXPECT_EQ(array_data[i], expected_property_values[i]); \
- } \
- ASSERT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(safearray)); \
- }
-
-#define EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(safearray, \
- expected_property_values) \
- { \
- EXPECT_EQ(sizeof(V_UNKNOWN(LPVARIANT(NULL))), \
- ::SafeArrayGetElemsize(safearray)); \
- EXPECT_EQ(1u, SafeArrayGetDim(safearray)); \
- LONG array_lower_bound; \
- EXPECT_HRESULT_SUCCEEDED( \
- SafeArrayGetLBound(safearray, 1, &array_lower_bound)); \
- LONG array_upper_bound; \
- EXPECT_HRESULT_SUCCEEDED( \
- SafeArrayGetUBound(safearray, 1, &array_upper_bound)); \
- ComPtr<IRawElementProviderSimple>* array_data; \
- EXPECT_HRESULT_SUCCEEDED(::SafeArrayAccessData( \
- safearray, reinterpret_cast<void**>(&array_data))); \
- size_t count = array_upper_bound - array_lower_bound + 1; \
- EXPECT_EQ(expected_property_values.size(), count); \
- for (size_t i = 0; i < count; ++i) { \
- EXPECT_EQ(array_data[i].Get(), expected_property_values[i].Get()); \
- } \
- EXPECT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(safearray)); \
+#define EXPECT_UIA_SAFEARRAY_EQ(safearray, expected_property_values) \
+ { \
+ using T = typename decltype(expected_property_values)::value_type; \
+ EXPECT_EQ(sizeof(T), ::SafeArrayGetElemsize(safearray)); \
+ EXPECT_EQ(1u, SafeArrayGetDim(safearray)); \
+ LONG array_lower_bound; \
+ EXPECT_HRESULT_SUCCEEDED( \
+ SafeArrayGetLBound(safearray, 1, &array_lower_bound)); \
+ LONG array_upper_bound; \
+ EXPECT_HRESULT_SUCCEEDED( \
+ SafeArrayGetUBound(safearray, 1, &array_upper_bound)); \
+ const size_t count = array_upper_bound - array_lower_bound + 1; \
+ EXPECT_EQ(expected_property_values.size(), count); \
+ if (sizeof(T) == ::SafeArrayGetElemsize(safearray) && \
+ count == expected_property_values.size()) { \
+ T* array_data; \
+ EXPECT_HRESULT_SUCCEEDED(::SafeArrayAccessData( \
+ safearray, reinterpret_cast<void**>(&array_data))); \
+ for (size_t i = 0; i < count; ++i) { \
+ EXPECT_EQ(array_data[i], expected_property_values[i]); \
+ } \
+ EXPECT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(safearray)); \
+ } \
}
#define EXPECT_UIA_TEXTATTRIBUTE_EQ(provider, attribute, variant) \
@@ -228,6 +208,30 @@ class AXPlatformNodeTextRangeProviderTest : public ui::AXPlatformNodeWinTest {
ASSERT_NE(nullptr, text_range_provider.Get());
}
+ void CreateTextRangeProviderWin(
+ ComPtr<AXPlatformNodeTextRangeProviderWin>& text_range_provider_win,
+ AXPlatformNodeWin* owner,
+ AXTreeID tree_id,
+ AXNode::AXID start_anchor_id,
+ int start_offset,
+ ax::mojom::TextAffinity start_affinity,
+ AXNode::AXID end_anchor_id,
+ int end_offset,
+ ax::mojom::TextAffinity end_affinity) {
+ AXNodePosition::AXPositionInstance range_start =
+ AXNodePosition::CreateTextPosition(tree_id, start_anchor_id,
+ start_offset, start_affinity);
+ AXNodePosition::AXPositionInstance range_end =
+ AXNodePosition::CreateTextPosition(tree_id, end_anchor_id, end_offset,
+ end_affinity);
+
+ ComPtr<ITextRangeProvider> text_range_provider =
+ AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
+ owner, std::move(range_start), std::move(range_end));
+
+ text_range_provider->QueryInterface(IID_PPV_ARGS(&text_range_provider_win));
+ }
+
void ComputeWordBoundariesOffsets(const std::string& text,
std::vector<int>& word_start_offsets,
std::vector<int>& word_end_offsets) {
@@ -2920,7 +2924,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
20, 50, 200, 30, /* check box */
220, 20, 30, 30, /* line 1 */
220, 50, 42, 30 /* line 2 */};
- EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(rectangles.Get(), expected_values);
rectangles.Reset();
// Move the text range end back by one character.
@@ -2936,7 +2940,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
20, 50, 200, 30, /* check box */
220, 20, 30, 30, /* line 1 */
220, 50, 35, 30 /* line 2 */};
- EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(rectangles.Get(), expected_values);
rectangles.Reset();
// Move the text range end back by one line.
@@ -2951,7 +2955,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
expected_values = {20, 20, 200, 30, /* button */
20, 50, 200, 30, /* check box */
220, 20, 30, 30 /* line 1 */};
- EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(rectangles.Get(), expected_values);
rectangles.Reset();
// Move the text range end back by one line.
@@ -2965,7 +2969,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
text_range_provider->GetBoundingRectangles(rectangles.Receive()));
expected_values = {20, 20, 200, 30, /* button */
20, 50, 200, 30 /* check box */};
- EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(rectangles.Get(), expected_values);
}
TEST_F(AXPlatformNodeTextRangeProviderTest,
@@ -3524,7 +3528,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
expected_values = {};
- EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(children.Get(), expected_values);
}
// Test static_text_6 - a leaf node should have no children.
@@ -3540,7 +3544,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
expected_values = {};
- EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(children.Get(), expected_values);
}
// Test static_text_3 - children should include inline_box_4 and inline_box_5.
@@ -3556,7 +3560,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
expected_values = {inline_box_4_raw, inline_box_5_raw};
- EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(children.Get(), expected_values);
}
// Test button_8 - a button should never expose its children.
@@ -3572,7 +3576,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
expected_values = {};
- EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(children.Get(), expected_values);
}
// Test document_2 - children should not include ignored nodes and nodes under
@@ -3592,7 +3596,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
static_text_6_raw, button_8_raw,
};
- EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+ EXPECT_UIA_SAFEARRAY_EQ(children.Get(), expected_values);
}
}
@@ -3612,10 +3616,18 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
0xDEADBEEFU);
text_data.AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xDEADC0DEU);
text_data.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr-CA");
- text_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ text_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
text_data.AddTextStyle(ax::mojom::TextStyle::kItalic);
text_data.SetTextPosition(ax::mojom::TextPosition::kSubscript);
text_data.SetRestriction(ax::mojom::Restriction::kReadOnly);
+ text_data.SetTextAlign(ax::mojom::TextAlign::kCenter);
+ text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes,
+ {(int)ax::mojom::MarkerType::kGrammar,
+ (int)ax::mojom::MarkerType::kSpelling});
+ text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts,
+ {0, 5});
+ text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
+ {9, 9});
text_data.SetName("some text");
ui::AXNodeData heading_data;
@@ -3625,7 +3637,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
heading_data.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
0xDEADBEEFU);
heading_data.AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xDEADC0DEU);
- heading_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ heading_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
heading_data.SetTextPosition(ax::mojom::TextPosition::kSuperscript);
heading_data.AddState(ax::mojom::State::kEditable);
heading_data.child_ids = {4};
@@ -3638,9 +3650,17 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
0xDEADBEEFU);
heading_text_data.AddIntAttribute(ax::mojom::IntAttribute::kColor,
0xDEADC0DEU);
- heading_text_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ heading_text_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
heading_text_data.SetTextPosition(ax::mojom::TextPosition::kSuperscript);
heading_text_data.AddState(ax::mojom::State::kEditable);
+ heading_text_data.SetTextAlign(ax::mojom::TextAlign::kJustify);
+ heading_text_data.AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerTypes,
+ {(int)ax::mojom::MarkerType::kSpelling});
+ heading_text_data.AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerStarts, {5});
+ heading_text_data.AddIntListAttribute(
+ ax::mojom::IntListAttribute::kMarkerEnds, {9});
heading_text_data.SetName("more text");
ui::AXNodeData mark_data;
@@ -3649,7 +3669,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
mark_data.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
0xDEADBEEFU);
mark_data.AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xDEADC0DEU);
- mark_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ mark_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
mark_data.child_ids = {6};
ui::AXNodeData mark_text_data;
@@ -3658,7 +3678,8 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
mark_text_data.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
0xDEADBEEFU);
mark_text_data.AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xDEADC0DEU);
- mark_text_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ mark_text_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
+ mark_text_data.SetTextAlign(ax::mojom::TextAlign::kNone);
mark_text_data.SetName("marked text");
ui::AXNodeData list_data;
@@ -3891,7 +3912,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
expected_variant);
expected_variant.Reset();
- expected_variant.Set(12.0f);
+ expected_variant.Set(12.0);
EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_FontSizeAttributeId,
expected_variant);
expected_variant.Reset();
@@ -3952,6 +3973,18 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
UIA_IsReadOnlyAttributeId, expected_variant);
expected_variant.Reset();
+ expected_variant.Set(HorizontalTextAlignment_Centered);
+ EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider,
+ UIA_HorizontalTextAlignmentAttributeId,
+ expected_variant);
+ expected_variant.Reset();
+
+ expected_variant.Set(HorizontalTextAlignment_Justified);
+ EXPECT_UIA_TEXTATTRIBUTE_EQ(heading_text_range_provider,
+ UIA_HorizontalTextAlignmentAttributeId,
+ expected_variant);
+ expected_variant.Reset();
+
expected_variant.Set(true);
EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_IsSubscriptAttributeId,
expected_variant);
@@ -4062,6 +4095,72 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
UIA_TextFlowDirectionsAttributeId,
expected_variant);
expected_variant.Reset();
+
+ {
+ // |text_node| has a grammar error on the entire text, and a spelling error
+ // for one word, so the range has mixed annotations.
+ EXPECT_UIA_TEXTATTRIBUTE_MIXED(text_range_provider,
+ UIA_AnnotationTypesAttributeId);
+
+ // start: TextPosition, anchor_id=2, text_offset=5,
+ // annotated_text=some <t>ext
+ // end : TextPosition, anchor_id=2, text_offset=9,
+ // annotated_text=some text<>
+ AXPlatformNodeWin* owner =
+ static_cast<AXPlatformNodeWin*>(AXPlatformNodeFromNode(text_node));
+ ComPtr<AXPlatformNodeTextRangeProviderWin> range_with_annotations;
+ CreateTextRangeProviderWin(
+ range_with_annotations, owner, tree_data.tree_id,
+ /*start_anchor_id=*/text_node->id(), /*start_offset=*/5,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/text_node->id(), /*end_offset=*/9,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
+
+ base::win::ScopedVariant annotation_types_variant;
+ EXPECT_HRESULT_SUCCEEDED(range_with_annotations->GetAttributeValue(
+ UIA_AnnotationTypesAttributeId, annotation_types_variant.Receive()));
+
+ EXPECT_EQ(annotation_types_variant.type(), VT_ARRAY | VT_I4);
+ std::vector<int> expected_annotations = {AnnotationType_SpellingError,
+ AnnotationType_GrammarError};
+ EXPECT_UIA_SAFEARRAY_EQ(V_ARRAY(annotation_types_variant.ptr()),
+ expected_annotations);
+ }
+
+ {
+ // |heading_text_node| has a a spelling error for one word, and no
+ // annotations for the remaining text, so the range has mixed annotations.
+ EXPECT_UIA_TEXTATTRIBUTE_MIXED(heading_text_range_provider,
+ UIA_AnnotationTypesAttributeId);
+
+ // start: TextPosition, anchor_id=4, text_offset=5,
+ // annotated_text=more <t>ext
+ // end : TextPosition, anchor_id=4, text_offset=9,
+ // annotated_text=more text<>
+ AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
+ AXPlatformNodeFromNode(heading_text_node));
+ ComPtr<AXPlatformNodeTextRangeProviderWin> range_with_annotations;
+ CreateTextRangeProviderWin(
+ range_with_annotations, owner, tree_data.tree_id,
+ /*start_anchor_id=*/heading_text_node->id(), /*start_offset=*/5,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/heading_text_node->id(), /*end_offset=*/9,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
+
+ base::win::ScopedVariant annotation_types_variant;
+ EXPECT_HRESULT_SUCCEEDED(range_with_annotations->GetAttributeValue(
+ UIA_AnnotationTypesAttributeId, annotation_types_variant.Receive()));
+
+ std::vector<int> expected_annotations = {AnnotationType_SpellingError};
+ EXPECT_UIA_SAFEARRAY_EQ(V_ARRAY(annotation_types_variant.ptr()),
+ expected_annotations);
+ }
+
+ {
+ base::win::ScopedVariant empty_variant;
+ EXPECT_UIA_TEXTATTRIBUTE_EQ(mark_text_range_provider,
+ UIA_AnnotationTypesAttributeId, empty_variant);
+ }
}
TEST_F(AXPlatformNodeTextRangeProviderTest,
@@ -4104,8 +4203,6 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_AnnotationObjectsAttributeId);
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
- UIA_AnnotationTypesAttributeId);
- EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_BeforeParagraphSpacingAttributeId);
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_CapStyleAttributeId);
@@ -4114,8 +4211,6 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_CaretPositionAttributeId);
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
- UIA_HorizontalTextAlignmentAttributeId);
- EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_IndentationFirstLineAttributeId);
EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
UIA_IndentationLeadingAttributeId);
@@ -4179,27 +4274,21 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
Init(initial_state);
- ComPtr<AXPlatformNodeTextRangeProviderWin> text_range_provider_win;
- {
- // Making |owner| AXID:2 so that |TestAXNodeWrapper::BuildAllWrappers|
- // will build the entire subtree, and not only AXID:3 for example.
- AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
- AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 2)));
-
- AXNodePosition::AXPositionInstance range_start =
- AXNodePosition::CreateTextPosition(
- tree_id, /*anchor_id=*/4, /*text_offset=*/0,
- ax::mojom::TextAffinity::kDownstream);
- AXNodePosition::AXPositionInstance range_end =
- AXNodePosition::CreateTextPosition(
- tree_id, /*anchor_id=*/2, /*text_offset=*/17,
- ax::mojom::TextAffinity::kDownstream);
+ // Making |owner| AXID:2 so that |TestAXNodeWrapper::BuildAllWrappers|
+ // will build the entire subtree, and not only AXID:3 for example.
+ AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
+ AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 2)));
- ComPtr<ITextRangeProvider> text_range_provider =
- AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
- owner, std::move(range_start), std::move(range_end));
- text_range_provider->QueryInterface(IID_PPV_ARGS(&text_range_provider_win));
- }
+ // start: TextPosition, anchor_id=4, text_offset=0, annotated_text=<s>ome text
+ // end : TextPosition, anchor_id=2, text_offset=17,
+ // annotated_text=some textmore tex<t>
+ ComPtr<AXPlatformNodeTextRangeProviderWin> text_range_provider_win;
+ CreateTextRangeProviderWin(
+ text_range_provider_win, owner, tree_id,
+ /*start_anchor_id=*/4, /*start_offset=*/0,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/2, /*end_offset=*/17,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
ASSERT_TRUE(GetStart(text_range_provider_win.Get())->IsTextPosition());
ASSERT_EQ(4, GetStart(text_range_provider_win.Get())->anchor_id());
@@ -5212,27 +5301,20 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
Init(initial_state);
- ComPtr<AXPlatformNodeTextRangeProviderWin> ignored_range_win;
- {
- // Making |owner| AXID:1 so that |TestAXNodeWrapper::BuildAllWrappers|
- // will build the entire tree.
- AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
- AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1)));
-
- AXNodePosition::AXPositionInstance range_start =
- AXNodePosition::CreateTextPosition(
- tree_id, /*anchor_id=*/3, /*text_offset=*/1,
- ax::mojom::TextAffinity::kDownstream);
- AXNodePosition::AXPositionInstance range_end =
- AXNodePosition::CreateTextPosition(
- tree_id, /*anchor_id=*/3, /*text_offset=*/6,
- ax::mojom::TextAffinity::kDownstream);
+ // Making |owner| AXID:1 so that |TestAXNodeWrapper::BuildAllWrappers|
+ // will build the entire tree.
+ AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
+ AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1)));
- ComPtr<ITextRangeProvider> text_range_provider =
- AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
- owner, std::move(range_start), std::move(range_end));
- text_range_provider->QueryInterface(IID_PPV_ARGS(&ignored_range_win));
- }
+ // start: TextPosition, anchor_id=3, text_offset=1, annotated_text=i<g>nored
+ // end : TextPosition, anchor_id=3, text_offset=6, annotated_text=ignore<d>
+ ComPtr<AXPlatformNodeTextRangeProviderWin> ignored_range_win;
+ CreateTextRangeProviderWin(
+ ignored_range_win, owner, tree_id,
+ /*start_anchor_id=*/3, /*start_offset=*/1,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/3, /*end_offset=*/6,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
EXPECT_TRUE(GetStart(ignored_range_win.Get())->IsIgnored());
EXPECT_TRUE(GetEnd(ignored_range_win.Get())->IsIgnored());
@@ -5317,26 +5399,18 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1)));
- // TextPosition, anchor_id=2, text_offset=6, annotated_text=before<>
- AXNodePosition::AXPositionInstance range_start =
- AXNodePosition::CreateTextPosition(tree_id, /*anchor_id=*/2,
- /*text_offset=*/6,
- ax::mojom::TextAffinity::kDownstream);
-
- // TextPosition, anchor_id=5, text_offset=0, annotated_text=<a>fter
- AXNodePosition::AXPositionInstance range_end =
- AXNodePosition::CreateTextPosition(tree_id, /*anchor_id=*/5,
- /*text_offset=*/0,
- ax::mojom::TextAffinity::kDownstream);
-
- ComPtr<AXPlatformNodeTextRangeProviderWin> range_span_ignored_nodes;
// Text range before NormalizeTextRange()
// |before<>||ignored1||ignored2||<a>fter|
// |-----------------------|
- ComPtr<ITextRangeProvider> text_range_provider =
- AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
- owner, std::move(range_start), std::move(range_end));
- text_range_provider->QueryInterface(IID_PPV_ARGS(&range_span_ignored_nodes));
+ // start: TextPosition, anchor_id=2, text_offset=6, annotated_text=before<>
+ // end : TextPosition, anchor_id=5, text_offset=0, annotated_text=<a>fter
+ ComPtr<AXPlatformNodeTextRangeProviderWin> range_span_ignored_nodes;
+ CreateTextRangeProviderWin(
+ range_span_ignored_nodes, owner, tree_id,
+ /*start_anchor_id=*/2, /*start_offset=*/6,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/5, /*end_offset=*/0,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
// Text range after NormalizeTextRange()
// |before||ignored1||ignored2||<a>fter|
@@ -5424,23 +5498,15 @@ TEST_F(AXPlatformNodeTextRangeProviderTest,
AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
AXPlatformNodeFromNode(GetNodeFromTree(tree_data.tree_id, 1)));
- // TextPosition, anchor_id=3, text_offset=1, annotated_text=/xFFFC<>
- AXNodePosition::AXPositionInstance range_start =
- AXNodePosition::CreateTextPosition(tree_data.tree_id, /*anchor_id=*/3,
- /*text_offset=*/1,
- ax::mojom::TextAffinity::kDownstream);
-
- // TextPosition, anchor_id=7, text_offset=0, annotated_text=<p>i
- AXNodePosition::AXPositionInstance range_end =
- AXNodePosition::CreateTextPosition(tree_data.tree_id, /*anchor_id=*/7,
- /*text_offset=*/0,
- ax::mojom::TextAffinity::kDownstream);
-
+ // start: TextPosition, anchor_id=3, text_offset=1, annotated_text=/xFFFC<>
+ // end : TextPosition, anchor_id=7, text_offset=0, annotated_text=<p>i
ComPtr<AXPlatformNodeTextRangeProviderWin> range;
- ComPtr<ITextRangeProvider> text_range_provider =
- AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
- owner, std::move(range_start), std::move(range_end));
- text_range_provider->QueryInterface(IID_PPV_ARGS(&range));
+ CreateTextRangeProviderWin(
+ range, owner, tree_data.tree_id,
+ /*start_anchor_id=*/3, /*start_offset=*/1,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/7, /*end_offset=*/0,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
NormalizeTextRange(range.Get());
EXPECT_EQ(*GetStart(range.Get()), *GetEnd(range.Get()));
@@ -5485,21 +5551,15 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestValidateStartAndEnd) {
AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>(
AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1)));
- // TextPosition, anchor_id=2, text_offset=9, annotated_text=some text<>
- AXNodePosition::AXPositionInstance range_start =
- AXNodePosition::CreateTextPosition(tree_id, /*anchor_id=*/1,
- /*text_offset=*/0,
- ax::mojom::TextAffinity::kDownstream);
-
- // TextPosition, anchor_id=2, text_offset=9, annotated_text=some text<>
- AXNodePosition::AXPositionInstance range_end =
- AXNodePosition::CreateTextPosition(tree_id, /*anchor_id=*/3,
- /*text_offset=*/9,
- ax::mojom::TextAffinity::kDownstream);
-
- ComPtr<ITextRangeProvider> text_range_provider =
- AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
- owner, std::move(range_start), std::move(range_end));
+ // start: TextPosition, anchor_id=1, text_offset=0, annotated_text=<s>ome text
+ // end : TextPosition, anchor_id=3, text_offset=9, annotated_text=more text<>
+ ComPtr<AXPlatformNodeTextRangeProviderWin> text_range_provider;
+ CreateTextRangeProviderWin(
+ text_range_provider, owner, tree_id,
+ /*start_anchor_id=*/1, /*start_offset=*/0,
+ /*start_affinity*/ ax::mojom::TextAffinity::kDownstream,
+ /*end_anchor_id=*/3, /*end_offset=*/9,
+ /*end_affinity*/ ax::mojom::TextAffinity::kDownstream);
// Since the end of the range is at MaxTextOffset, moving it by 1 character
// should have an expected_count of 0.
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc
index d0fbf20bcf0..64e997a33d6 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc
@@ -18,6 +18,7 @@
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_functions.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -27,6 +28,7 @@
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_safearray.h"
#include "base/win/scoped_variant.h"
+#include "base/win/variant_vector.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -51,7 +53,6 @@
#include "ui/base/win/atl_module.h"
#include "ui/display/win/screen_win.h"
#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
//
// Macros to use at the top of any AXPlatformNodeWin function that implements
@@ -515,7 +516,7 @@ gfx::Vector2d AXPlatformNodeWin::CalculateUIAScrollPoint(
const float scale_factor =
display::win::ScreenWin::GetScaleFactorForHWND(hwnd);
const int small_change =
- gfx::ToRoundedInt(kSmallScrollIncrement * scale_factor);
+ base::ClampRound(kSmallScrollIncrement * scale_factor);
const int x_min = GetIntAttribute(ax::mojom::IntAttribute::kScrollXMin);
const int x_max = GetIntAttribute(ax::mojom::IntAttribute::kScrollXMax);
@@ -1681,6 +1682,12 @@ IFACEMETHODIMP AXPlatformNodeWin::setSelectionRanges(LONG nRanges,
if (!anchor_node || !focus_node)
return E_INVALIDARG;
+ // Blink only supports selections within a single tree.
+ if (anchor_node->GetDelegate()->GetTreeData().tree_id !=
+ focus_node->GetDelegate()->GetTreeData().tree_id) {
+ return E_INVALIDARG;
+ }
+
if (ranges->anchorOffset < 0 || ranges->activeOffset < 0)
return E_INVALIDARG;
@@ -1988,9 +1995,9 @@ IFACEMETHODIMP AXPlatformNodeWin::SetScrollPercent(double horizontal_percent,
const double y_min = GetIntAttribute(ax::mojom::IntAttribute::kScrollYMin);
const double y_max = GetIntAttribute(ax::mojom::IntAttribute::kScrollYMax);
const int x =
- gfx::ToRoundedInt(horizontal_percent / 100.0 * (x_max - x_min) + x_min);
+ base::ClampRound(horizontal_percent / 100.0 * (x_max - x_min) + x_min);
const int y =
- gfx::ToRoundedInt(vertical_percent / 100.0 * (y_max - y_min) + y_min);
+ base::ClampRound(vertical_percent / 100.0 * (y_max - y_min) + y_min);
const gfx::Point scroll_to(x, y);
AXActionData action_data;
@@ -2077,11 +2084,8 @@ IFACEMETHODIMP AXPlatformNodeWin::get_VerticalViewSize(double* result) {
HRESULT AXPlatformNodeWin::ISelectionItemProviderSetSelected(
bool selected) const {
UIA_VALIDATE_CALL();
- int restriction;
- if (GetIntAttribute(ax::mojom::IntAttribute::kRestriction, &restriction)) {
- if (restriction == static_cast<int>(ax::mojom::Restriction::kDisabled))
- return UIA_E_ELEMENTNOTENABLED;
- }
+ if (GetData().GetRestriction() == ax::mojom::Restriction::kDisabled)
+ return UIA_E_ELEMENTNOTENABLED;
// The platform implements selection follows focus for single-selection
// container elements. Focus action can change a node's accessibility selected
@@ -3578,7 +3582,7 @@ IFACEMETHODIMP AXPlatformNodeWin::get_offsetAtPoint(
const AXPlatformNodeWin* hit_child = static_cast<AXPlatformNodeWin*>(
FromNativeViewAccessible(GetDelegate()->HitTestSync(x, y)));
- if (!hit_child || !hit_child->IsTextOnlyObject()) {
+ if (!hit_child || !hit_child->IsText()) {
return S_FALSE;
}
@@ -4077,9 +4081,14 @@ HRESULT AXPlatformNodeWin::GetPropertyValueImpl(PROPERTYID property_id,
result->lVal = ComputeUIAControlType();
break;
- case UIA_CulturePropertyId:
- return GetCultureAttributeAsVariant(result);
+ case UIA_CulturePropertyId: {
+ base::Optional<LCID> lcid = GetCultureAttributeAsLCID();
+ if (!lcid)
+ return E_FAIL;
+ result->vt = VT_I4;
+ result->lVal = lcid.value();
break;
+ }
case UIA_DescribedByPropertyId:
result->vt = VT_ARRAY | VT_UNKNOWN;
@@ -4596,49 +4605,55 @@ STDMETHODIMP AXPlatformNodeWin::InternalQueryInterface(
object);
}
-HRESULT AXPlatformNodeWin::GetTextAttributeValue(TEXTATTRIBUTEID attribute_id,
- VARIANT* result) {
+HRESULT AXPlatformNodeWin::GetTextAttributeValue(
+ TEXTATTRIBUTEID attribute_id,
+ const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ base::win::VariantVector* result) {
+ DCHECK(!start_offset || start_offset.value() >= 0);
+ DCHECK(!end_offset || end_offset.value() >= 0);
+
switch (attribute_id) {
+ case UIA_AnnotationTypesAttributeId:
+ return GetAnnotationTypesAttribute(start_offset, end_offset, result);
case UIA_BackgroundColorAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) =
- GetIntAttributeAsCOLORREF(ax::mojom::IntAttribute::kBackgroundColor);
+ result->Insert<VT_I4>(
+ GetIntAttributeAsCOLORREF(ax::mojom::IntAttribute::kBackgroundColor));
break;
case UIA_BulletStyleAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = ComputeUIABulletStyle();
+ result->Insert<VT_I4>(ComputeUIABulletStyle());
break;
- case UIA_CultureAttributeId:
- return GetCultureAttributeAsVariant(result);
+ case UIA_CultureAttributeId: {
+ base::Optional<LCID> lcid = GetCultureAttributeAsLCID();
+ if (!lcid)
+ return E_FAIL;
+ result->Insert<VT_I4>(lcid.value());
+ break;
+ }
case UIA_FontNameAttributeId:
- V_VT(result) = VT_BSTR;
- V_BSTR(result) = GetFontNameAttributeAsBSTR();
+ result->Insert<VT_BSTR>(GetFontNameAttributeAsBSTR());
break;
case UIA_FontSizeAttributeId: {
base::Optional<float> font_size_in_points = GetFontSizeInPoints();
if (font_size_in_points) {
- V_VT(result) = VT_R8;
- V_R8(result) = *font_size_in_points;
+ result->Insert<VT_R8>(*font_size_in_points);
}
break;
}
case UIA_FontWeightAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = GetFloatAttribute(ax::mojom::FloatAttribute::kFontWeight);
+ result->Insert<VT_I4>(
+ GetFloatAttribute(ax::mojom::FloatAttribute::kFontWeight));
break;
case UIA_ForegroundColorAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = GetIntAttributeAsCOLORREF(ax::mojom::IntAttribute::kColor);
+ result->Insert<VT_I4>(
+ GetIntAttributeAsCOLORREF(ax::mojom::IntAttribute::kColor));
break;
case UIA_IsHiddenAttributeId:
- V_VT(result) = VT_BOOL;
- V_BOOL(result) = IsInvisibleOrIgnored() ? VARIANT_TRUE : VARIANT_FALSE;
+ result->Insert<VT_BOOL>(IsInvisibleOrIgnored());
break;
case UIA_IsItalicAttributeId:
- V_VT(result) = VT_BOOL;
- V_BOOL(result) = GetData().HasTextStyle(ax::mojom::TextStyle::kItalic)
- ? VARIANT_TRUE
- : VARIANT_FALSE;
+ result->Insert<VT_BOOL>(
+ GetData().HasTextStyle(ax::mojom::TextStyle::kItalic));
break;
case UIA_IsReadOnlyAttributeId:
// Placeholder text should return the enclosing element's read-only value.
@@ -4646,74 +4661,102 @@ HRESULT AXPlatformNodeWin::GetTextAttributeValue(TEXTATTRIBUTEID attribute_id,
AXPlatformNodeWin* parent_platform_node =
static_cast<AXPlatformNodeWin*>(
FromNativeViewAccessible(GetParent()));
- return parent_platform_node->GetTextAttributeValue(attribute_id,
- result);
+ return parent_platform_node->GetTextAttributeValue(
+ attribute_id, start_offset, end_offset, result);
}
- V_VT(result) = VT_BOOL;
- V_BOOL(result) =
- GetData().IsReadOnlyOrDisabled() ? VARIANT_TRUE : VARIANT_FALSE;
+ result->Insert<VT_BOOL>(GetData().IsReadOnlyOrDisabled());
break;
case UIA_IsSubscriptAttributeId:
- V_VT(result) = VT_BOOL;
- V_BOOL(result) =
- (GetData().GetTextPosition() == ax::mojom::TextPosition::kSubscript)
- ? VARIANT_TRUE
- : VARIANT_FALSE;
+ result->Insert<VT_BOOL>(GetData().GetTextPosition() ==
+ ax::mojom::TextPosition::kSubscript);
break;
case UIA_IsSuperscriptAttributeId:
- V_VT(result) = VT_BOOL;
- V_BOOL(result) =
- (GetData().GetTextPosition() == ax::mojom::TextPosition::kSuperscript)
- ? VARIANT_TRUE
- : VARIANT_FALSE;
+ result->Insert<VT_BOOL>(GetData().GetTextPosition() ==
+ ax::mojom::TextPosition::kSuperscript);
break;
case UIA_OverlineStyleAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = GetUIATextDecorationStyle(
- ax::mojom::IntAttribute::kTextOverlineStyle);
+ result->Insert<VT_I4>(GetUIATextDecorationStyle(
+ ax::mojom::IntAttribute::kTextOverlineStyle));
break;
case UIA_StrikethroughStyleAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = GetUIATextDecorationStyle(
- ax::mojom::IntAttribute::kTextStrikethroughStyle);
+ result->Insert<VT_I4>(GetUIATextDecorationStyle(
+ ax::mojom::IntAttribute::kTextStrikethroughStyle));
break;
case UIA_StyleNameAttributeId:
- V_VT(result) = VT_BSTR;
- V_BSTR(result) = GetStyleNameAttributeAsBSTR();
+ result->Insert<VT_BSTR>(GetStyleNameAttributeAsBSTR());
break;
case UIA_StyleIdAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = ComputeUIAStyleId();
+ result->Insert<VT_I4>(ComputeUIAStyleId());
+ break;
+ case UIA_HorizontalTextAlignmentAttributeId: {
+ base::Optional<HorizontalTextAlignment> horizontal_text_alignment =
+ AXTextAlignToUIAHorizontalTextAlignment(GetData().GetTextAlign());
+ if (horizontal_text_alignment)
+ result->Insert<VT_I4>(*horizontal_text_alignment);
break;
+ }
case UIA_UnderlineStyleAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) = GetUIATextDecorationStyle(
- ax::mojom::IntAttribute::kTextUnderlineStyle);
+ result->Insert<VT_I4>(GetUIATextDecorationStyle(
+ ax::mojom::IntAttribute::kTextUnderlineStyle));
break;
case UIA_TextFlowDirectionsAttributeId:
- V_VT(result) = VT_I4;
- V_I4(result) =
- TextDirectionToFlowDirections(GetData().GetTextDirection());
- break;
- default:
- V_VT(result) = VT_UNKNOWN;
- return ::UiaGetReservedNotSupportedValue(&V_UNKNOWN(result));
+ result->Insert<VT_I4>(
+ TextDirectionToFlowDirections(GetData().GetTextDirection()));
+ break;
+ default: {
+ Microsoft::WRL::ComPtr<IUnknown> not_supported_value;
+ HRESULT hr = ::UiaGetReservedNotSupportedValue(&not_supported_value);
+ if (SUCCEEDED(hr))
+ result->Insert<VT_UNKNOWN>(not_supported_value.Get());
+ return hr;
+ } break;
}
return S_OK;
}
-HRESULT AXPlatformNodeWin::GetCultureAttributeAsVariant(VARIANT* result) const {
+HRESULT AXPlatformNodeWin::GetAnnotationTypesAttribute(
+ const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ base::win::VariantVector* result) {
+ base::win::VariantVector variant_vector;
+
+ MarkerTypeRangeResult grammar_result = MarkerTypeRangeResult::kNone;
+ MarkerTypeRangeResult spelling_result = MarkerTypeRangeResult::kNone;
+
+ if (IsText() || IsPlainTextField()) {
+ grammar_result = GetMarkerTypeFromRange(start_offset, end_offset,
+ ax::mojom::MarkerType::kGrammar);
+ spelling_result = GetMarkerTypeFromRange(start_offset, end_offset,
+ ax::mojom::MarkerType::kSpelling);
+ }
+
+ if (grammar_result == MarkerTypeRangeResult::kMixed ||
+ spelling_result == MarkerTypeRangeResult::kMixed) {
+ Microsoft::WRL::ComPtr<IUnknown> mixed_attribute_value;
+ HRESULT hr = ::UiaGetReservedMixedAttributeValue(&mixed_attribute_value);
+ if (SUCCEEDED(hr))
+ result->Insert<VT_UNKNOWN>(mixed_attribute_value.Get());
+ return hr;
+ }
+
+ if (spelling_result == MarkerTypeRangeResult::kMatch)
+ result->Insert<VT_I4>(AnnotationType_SpellingError);
+ if (grammar_result == MarkerTypeRangeResult::kMatch)
+ result->Insert<VT_I4>(AnnotationType_GrammarError);
+
+ return S_OK;
+}
+
+base::Optional<LCID> AXPlatformNodeWin::GetCultureAttributeAsLCID() const {
const base::string16 language =
GetInheritedString16Attribute(ax::mojom::StringAttribute::kLanguage);
const LCID lcid =
LocaleNameToLCID(language.c_str(), LOCALE_ALLOW_NEUTRAL_NAMES);
if (!lcid)
- return E_FAIL;
+ return base::nullopt;
- V_VT(result) = VT_I4;
- V_I4(result) = lcid;
- return S_OK;
+ return lcid;
}
COLORREF AXPlatformNodeWin::GetIntAttributeAsCOLORREF(
@@ -4777,6 +4820,24 @@ LONG AXPlatformNodeWin::ComputeUIAStyleId() const {
}
// static
+base::Optional<HorizontalTextAlignment>
+AXPlatformNodeWin::AXTextAlignToUIAHorizontalTextAlignment(
+ ax::mojom::TextAlign text_align) {
+ switch (text_align) {
+ case ax::mojom::TextAlign::kNone:
+ return base::nullopt;
+ case ax::mojom::TextAlign::kLeft:
+ return HorizontalTextAlignment_Left;
+ case ax::mojom::TextAlign::kRight:
+ return HorizontalTextAlignment_Right;
+ case ax::mojom::TextAlign::kCenter:
+ return HorizontalTextAlignment_Centered;
+ case ax::mojom::TextAlign::kJustify:
+ return HorizontalTextAlignment_Justified;
+ }
+}
+
+// static
LONG AXPlatformNodeWin::AXHierarchicalLevelToUIAStyleId(
int32_t hierarchical_level) {
switch (hierarchical_level) {
@@ -4824,21 +4885,129 @@ LONG AXPlatformNodeWin::AXListStyleToUIAStyleId(
// static
FlowDirections AXPlatformNodeWin::TextDirectionToFlowDirections(
- ax::mojom::TextDirection text_direction) {
+ ax::mojom::WritingDirection text_direction) {
switch (text_direction) {
- case ax::mojom::TextDirection::kNone:
+ case ax::mojom::WritingDirection::kNone:
return FlowDirections::FlowDirections_Default;
- case ax::mojom::TextDirection::kLtr:
+ case ax::mojom::WritingDirection::kLtr:
return FlowDirections::FlowDirections_Default;
- case ax::mojom::TextDirection::kRtl:
+ case ax::mojom::WritingDirection::kRtl:
return FlowDirections::FlowDirections_RightToLeft;
- case ax::mojom::TextDirection::kTtb:
+ case ax::mojom::WritingDirection::kTtb:
return FlowDirections::FlowDirections_Vertical;
- case ax::mojom::TextDirection::kBtt:
+ case ax::mojom::WritingDirection::kBtt:
return FlowDirections::FlowDirections_BottomToTop;
}
}
+// static
+void AXPlatformNodeWin::AggregateRangesForMarkerType(
+ AXPlatformNodeBase* node,
+ ax::mojom::MarkerType marker_type,
+ int offset_ranges_amount,
+ std::vector<std::pair<int, int>>* ranges) {
+ DCHECK(node->IsText());
+ const std::vector<int32_t>& marker_types =
+ node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes);
+ const std::vector<int>& marker_starts =
+ node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts);
+ const std::vector<int>& marker_ends =
+ node->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds);
+
+ for (size_t i = 0; i < marker_types.size(); ++i) {
+ if (static_cast<ax::mojom::MarkerType>(marker_types[i]) != marker_type)
+ continue;
+
+ const int marker_start = marker_starts[i] + offset_ranges_amount;
+ const int marker_end = marker_ends[i] + offset_ranges_amount;
+ ranges->emplace_back(std::make_pair(marker_start, marker_end));
+ }
+}
+
+AXPlatformNodeWin::MarkerTypeRangeResult
+AXPlatformNodeWin::GetMarkerTypeFromRange(
+ const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ ax::mojom::MarkerType marker_type) {
+ DCHECK(IsText() || IsPlainTextField());
+ std::vector<std::pair<int, int>> relevant_ranges;
+
+ if (IsText()) {
+ AggregateRangesForMarkerType(this, marker_type, /*offset_ranges_amount=*/0,
+ &relevant_ranges);
+ } else if (IsPlainTextField()) {
+ int offset_ranges_amount = 0;
+ for (AXPlatformNodeBase* static_text = GetFirstTextOnlyDescendant();
+ static_text; static_text = static_text->GetNextSibling()) {
+ const int child_offset_ranges_amount = offset_ranges_amount;
+ if (start_offset || end_offset) {
+ // Break if the current node is after the desired |end_offset|.
+ if (end_offset && child_offset_ranges_amount > end_offset.value())
+ break;
+
+ // Skip over nodes preceding the desired |start_offset|.
+ offset_ranges_amount += static_text->GetHypertext().length();
+ if (start_offset && offset_ranges_amount < start_offset.value())
+ continue;
+ }
+
+ AggregateRangesForMarkerType(static_text, marker_type,
+ child_offset_ranges_amount,
+ &relevant_ranges);
+ }
+ }
+
+ // Sort the ranges by their start offset.
+ const auto sort_ranges_by_start_offset = [](const std::pair<int, int>& a,
+ const std::pair<int, int>& b) {
+ return a.first < b.first;
+ };
+ std::sort(relevant_ranges.begin(), relevant_ranges.end(),
+ sort_ranges_by_start_offset);
+
+ // Validate that the desired range has a contiguous MarkerType.
+ base::Optional<std::pair<int, int>> contiguous_range;
+ for (const std::pair<int, int>& range : relevant_ranges) {
+ if (end_offset && range.first > end_offset.value())
+ break;
+ if (start_offset && range.second < start_offset.value())
+ continue;
+
+ if (!contiguous_range) {
+ contiguous_range = range;
+ continue;
+ }
+
+ // If there is a gap, then the range must be mixed.
+ if ((range.first - contiguous_range->second) > 1)
+ return MarkerTypeRangeResult::kMixed;
+
+ // Expand the range if possible.
+ contiguous_range->second = std::max(contiguous_range->second, range.second);
+ }
+
+ // The desired range does not overlap with |marker_type|.
+ if (!contiguous_range)
+ return MarkerTypeRangeResult::kNone;
+
+ // If there is a partial overlap, then the desired range must be mixed.
+ // 1. The |start_offset| is not specified, treat it as offset 0.
+ if (!start_offset && contiguous_range->first > 0)
+ return MarkerTypeRangeResult::kMixed;
+ // 2. The |end_offset| is not specified, treat it as max text offset.
+ if (!end_offset && size_t{contiguous_range->second} < GetHypertext().length())
+ return MarkerTypeRangeResult::kMixed;
+ // 3. The |start_offset| is specified, but is before the first matching range.
+ if (start_offset && start_offset.value() < contiguous_range->first)
+ return MarkerTypeRangeResult::kMixed;
+ // 4. The |end_offset| is specified, but is after the last matching range.
+ if (end_offset && end_offset.value() > contiguous_range->second)
+ return MarkerTypeRangeResult::kMixed;
+
+ // The desired range is a complete match for |marker_type|.
+ return MarkerTypeRangeResult::kMatch;
+}
+
// IRawElementProviderSimple support methods.
bool AXPlatformNodeWin::IsPatternProviderSupported(PATTERNID pattern_id) {
@@ -5141,18 +5310,14 @@ int AXPlatformNodeWin::MSAARole() {
return ROLE_SYSTEM_EQUATION;
case ax::mojom::Role::kMenu:
- case ax::mojom::Role::kMenuButton:
return ROLE_SYSTEM_MENUPOPUP;
case ax::mojom::Role::kMenuBar:
return ROLE_SYSTEM_MENUBAR;
+ case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
- return ROLE_SYSTEM_MENUITEM;
-
case ax::mojom::Role::kMenuItemCheckBox:
- return ROLE_SYSTEM_MENUITEM;
-
case ax::mojom::Role::kMenuItemRadio:
return ROLE_SYSTEM_MENUITEM;
@@ -5973,12 +6138,12 @@ base::string16 AXPlatformNodeWin::UIAAriaRole() {
return L"group";
case ax::mojom::Role::kMenu:
- case ax::mojom::Role::kMenuButton:
return L"menu";
case ax::mojom::Role::kMenuBar:
return L"menubar";
+ case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
return L"menuitem";
@@ -6640,12 +6805,12 @@ LONG AXPlatformNodeWin::ComputeUIAControlType() { // NOLINT(runtime/int)
return UIA_GroupControlTypeId;
case ax::mojom::Role::kMenu:
- case ax::mojom::Role::kMenuButton:
return UIA_MenuControlTypeId;
case ax::mojom::Role::kMenuBar:
return UIA_MenuBarControlTypeId;
+ case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
return UIA_MenuItemControlTypeId;
@@ -6928,7 +7093,7 @@ bool AXPlatformNodeWin::IsUIAControl() const {
if (IsInvisibleOrIgnored())
return false;
- if (IsTextOnlyObject()) {
+ if (IsText()) {
// A text leaf can be a UIAControl, but text inside of a heading, link,
// button, etc. where the role allows the name to be generated from the
// content is not. We want to avoid reading out a button, moving to the
@@ -7110,7 +7275,7 @@ bool AXPlatformNodeWin::ShouldHideChildrenForUIA() const {
// Links with a single text-only child should hide their subtree.
if (GetChildCount() == 1) {
AXPlatformNodeBase* only_child = GetFirstChild();
- return only_child && only_child->IsTextOnlyObject();
+ return only_child && only_child->IsText();
}
return false;
case ax::mojom::Role::kPdfActionableHighlight:
@@ -7194,10 +7359,9 @@ int AXPlatformNodeWin::MSAAState() const {
if (ShouldNodeHaveFocusableState(data))
msaa_state |= STATE_SYSTEM_FOCUSABLE;
- if (data.HasIntAttribute(ax::mojom::IntAttribute::kHasPopup) ||
- data.HasState(ax::mojom::State::kAutofillAvailable)) {
+ // Built-in autofill and autocomplete wil also set has popup.
+ if (data.HasIntAttribute(ax::mojom::IntAttribute::kHasPopup))
msaa_state |= STATE_SYSTEM_HASPOPUP;
- }
// TODO(dougt) unhandled ux::ax::mojom::State::kHorizontal
@@ -7233,7 +7397,7 @@ int AXPlatformNodeWin::MSAAState() const {
// TODO(dougt) unhandled ux::ax::mojom::State::kRequired
// TODO(dougt) unhandled ux::ax::mojom::State::kRichlyEditable
- if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+ if (data.IsSelectable())
msaa_state |= STATE_SYSTEM_SELECTABLE;
if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
@@ -7433,6 +7597,10 @@ base::Optional<PROPERTYID> AXPlatformNodeWin::MojoEventToUIAProperty(
case ax::mojom::Event::kRowCollapsed:
case ax::mojom::Event::kRowExpanded:
return UIA_ExpandCollapseExpandCollapseStatePropertyId;
+ case ax::mojom::Event::kSelection:
+ case ax::mojom::Event::kSelectionAdd:
+ case ax::mojom::Event::kSelectionRemove:
+ return UIA_SelectionItemIsSelectedPropertyId;
default:
return base::nullopt;
}
@@ -7838,7 +8006,7 @@ AXPlatformNodeWin::GetPatternProviderFactoryMethod(PATTERNID pattern_id) {
case UIA_TextEditPatternId:
case UIA_TextPatternId:
- if (IsTextOnlyObject() || IsDocument() ||
+ if (IsText() || IsDocument() ||
HasBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot)) {
return &AXPlatformNodeTextProviderWin::CreateIUnknown;
}
@@ -7926,6 +8094,17 @@ AXPlatformNodeWin* AXPlatformNodeWin::GetLowestAccessibleElement() {
return nullptr;
}
+AXPlatformNodeWin* AXPlatformNodeWin::GetFirstTextOnlyDescendant() {
+ for (auto* child = static_cast<AXPlatformNodeWin*>(GetFirstChild()); child;
+ child = static_cast<AXPlatformNodeWin*>(child->GetNextSibling())) {
+ if (child->IsText())
+ return child;
+ if (AXPlatformNodeWin* descendant = child->GetFirstTextOnlyDescendant())
+ return descendant;
+ }
+ return nullptr;
+}
+
void AXPlatformNodeWin::SanitizeTextAttributeValue(const std::string& input,
std::string* output) const {
SanitizeStringAttributeForIA2(input, output);
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.h b/chromium/ui/accessibility/platform/ax_platform_node_win.h
index 9f4367393bc..e410e60a31a 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_win.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_win.h
@@ -311,6 +311,12 @@ enum {
return E_INVALIDARG; \
*arg = {};
+namespace base {
+namespace win {
+class VariantVector;
+} // namespace win
+} // namespace base
+
namespace ui {
class AXPlatformNodeWin;
@@ -1060,8 +1066,13 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2"))
REFIID riid,
void** object);
- // Support method for ITextRangeProvider::GetAttributeValue
- HRESULT GetTextAttributeValue(TEXTATTRIBUTEID attribute_id, VARIANT* result);
+ // Support method for ITextRangeProvider::GetAttributeValue.
+ // If either |start_offset| or |end_offset| are not provided then the
+ // endpoint is treated as the start or end of the node respectively.
+ HRESULT GetTextAttributeValue(TEXTATTRIBUTEID attribute_id,
+ const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ base::win::VariantVector* result);
// IRawElementProviderSimple support method.
bool IsPatternProviderSupported(PATTERNID pattern_id);
@@ -1096,6 +1107,10 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2"))
// Returns the parent node that makes this node inaccessible.
AXPlatformNodeWin* GetLowestAccessibleElement();
+ // Returns the first |IsTextOnlyObject| descendant using
+ // depth-first pre-order traversal.
+ AXPlatformNodeWin* GetFirstTextOnlyDescendant();
+
// Convert a mojo event to an MSAA event. Exposed for testing.
static base::Optional<DWORD> MojoEventToMSAAEvent(ax::mojom::Event event);
@@ -1339,20 +1354,53 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2"))
// Getters for UIA GetTextAttributeValue
//
- // Lookup the LCID for the language this node is using
- HRESULT GetCultureAttributeAsVariant(VARIANT* result) const;
+ // Computes the AnnotationTypes Attribute for the current node.
+ HRESULT GetAnnotationTypesAttribute(const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ base::win::VariantVector* result);
+ // Lookup the LCID for the language this node is using.
+ // Returns base::nullopt if there was an error.
+ base::Optional<LCID> GetCultureAttributeAsLCID() const;
// Converts an int attribute to a COLORREF
COLORREF GetIntAttributeAsCOLORREF(ax::mojom::IntAttribute attribute) const;
// Converts the ListStyle to UIA BulletStyle
BulletStyle ComputeUIABulletStyle() const;
// Helper to get the UIA StyleId enumeration for this node
LONG ComputeUIAStyleId() const;
+ // Convert mojom TextAlign to UIA HorizontalTextAlignment enumeration
+ static base::Optional<HorizontalTextAlignment>
+ AXTextAlignToUIAHorizontalTextAlignment(ax::mojom::TextAlign text_align);
// Converts IntAttribute::kHierarchicalLevel to UIA StyleId enumeration
static LONG AXHierarchicalLevelToUIAStyleId(int32_t hierarchical_level);
// Converts a ListStyle to UIA StyleId enumeration
static LONG AXListStyleToUIAStyleId(ax::mojom::ListStyle list_style);
// Convert mojom TextDirection to UIA FlowDirections enumeration
- static FlowDirections TextDirectionToFlowDirections(ax::mojom::TextDirection);
+ static FlowDirections TextDirectionToFlowDirections(
+ ax::mojom::WritingDirection);
+
+ // Helper method for |GetMarkerTypeFromRange| which aggregates all
+ // of the ranges for |marker_type| attached to |node|.
+ static void AggregateRangesForMarkerType(
+ AXPlatformNodeBase* node,
+ ax::mojom::MarkerType marker_type,
+ int offset_ranges_amount,
+ std::vector<std::pair<int, int>>* ranges);
+
+ enum class MarkerTypeRangeResult {
+ // The MarkerType does not overlap the range.
+ kNone,
+ // The MarkerType overlaps the entire range.
+ kMatch,
+ // The MarkerType partially overlaps the range.
+ kMixed,
+ };
+
+ // Determine if a text range overlaps a |marker_type|, and whether
+ // the overlap is a partial or or complete match.
+ MarkerTypeRangeResult GetMarkerTypeFromRange(
+ const base::Optional<int>& start_offset,
+ const base::Optional<int>& end_offset,
+ ax::mojom::MarkerType marker_type);
bool IsAncestorComboBox();
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index c7fce2de057..9a23f291230 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -223,6 +223,47 @@ ScopedVariant SELF(CHILDID_SELF);
testing::UnorderedElementsAreArray(expected_property_values)); \
}
+MockIRawElementProviderSimple::MockIRawElementProviderSimple() = default;
+MockIRawElementProviderSimple::~MockIRawElementProviderSimple() = default;
+
+HRESULT
+MockIRawElementProviderSimple::CreateMockIRawElementProviderSimple(
+ IRawElementProviderSimple** provider) {
+ CComObject<MockIRawElementProviderSimple>* raw_element_provider = nullptr;
+ HRESULT hr = CComObject<MockIRawElementProviderSimple>::CreateInstance(
+ &raw_element_provider);
+ if (SUCCEEDED(hr)) {
+ *provider = raw_element_provider;
+ }
+
+ return hr;
+}
+
+//
+// IRawElementProviderSimple methods.
+//
+IFACEMETHODIMP MockIRawElementProviderSimple::GetPatternProvider(
+ PATTERNID pattern_id,
+ IUnknown** result) {
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP MockIRawElementProviderSimple::GetPropertyValue(
+ PROPERTYID property_id,
+ VARIANT* result) {
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP
+MockIRawElementProviderSimple::get_ProviderOptions(enum ProviderOptions* ret) {
+ return E_NOTIMPL;
+}
+
+IFACEMETHODIMP MockIRawElementProviderSimple::get_HostRawElementProvider(
+ IRawElementProviderSimple** provider) {
+ return E_NOTIMPL;
+}
+
AXPlatformNodeWinTest::AXPlatformNodeWinTest() {
scoped_feature_list_.InitAndEnableFeature(features::kIChromeAccessible);
}
@@ -716,8 +757,7 @@ TEST_F(AXPlatformNodeWinTest,
// Loop through the selections and make sure we have the right ones.
ComPtr<IEnumVARIANT> accessibles;
ASSERT_HRESULT_SUCCEEDED(
- V_UNKNOWN(selection.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessibles.GetAddressOf())));
+ V_UNKNOWN(selection.ptr())->QueryInterface(IID_PPV_ARGS(&accessibles)));
ULONG retrieved_count;
// Check out the first selected item.
@@ -728,8 +768,7 @@ TEST_F(AXPlatformNodeWinTest,
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedBstr name;
EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive()));
EXPECT_STREQ(L"Name1", name.Get());
@@ -743,8 +782,7 @@ TEST_F(AXPlatformNodeWinTest,
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedBstr name;
EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive()));
EXPECT_STREQ(L"Name2", name.Get());
@@ -787,8 +825,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableRowOneSelected) {
ComPtr<IAccessible> row;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(selection.ptr())
- ->QueryInterface(IID_PPV_ARGS(row.GetAddressOf())));
+ V_DISPATCH(selection.ptr())->QueryInterface(IID_PPV_ARGS(&row)));
ScopedVariant role;
EXPECT_HRESULT_SUCCEEDED(row->get_accRole(SELF, role.Receive()));
@@ -816,8 +853,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableRowMultipleSelected) {
// Loop through the selections and make sure we have the right ones.
ComPtr<IEnumVARIANT> accessibles;
ASSERT_HRESULT_SUCCEEDED(
- V_UNKNOWN(selection.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessibles.GetAddressOf())));
+ V_UNKNOWN(selection.ptr())->QueryInterface(IID_PPV_ARGS(&accessibles)));
ULONG retrieved_count;
// Check out the first selected row.
@@ -828,8 +864,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableRowMultipleSelected) {
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedVariant role;
EXPECT_HRESULT_SUCCEEDED(accessible->get_accRole(SELF, role.Receive()));
EXPECT_EQ(ROLE_SYSTEM_ROW, V_I4(role.ptr()));
@@ -843,8 +878,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableRowMultipleSelected) {
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedVariant role;
EXPECT_HRESULT_SUCCEEDED(accessible->get_accRole(SELF, role.Receive()));
EXPECT_EQ(ROLE_SYSTEM_ROW, V_I4(role.ptr()));
@@ -870,8 +904,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellOneSelected) {
ASSERT_NE(nullptr, root_obj.Get());
ComPtr<IDispatch> row2;
- ASSERT_HRESULT_SUCCEEDED(
- root_obj->get_accChild(ScopedVariant(2), row2.GetAddressOf()));
+ ASSERT_HRESULT_SUCCEEDED(root_obj->get_accChild(ScopedVariant(2), &row2));
ComPtr<IAccessible> row2_accessible;
ASSERT_HRESULT_SUCCEEDED(row2.As(&row2_accessible));
@@ -882,8 +915,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellOneSelected) {
ComPtr<IAccessible> cell;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(selection.ptr())
- ->QueryInterface(IID_PPV_ARGS(cell.GetAddressOf())));
+ V_DISPATCH(selection.ptr())->QueryInterface(IID_PPV_ARGS(&cell)));
ScopedVariant role;
EXPECT_HRESULT_SUCCEEDED(cell->get_accRole(SELF, role.Receive()));
@@ -908,8 +940,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellMultipleSelected) {
ASSERT_NE(nullptr, root_obj.Get());
ComPtr<IDispatch> row3;
- ASSERT_HRESULT_SUCCEEDED(
- root_obj->get_accChild(ScopedVariant(3), row3.GetAddressOf()));
+ ASSERT_HRESULT_SUCCEEDED(root_obj->get_accChild(ScopedVariant(3), &row3));
ComPtr<IAccessible> row3_accessible;
ASSERT_HRESULT_SUCCEEDED(row3.As(&row3_accessible));
@@ -921,8 +952,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellMultipleSelected) {
// Loop through the selections and make sure we have the right ones.
ComPtr<IEnumVARIANT> accessibles;
ASSERT_HRESULT_SUCCEEDED(
- V_UNKNOWN(selection.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessibles.GetAddressOf())));
+ V_UNKNOWN(selection.ptr())->QueryInterface(IID_PPV_ARGS(&accessibles)));
ULONG retrieved_count;
// Check out the first selected cell.
@@ -933,8 +963,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellMultipleSelected) {
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedBstr name;
EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive()));
EXPECT_STREQ(L"3", name.Get());
@@ -948,8 +977,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleSelectionTableCellMultipleSelected) {
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(
- V_DISPATCH(item.ptr())
- ->QueryInterface(IID_PPV_ARGS(accessible.GetAddressOf())));
+ V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)));
ScopedBstr name;
EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive()));
EXPECT_STREQ(L"4", name.Get());
@@ -1061,24 +1089,21 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleChildAndParent) {
{
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK,
- root_iaccessible->get_accChild(SELF, result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(SELF, &result));
EXPECT_EQ(result.Get(), root_iaccessible.Get());
}
{
ComPtr<IDispatch> result;
ScopedVariant child1(1);
- EXPECT_EQ(S_OK,
- root_iaccessible->get_accChild(child1, result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(child1, &result));
EXPECT_EQ(result.Get(), button_iaccessible.Get());
}
{
ComPtr<IDispatch> result;
ScopedVariant child2(2);
- EXPECT_EQ(S_OK,
- root_iaccessible->get_accChild(child2, result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(child2, &result));
EXPECT_EQ(result.Get(), checkbox_iaccessible.Get());
}
@@ -1086,8 +1111,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleChildAndParent) {
// Asking for child id 3 should fail.
ComPtr<IDispatch> result;
ScopedVariant child3(3);
- EXPECT_EQ(E_INVALIDARG,
- root_iaccessible->get_accChild(child3, result.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, root_iaccessible->get_accChild(child3, &result));
}
// We should be able to ask for the button by its unique id too.
@@ -1098,8 +1122,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleChildAndParent) {
{
ComPtr<IDispatch> result;
ScopedVariant button_id_variant(button_unique_id);
- EXPECT_EQ(S_OK, root_iaccessible->get_accChild(button_id_variant,
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(button_id_variant, &result));
EXPECT_EQ(result.Get(), button_iaccessible.Get());
}
@@ -1112,26 +1135,26 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleChildAndParent) {
{
ComPtr<IDispatch> result;
ScopedVariant root_id_variant(root_unique_id);
- EXPECT_EQ(E_INVALIDARG, button_iaccessible->get_accChild(
- root_id_variant, result.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG,
+ button_iaccessible->get_accChild(root_id_variant, &result));
}
// Now check parents.
{
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, button_iaccessible->get_accParent(result.GetAddressOf()));
+ EXPECT_EQ(S_OK, button_iaccessible->get_accParent(&result));
EXPECT_EQ(result.Get(), root_iaccessible.Get());
}
{
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, checkbox_iaccessible->get_accParent(result.GetAddressOf()));
+ EXPECT_EQ(S_OK, checkbox_iaccessible->get_accParent(&result));
EXPECT_EQ(result.Get(), root_iaccessible.Get());
}
{
ComPtr<IDispatch> result;
- EXPECT_EQ(S_FALSE, root_iaccessible->get_accParent(result.GetAddressOf()));
+ EXPECT_EQ(S_FALSE, root_iaccessible->get_accParent(&result));
}
}
@@ -1319,19 +1342,19 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetAccessibilityAt) {
ASSERT_NE(nullptr, result.Get());
ComPtr<IUnknown> cell_1;
- EXPECT_EQ(S_OK, result->get_accessibleAt(1, 1, cell_1.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_accessibleAt(1, 1, &cell_1));
CheckIUnknownHasName(cell_1, L"1");
ComPtr<IUnknown> cell_2;
- EXPECT_EQ(S_OK, result->get_accessibleAt(1, 2, cell_2.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_accessibleAt(1, 2, &cell_2));
CheckIUnknownHasName(cell_2, L"2");
ComPtr<IUnknown> cell_3;
- EXPECT_EQ(S_OK, result->get_accessibleAt(2, 1, cell_3.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_accessibleAt(2, 1, &cell_3));
CheckIUnknownHasName(cell_3, L"3");
ComPtr<IUnknown> cell_4;
- EXPECT_EQ(S_OK, result->get_accessibleAt(2, 2, cell_4.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_accessibleAt(2, 2, &cell_4));
CheckIUnknownHasName(cell_4, L"4");
}
@@ -1346,26 +1369,22 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetAccessibilityAtOutOfBounds) {
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(E_INVALIDARG,
- result->get_accessibleAt(-1, -1, cell.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, result->get_accessibleAt(-1, -1, &cell));
}
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(E_INVALIDARG,
- result->get_accessibleAt(0, 5, cell.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, result->get_accessibleAt(0, 5, &cell));
}
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(E_INVALIDARG,
- result->get_accessibleAt(5, 0, cell.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, result->get_accessibleAt(5, 0, &cell));
}
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(E_INVALIDARG,
- result->get_accessibleAt(10, 10, cell.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, result->get_accessibleAt(10, 10, &cell));
}
}
@@ -1416,8 +1435,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2ScrollToPoint) {
ComPtr<IAccessible> root_iaccessible(GetRootIAccessible());
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, root_iaccessible->get_accChild(ScopedVariant(1),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(ScopedVariant(1), &result));
ComPtr<IAccessible2> ax_child1;
EXPECT_EQ(S_OK, result.As(&ax_child1));
result.Reset();
@@ -1468,8 +1486,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2ScrollTo) {
ComPtr<IAccessible> root_iaccessible(GetRootIAccessible());
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, root_iaccessible->get_accChild(ScopedVariant(1),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible->get_accChild(ScopedVariant(1), &result));
ComPtr<IAccessible2> ax_child1;
EXPECT_EQ(S_OK, result.As(&ax_child1));
result.Reset();
@@ -1711,13 +1728,13 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetCellAt) {
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(S_OK, result->get_cellAt(1, 1, cell.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_cellAt(1, 1, &cell));
CheckIUnknownHasName(cell, L"1");
}
{
ComPtr<IUnknown> cell;
- EXPECT_EQ(E_INVALIDARG, result->get_cellAt(-1, -1, cell.GetAddressOf()));
+ EXPECT_EQ(E_INVALIDARG, result->get_cellAt(-1, -1, &cell));
}
}
@@ -1816,7 +1833,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableCellGetTable) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -1824,7 +1841,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableCellGetTable) {
// Check to make sure that this is the right table by checking one cell.
ComPtr<IUnknown> cell_1;
- EXPECT_EQ(S_OK, result->get_accessibleAt(1, 1, cell_1.GetAddressOf()));
+ EXPECT_EQ(S_OK, result->get_accessibleAt(1, 1, &cell_1));
CheckIUnknownHasName(cell_1, L"1");
}
@@ -1857,14 +1874,12 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
ComPtr<IAccessible2> root_iaccessible2 = ToIAccessible2(root_iaccessible);
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(1),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(1), &result));
ComPtr<IAccessible2> ax_child1;
EXPECT_EQ(S_OK, result.As(&ax_child1));
result.Reset();
- EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(2),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(2), &result));
ComPtr<IAccessible2> ax_child2;
EXPECT_EQ(S_OK, result.As(&ax_child2));
result.Reset();
@@ -1880,7 +1895,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_EQ(1, n_relations);
EXPECT_HRESULT_SUCCEEDED(
- root_iaccessible2->get_relation(0, describedby_relation.GetAddressOf()));
+ root_iaccessible2->get_relation(0, &describedby_relation));
EXPECT_HRESULT_SUCCEEDED(
describedby_relation->get_relationType(relation_type.Receive()));
@@ -1891,12 +1906,10 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_nTargets(&n_targets));
EXPECT_EQ(2, n_targets);
- EXPECT_HRESULT_SUCCEEDED(
- describedby_relation->get_target(0, target.GetAddressOf()));
+ EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(0, &target));
target.Reset();
- EXPECT_HRESULT_SUCCEEDED(
- describedby_relation->get_target(1, target.GetAddressOf()));
+ EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(1, &target));
target.Reset();
describedby_relation.Reset();
@@ -1906,7 +1919,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_EQ(1, n_relations);
EXPECT_HRESULT_SUCCEEDED(
- ax_child1->get_relation(0, description_for_relation.GetAddressOf()));
+ ax_child1->get_relation(0, &description_for_relation));
EXPECT_HRESULT_SUCCEEDED(
description_for_relation->get_relationType(relation_type.Receive()));
EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
@@ -1915,8 +1928,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
EXPECT_EQ(1, n_targets);
- EXPECT_HRESULT_SUCCEEDED(
- description_for_relation->get_target(0, target.GetAddressOf()));
+ EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
target.Reset();
description_for_relation.Reset();
@@ -1924,7 +1936,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_EQ(1, n_relations);
EXPECT_HRESULT_SUCCEEDED(
- ax_child2->get_relation(0, description_for_relation.GetAddressOf()));
+ ax_child2->get_relation(0, &description_for_relation));
EXPECT_HRESULT_SUCCEEDED(
description_for_relation->get_relationType(relation_type.Receive()));
EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
@@ -1933,8 +1945,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessible2GetNRelations) {
EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
EXPECT_EQ(1, n_targets);
- EXPECT_HRESULT_SUCCEEDED(
- description_for_relation->get_target(0, target.GetAddressOf()));
+ EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
target.Reset();
// TODO(dougt): Try adding one more relation.
@@ -2070,20 +2081,17 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_TestRelationTargetsOfType) {
ComPtr<IAccessible2_2> root_iaccessible2 = ToIAccessible2_2(root_iaccessible);
ComPtr<IDispatch> result;
- EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(1),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(1), &result));
ComPtr<IAccessible2_2> ax_child1;
EXPECT_EQ(S_OK, result.As(&ax_child1));
result.Reset();
- EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(2),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(2), &result));
ComPtr<IAccessible2_2> ax_child2;
EXPECT_EQ(S_OK, result.As(&ax_child2));
result.Reset();
- EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(3),
- result.GetAddressOf()));
+ EXPECT_EQ(S_OK, root_iaccessible2->get_accChild(ScopedVariant(3), &result));
ComPtr<IAccessible2_2> ax_child3;
EXPECT_EQ(S_OK, result.As(&ax_child3));
result.Reset();
@@ -2131,7 +2139,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedChildrenZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2153,7 +2161,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedChildrenOne) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2182,7 +2190,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedChildrenMany) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2200,7 +2208,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedColumnsZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2227,7 +2235,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedColumnsOne) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2261,7 +2269,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedColumnsMany) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2279,7 +2287,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedRowsZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2306,7 +2314,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedRowsOne) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2340,7 +2348,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetNSelectedRowsMany) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2365,7 +2373,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedChildren) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2394,7 +2402,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedChildrenZeroMax) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2419,7 +2427,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedColumnsZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2449,7 +2457,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedColumnsOne) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2487,7 +2495,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedColumnsMany) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2510,7 +2518,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedRowsZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2539,7 +2547,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedRowsOne) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2576,7 +2584,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableGetSelectedRowsMany) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2607,7 +2615,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableIsColumnSelected) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2643,7 +2651,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableIsRowSelected) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2679,7 +2687,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTableIsSelected) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable> result;
table.As(&result);
@@ -2716,7 +2724,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTable2GetSelectedChildrenZero) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable2> result;
table.As(&result);
@@ -2742,7 +2750,7 @@ TEST_F(AXPlatformNodeWinTest, IAccessibleTable2GetSelectedChildren) {
ASSERT_NE(nullptr, cell.Get());
ComPtr<IUnknown> table;
- EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf()));
+ EXPECT_EQ(S_OK, cell->get_table(&table));
ComPtr<IAccessibleTable2> result;
table.As(&result);
@@ -5437,8 +5445,9 @@ TEST_F(AXPlatformNodeWinTest, UIAErrorHandling) {
&expand_collapse_state));
// IGridProvider
+ ComPtr<IRawElementProviderSimple> temp_simple_provider;
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
- grid_provider->GetItem(0, 0, simple_provider.GetAddressOf()));
+ grid_provider->GetItem(0, 0, &temp_simple_provider));
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
grid_provider->get_RowCount(&int_result));
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
@@ -5475,9 +5484,9 @@ TEST_F(AXPlatformNodeWinTest, UIAErrorHandling) {
selection_item_provider->Select());
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
selection_item_provider->get_IsSelected(&bool_result));
- EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
- selection_item_provider->get_SelectionContainer(
- simple_provider.GetAddressOf()));
+ EXPECT_EQ(
+ static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
+ selection_item_provider->get_SelectionContainer(&temp_simple_provider));
// ISelectionProvider
base::win::ScopedSafearray array_result;
@@ -5509,8 +5518,7 @@ TEST_F(AXPlatformNodeWinTest, UIAErrorHandling) {
ComPtr<IRawElementProviderSimple> host_provider;
ProviderOptions options;
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
- simple_provider->GetPatternProvider(UIA_WindowPatternId,
- unknown.GetAddressOf()));
+ simple_provider->GetPatternProvider(UIA_WindowPatternId, &unknown));
EXPECT_EQ(static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE),
simple_provider->GetPropertyValue(UIA_FrameworkIdPropertyId,
variant.Receive()));
diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.h b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.h
index 1fb54910d86..2d272715747 100644
--- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.h
+++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.h
@@ -46,6 +46,36 @@ class TestFragmentRootDelegate : public AXFragmentRootDelegateWin {
bool is_control_element_ = true;
};
+class MockIRawElementProviderSimple
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public IRawElementProviderSimple {
+ public:
+ BEGIN_COM_MAP(MockIRawElementProviderSimple)
+ COM_INTERFACE_ENTRY(IRawElementProviderSimple)
+ END_COM_MAP()
+
+ MockIRawElementProviderSimple();
+ ~MockIRawElementProviderSimple();
+
+ static HRESULT CreateMockIRawElementProviderSimple(
+ IRawElementProviderSimple** provider);
+
+ //
+ // IRawElementProviderSimple methods.
+ //
+ IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id,
+ IUnknown** result) override;
+
+ IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id,
+ VARIANT* result) override;
+
+ IFACEMETHODIMP
+ get_ProviderOptions(enum ProviderOptions* ret) override;
+
+ IFACEMETHODIMP
+ get_HostRawElementProvider(IRawElementProviderSimple** provider) override;
+};
+
class AXPlatformNodeWinTest : public AXPlatformNodeTest {
public:
AXPlatformNodeWinTest();
diff --git a/chromium/ui/accessibility/platform/ax_system_caret_win.cc b/chromium/ui/accessibility/platform/ax_system_caret_win.cc
index 00aede8ab16..4b7356abeef 100644
--- a/chromium/ui/accessibility/platform/ax_system_caret_win.cc
+++ b/chromium/ui/accessibility/platform/ax_system_caret_win.cc
@@ -47,9 +47,7 @@ AXSystemCaretWin::~AXSystemCaretWin() {
Microsoft::WRL::ComPtr<IAccessible> AXSystemCaretWin::GetCaret() const {
Microsoft::WRL::ComPtr<IAccessible> caret_accessible;
- HRESULT hr = caret_->QueryInterface(
- IID_IAccessible,
- reinterpret_cast<void**>(caret_accessible.GetAddressOf()));
+ HRESULT hr = caret_->QueryInterface(IID_PPV_ARGS(&caret_accessible));
DCHECK(SUCCEEDED(hr));
return caret_accessible;
}
diff --git a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc
index 873ab5c1985..45d3274a5ea 100644
--- a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -966,11 +966,11 @@ gfx::RectF TestAXNodeWrapper::GetInlineTextRect(const int start_offset,
gfx::RectF location = GetLocation();
gfx::RectF bounds;
- switch (static_cast<ax::mojom::TextDirection>(
+ switch (static_cast<ax::mojom::WritingDirection>(
GetData().GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) {
// Currently only kNone and kLtr are supported text direction.
- case ax::mojom::TextDirection::kNone:
- case ax::mojom::TextDirection::kLtr: {
+ case ax::mojom::WritingDirection::kNone:
+ case ax::mojom::WritingDirection::kLtr: {
int start_pixel_offset =
start_offset > 0 ? character_offsets[start_offset - 1] : location.x();
int end_pixel_offset =
diff --git a/chromium/ui/accessibility/test_ax_node_helper.cc b/chromium/ui/accessibility/test_ax_node_helper.cc
index ba257f7fffe..a2cfcf0f114 100644
--- a/chromium/ui/accessibility/test_ax_node_helper.cc
+++ b/chromium/ui/accessibility/test_ax_node_helper.cc
@@ -158,11 +158,11 @@ gfx::RectF TestAXNodeHelper::GetInlineTextRect(const int start_offset,
gfx::RectF location = GetLocation();
gfx::RectF bounds;
- switch (static_cast<ax::mojom::TextDirection>(
+ switch (static_cast<ax::mojom::WritingDirection>(
GetData().GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) {
// Currently only kNone and kLtr are supported text direction.
- case ax::mojom::TextDirection::kNone:
- case ax::mojom::TextDirection::kLtr: {
+ case ax::mojom::WritingDirection::kNone:
+ case ax::mojom::WritingDirection::kLtr: {
int start_pixel_offset =
start_offset > 0 ? character_offsets[start_offset - 1] : location.x();
int end_pixel_offset =
diff --git a/chromium/ui/android/BUILD.gn b/chromium/ui/android/BUILD.gn
index 7b8a034042b..ec14b8b8b33 100644
--- a/chromium/ui/android/BUILD.gn
+++ b/chromium/ui/android/BUILD.gn
@@ -18,10 +18,6 @@ component("android") {
"display_android_manager.h",
"edge_effect.cc",
"edge_effect.h",
- "edge_effect_base.cc",
- "edge_effect_base.h",
- "edge_effect_l.cc",
- "edge_effect_l.h",
"event_forwarder.cc",
"event_forwarder.h",
"handle_view_resources.cc",
@@ -129,7 +125,7 @@ java_strings_grd("ui_strings_grd") {
}
android_resources("ui_java_resources") {
- custom_package = "org.chromium.ui"
+ create_srcjar = false
sources = [
"java/res/color/blue_when_enabled.xml",
"java/res/color/chip_background_color.xml",
@@ -169,12 +165,14 @@ android_resources("ui_java_resources") {
"java/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png",
"java/res/drawable-xxxhdpi/popup_bg.9.png",
"java/res/drawable/drag_handlebar.xml",
+ "java/res/drawable/ic_expand_more_horizontal_black_24dp.xml",
"java/res/drawable/popup_bg_tinted.xml",
"java/res/font/accent_font.xml",
"java/res/layout/dropdown_footer_wrapper_jellybean.xml",
"java/res/layout/dropdown_item.xml",
"java/res/layout/dropdown_window.xml",
"java/res/values-ldrtl/dimens.xml",
+ "java/res/values-ldrtl/values.xml",
"java/res/values-night/colors.xml",
"java/res/values-night/dimens.xml",
"java/res/values-sw600dp/values.xml",
@@ -184,6 +182,7 @@ android_resources("ui_java_resources") {
"java/res/values/attrs.xml",
"java/res/values/color_palette.xml",
"java/res/values/dimens.xml",
+ "java/res/values/font_certs.xml",
"java/res/values/ids.xml",
"java/res/values/one_off_colors.xml",
"java/res/values/semantic_colors_adaptive.xml",
@@ -208,12 +207,12 @@ android_library("ui_utils_java") {
sources = [
"java/src/org/chromium/ui/ContactsPickerListener.java",
"java/src/org/chromium/ui/KeyboardVisibilityDelegate.java",
- "java/src/org/chromium/ui/PhotoPickerListener.java",
"java/src/org/chromium/ui/UiUtils.java",
]
deps = [
"//base:base_java",
"//components/payments/mojom:mojom_java",
+ "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_deps:androidx_appcompat_appcompat_java",
"//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
"//third_party/android_deps:androidx_core_core_java",
@@ -235,6 +234,7 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/DropdownPopupWindowInterface.java",
"java/src/org/chromium/ui/DropdownPopupWindowJellyBean.java",
"java/src/org/chromium/ui/HorizontalListDividerDrawable.java",
+ "java/src/org/chromium/ui/LayoutInflaterUtils.java",
"java/src/org/chromium/ui/OverscrollRefreshHandler.java",
"java/src/org/chromium/ui/UiSwitches.java",
"java/src/org/chromium/ui/VSyncMonitor.java",
@@ -254,6 +254,8 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/base/IntentWindowAndroid.java",
"java/src/org/chromium/ui/base/LocalizationUtils.java",
"java/src/org/chromium/ui/base/PermissionCallback.java",
+ "java/src/org/chromium/ui/base/PhotoPickerDelegate.java",
+ "java/src/org/chromium/ui/base/PhotoPickerListener.java",
"java/src/org/chromium/ui/base/ResourceBundle.java",
"java/src/org/chromium/ui/base/SPenSupport.java",
"java/src/org/chromium/ui/base/SelectFileDialog.java",
@@ -356,6 +358,7 @@ android_library("ui_full_java") {
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
srcjar_deps = [ ":java_enums_srcjar" ]
+ resources_package = "org.chromium.ui"
}
android_library("ui_java_test_support") {
@@ -378,6 +381,7 @@ android_library("ui_java_test_support") {
"//base:base_java",
"//base:base_java_test_support",
"//third_party/android_deps:android_support_v7_appcompat_java",
+ "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_support_test_runner:rules_java",
"//third_party/junit",
]
@@ -385,6 +389,7 @@ android_library("ui_java_test_support") {
android_resources("ui_javatest_resources") {
testonly = true
+ create_srcjar = false
sources = [
"junit/res/layout/inflated_view.xml",
"junit/res/layout/layout_view_builder_test.xml",
@@ -505,6 +510,7 @@ android_library("ui_javatests") {
"//base:base_java_test_support",
"//base:jni_java",
"//content/public/test/android:content_java_test_support",
+ "//third_party/android_deps:androidx_test_runner_java",
"//third_party/junit",
]
}
diff --git a/chromium/ui/android/OWNERS b/chromium/ui/android/OWNERS
index 4f91a99ea76..2a99c692980 100644
--- a/chromium/ui/android/OWNERS
+++ b/chromium/ui/android/OWNERS
@@ -10,7 +10,6 @@ boliu@chromium.org
jinsukkim@chromium.org
# for CC and Viz integration
-ericrk@chromium.org
khushalsagar@chromium.org
# COMPONENT: UI
diff --git a/chromium/ui/android/delegated_frame_host_android.cc b/chromium/ui/android/delegated_frame_host_android.cc
index da695040108..cb488cb8593 100644
--- a/chromium/ui/android/delegated_frame_host_android.cc
+++ b/chromium/ui/android/delegated_frame_host_android.cc
@@ -226,18 +226,21 @@ void DelegatedFrameHostAndroid::WasHidden() {
void DelegatedFrameHostAndroid::WasShown(
const viz::LocalSurfaceId& new_local_surface_id,
- const gfx::Size& new_size_in_pixels) {
+ const gfx::Size& new_size_in_pixels,
+ bool is_fullscreen) {
frame_evictor_->SetVisible(true);
EmbedSurface(
new_local_surface_id, new_size_in_pixels,
- cc::DeadlinePolicy::UseSpecifiedDeadline(FirstFrameTimeoutFrames()));
+ cc::DeadlinePolicy::UseSpecifiedDeadline(FirstFrameTimeoutFrames()),
+ is_fullscreen);
}
void DelegatedFrameHostAndroid::EmbedSurface(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Size& new_size_in_pixels,
- cc::DeadlinePolicy deadline_policy) {
+ cc::DeadlinePolicy deadline_policy,
+ bool is_fullscreen) {
// We should never attempt to embed an invalid surface. Catch this here to
// track down the root cause. Otherwise we will have vague crashes later on
// at serialization time.
@@ -249,16 +252,23 @@ void DelegatedFrameHostAndroid::EmbedSurface(
viz::SurfaceId current_primary_surface_id = content_layer_->surface_id();
viz::SurfaceId new_primary_surface_id(frame_sink_id_, local_surface_id_);
- if (!frame_evictor_->visible()) {
- // If the tab is resized while hidden, advance the fallback so that the next
- // time user switches back to it the page is blank. This is preferred to
- // showing contents of old size. Don't call EvictDelegatedFrame to avoid
- // races when dragging tabs across displays. See https://crbug.com/813157.
+ if (!frame_evictor_->visible() || is_fullscreen) {
+ // For fullscreen or when tab is hidden we don't want to display old sized
+ // content. So we advance the fallback forcing viz to fallback to blank
+ // screen if renderer won't submit frame in time. See
+ // https://crbug.com/1088369 and https://crbug.com/813157
if (surface_size_in_pixels_ != content_layer_->bounds() &&
content_layer_->oldest_acceptable_fallback() &&
content_layer_->oldest_acceptable_fallback()->is_valid()) {
content_layer_->SetOldestAcceptableFallback(new_primary_surface_id);
+
+ // We default to black background for fullscreen case.
+ content_layer_->SetBackgroundColor(is_fullscreen ? SK_ColorBLACK
+ : SK_ColorTRANSPARENT);
}
+ }
+
+ if (!frame_evictor_->visible()) {
// Don't update the SurfaceLayer when invisible to avoid blocking on
// renderers that do not submit CompositorFrames. Next time the renderer
// is visible, EmbedSurface will be called again. See WasShown.
diff --git a/chromium/ui/android/delegated_frame_host_android.h b/chromium/ui/android/delegated_frame_host_android.h
index ee97a55ab97..fcea61c10b7 100644
--- a/chromium/ui/android/delegated_frame_host_android.h
+++ b/chromium/ui/android/delegated_frame_host_android.h
@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/numerics/safe_conversions.h"
#include "cc/layers/deadline_policy.h"
#include "components/viz/client/frame_evictor.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
@@ -48,14 +49,19 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid
~DelegatedFrameHostAndroid() override;
+ static int64_t TimeDeltaToFrames(base::TimeDelta delta) {
+ return base::ClampRound<int64_t>(delta /
+ viz::BeginFrameArgs::DefaultInterval());
+ }
+
// Wait up to 5 seconds for the first frame to be produced. Having Android
// display a placeholder for a longer period of time is preferable to drawing
// nothing, and the first frame can take a while on low-end systems.
static constexpr base::TimeDelta FirstFrameTimeout() {
return base::TimeDelta::FromSeconds(5);
}
- static constexpr int64_t FirstFrameTimeoutFrames() {
- return FirstFrameTimeout() / viz::BeginFrameArgs::DefaultInterval();
+ static int64_t FirstFrameTimeoutFrames() {
+ return TimeDeltaToFrames(FirstFrameTimeout());
}
// Wait up to 1 second for a frame of the correct size to be produced. Android
@@ -64,13 +70,10 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid
static constexpr base::TimeDelta ResizeTimeout() {
return base::TimeDelta::FromSeconds(1);
}
- static constexpr int64_t ResizeTimeoutFrames() {
- return ResizeTimeout() / viz::BeginFrameArgs::DefaultInterval();
+ static int64_t ResizeTimeoutFrames() {
+ return TimeDeltaToFrames(ResizeTimeout());
}
- // FrameEvictorClient implementation.
- void EvictDelegatedFrame() override;
-
// Advances the fallback surface to the first surface after navigation. This
// ensures that stale surfaces are not presented to the user for an indefinite
// period of time.
@@ -102,10 +105,12 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid
bool HasSavedFrame() const;
void WasHidden();
void WasShown(const viz::LocalSurfaceId& local_surface_id,
- const gfx::Size& size_in_pixels);
+ const gfx::Size& size_in_pixels,
+ bool is_fullscreen);
void EmbedSurface(const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Size& new_size_in_pixels,
- cc::DeadlinePolicy deadline_policy);
+ cc::DeadlinePolicy deadline_policy,
+ bool is_fullscreen);
// Returns the ID for the current Surface. Returns an invalid ID if no
// surface exists (!HasDelegatedContent()).
@@ -121,6 +126,9 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid
void SetTopControlsVisibleHeight(float height);
private:
+ // FrameEvictorClient implementation.
+ void EvictDelegatedFrame() override;
+
// viz::HostFrameSinkClient implementation.
void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
void OnFrameTokenChanged(uint32_t frame_token) override;
diff --git a/chromium/ui/android/edge_effect.cc b/chromium/ui/android/edge_effect.cc
index a5189405681..bc09abc4c32 100644
--- a/chromium/ui/android/edge_effect.cc
+++ b/chromium/ui/android/edge_effect.cc
@@ -1,40 +1,33 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/android/edge_effect.h"
-#include "base/macros.h"
-#include "cc/layers/layer.h"
+#include "base/notreached.h"
#include "cc/layers/ui_resource_layer.h"
#include "ui/android/animation_utils.h"
#include "ui/android/resources/resource_manager.h"
#include "ui/android/resources/system_ui_resource_type.h"
#include "ui/android/window_android_compositor.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace ui {
-
namespace {
-
-const ui::SystemUIResourceType kEdgeResourceId = ui::OVERSCROLL_EDGE;
-const ui::SystemUIResourceType kGlowResourceId = ui::OVERSCROLL_GLOW;
-
// Time it will take the effect to fully recede in ms
-const int kRecedeTimeMs = 1000;
+const int kRecedeTimeMs = 600;
// Time it will take before a pulled glow begins receding in ms
const int kPullTimeMs = 167;
-// Time it will take in ms for a pulled glow to decay before release
-const int kPullDecayTimeMs = 1000;
-
-const float kMaxAlpha = 1.f;
-const float kHeldEdgeScaleY = .5f;
+// Time it will take for a pulled glow to decay to partial strength before
+// release
+const int kPullDecayTimeMs = 2000;
-const float kMaxGlowHeight = 4.f;
+const float kMaxAlpha = 0.5f;
-const float kPullGlowBegin = 1.f;
-const float kPullEdgeBegin = 0.6f;
+const float kPullGlowBegin = 0.f;
// Min/max velocity that will be absorbed
const float kMinVelocity = 100.f;
@@ -42,85 +35,82 @@ const float kMaxVelocity = 10000.f;
const float kEpsilon = 0.001f;
-const float kGlowHeightWidthRatio = 0.25f;
-
-// How much dragging should effect the height of the edge image.
-// Number determined by user testing.
-const int kPullDistanceEdgeFactor = 7;
+const float kSin = 0.5f; // sin(PI / 6)
+const float kCos = 0.866f; // cos(PI / 6);
// How much dragging should effect the height of the glow image.
// Number determined by user testing.
-const int kPullDistanceGlowFactor = 7;
-const float kPullDistanceAlphaGlowFactor = 1.1f;
-
-const int kVelocityEdgeFactor = 8;
-const int kVelocityGlowFactor = 12;
+const float kPullDistanceAlphaGlowFactor = 0.8f;
+
+const int kVelocityGlowFactor = 6;
+
+const ui::SystemUIResourceType kResourceId = ui::OVERSCROLL_GLOW;
+
+// Computes the transform for an edge effect given the |edge|, |viewport_size|
+// and edge |offset|. This assumes the the effect transform anchor is at the
+// centered edge of the effect.
+gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
+ const gfx::SizeF& viewport_size,
+ float offset) {
+ // Transforms assume the edge layers are anchored to their *top center point*.
+ switch (edge) {
+ case EdgeEffect::EDGE_TOP:
+ return gfx::Transform(1, 0, 0, 1, 0, offset);
+ case EdgeEffect::EDGE_LEFT:
+ return gfx::Transform(0, 1, -1, 0, -viewport_size.height() / 2.f + offset,
+ viewport_size.height() / 2.f);
+ case EdgeEffect::EDGE_BOTTOM:
+ return gfx::Transform(-1, 0, 0, -1, 0, viewport_size.height() + offset);
+ case EdgeEffect::EDGE_RIGHT:
+ return gfx::Transform(
+ 0, -1, 1, 0,
+ -viewport_size.height() / 2.f + viewport_size.width() + offset,
+ viewport_size.height() / 2.f);
+ default:
+ NOTREACHED() << "Invalid edge: " << edge;
+ return gfx::Transform();
+ };
+}
-const float kEdgeHeightAtMdpi = 12.f;
-const float kGlowHeightAtMdpi = 128.f;
+// Computes the maximum effect size relative to the screen |edge|. For
+// top/bottom edges, this is simply |viewport_size|, while for left/right
+// edges this is |viewport_size| with coordinates swapped.
+gfx::SizeF ComputeOrientedSize(EdgeEffect::Edge edge,
+ const gfx::SizeF& viewport_size) {
+ switch (edge) {
+ case EdgeEffect::EDGE_TOP:
+ case EdgeEffect::EDGE_BOTTOM:
+ return viewport_size;
+ case EdgeEffect::EDGE_LEFT:
+ case EdgeEffect::EDGE_RIGHT:
+ return gfx::SizeF(viewport_size.height(), viewport_size.width());
+ default:
+ NOTREACHED() << "Invalid edge: " << edge;
+ return gfx::SizeF();
+ };
+}
} // namespace
-class EdgeEffect::EffectLayer {
- public:
- EffectLayer(ui::SystemUIResourceType resource_type,
- ui::ResourceManager* resource_manager)
- : ui_resource_layer_(cc::UIResourceLayer::Create()),
- resource_type_(resource_type),
- resource_manager_(resource_manager) {}
-
- ~EffectLayer() { ui_resource_layer_->RemoveFromParent(); }
-
- void SetParent(cc::Layer* parent) {
- if (ui_resource_layer_->parent() != parent)
- parent->AddChild(ui_resource_layer_);
- }
-
- void Disable() { ui_resource_layer_->SetIsDrawable(false); }
-
- void Update(const gfx::Size& size,
- const gfx::Transform& transform,
- float opacity) {
- ui_resource_layer_->SetUIResourceId(resource_manager_->GetUIResourceId(
- ui::ANDROID_RESOURCE_TYPE_SYSTEM, resource_type_));
- ui_resource_layer_->SetIsDrawable(true);
- ui_resource_layer_->SetTransformOrigin(
- gfx::Point3F(size.width() * 0.5f, 0, 0));
- ui_resource_layer_->SetTransform(transform);
- ui_resource_layer_->SetBounds(size);
- ui_resource_layer_->SetOpacity(Clamp(opacity, 0.f, 1.f));
- }
-
- scoped_refptr<cc::UIResourceLayer> ui_resource_layer_;
- ui::SystemUIResourceType resource_type_;
- ui::ResourceManager* resource_manager_;
-
- DISALLOW_COPY_AND_ASSIGN(EffectLayer);
-};
-
-EdgeEffect::EdgeEffect(ui::ResourceManager* resource_manager,
- float device_scale_factor)
- : edge_(new EffectLayer(kEdgeResourceId, resource_manager)),
- glow_(new EffectLayer(kGlowResourceId, resource_manager)),
- base_edge_height_(kEdgeHeightAtMdpi * device_scale_factor),
- base_glow_height_(kGlowHeightAtMdpi * device_scale_factor),
- edge_alpha_(0),
- edge_scale_y_(0),
+EdgeEffect::EdgeEffect(ui::ResourceManager* resource_manager)
+ : resource_manager_(resource_manager),
+ glow_(cc::UIResourceLayer::Create()),
glow_alpha_(0),
glow_scale_y_(0),
- edge_alpha_start_(0),
- edge_alpha_finish_(0),
- edge_scale_y_start_(0),
- edge_scale_y_finish_(0),
glow_alpha_start_(0),
glow_alpha_finish_(0),
glow_scale_y_start_(0),
glow_scale_y_finish_(0),
+ displacement_(0.5f),
+ target_displacement_(0.5f),
state_(STATE_IDLE),
pull_distance_(0) {
+ // Prevent the provided layers from drawing until the effect is activated.
+ glow_->SetIsDrawable(false);
}
EdgeEffect::~EdgeEffect() {
+ glow_->RemoveFromParent();
}
bool EdgeEffect::IsFinished() const {
@@ -128,8 +118,7 @@ bool EdgeEffect::IsFinished() const {
}
void EdgeEffect::Finish() {
- edge_->Disable();
- glow_->Disable();
+ glow_->SetIsDrawable(false);
pull_distance_ = 0;
state_ = STATE_IDLE;
}
@@ -137,11 +126,12 @@ void EdgeEffect::Finish() {
void EdgeEffect::Pull(base::TimeTicks current_time,
float delta_distance,
float displacement) {
+ target_displacement_ = displacement;
if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
return;
}
if (state_ != STATE_PULL) {
- glow_scale_y_ = kPullGlowBegin;
+ glow_scale_y_ = std::max(kPullGlowBegin, glow_scale_y_);
}
state_ = STATE_PULL;
@@ -150,30 +140,20 @@ void EdgeEffect::Pull(base::TimeTicks current_time,
float abs_delta_distance = std::abs(delta_distance);
pull_distance_ += delta_distance;
- float distance = std::abs(pull_distance_);
-
- edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
- edge_scale_y_ = edge_scale_y_start_ =
- Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
-
- glow_alpha_ = glow_alpha_start_ =
- std::min(kMaxAlpha,
- glow_alpha_ + abs_delta_distance * kPullDistanceAlphaGlowFactor);
-
- float glow_change = abs_delta_distance;
- if (delta_distance > 0 && pull_distance_ < 0)
- glow_change = -glow_change;
- if (pull_distance_ == 0)
- glow_scale_y_ = 0;
-
- // Do not allow glow to get larger than kMaxGlowHeight.
- glow_scale_y_ = glow_scale_y_start_ =
- Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
- 0.f,
- kMaxGlowHeight);
-
- edge_alpha_finish_ = edge_alpha_;
- edge_scale_y_finish_ = edge_scale_y_;
+
+ glow_alpha_ = glow_alpha_start_ = std::min(
+ kMaxAlpha,
+ glow_alpha_ + (abs_delta_distance * kPullDistanceAlphaGlowFactor));
+
+ if (pull_distance_ == 0) {
+ glow_scale_y_ = glow_scale_y_start_ = 0;
+ } else {
+ float scale = 1.f -
+ 1.f / std::sqrt(std::abs(pull_distance_) * bounds_.height()) -
+ 0.3f;
+ glow_scale_y_ = glow_scale_y_start_ = std::max(0.f, scale) / 0.7f;
+ }
+
glow_alpha_finish_ = glow_alpha_;
glow_scale_y_finish_ = glow_scale_y_;
}
@@ -185,13 +165,9 @@ void EdgeEffect::Release(base::TimeTicks current_time) {
return;
state_ = STATE_RECEDE;
- edge_alpha_start_ = edge_alpha_;
- edge_scale_y_start_ = edge_scale_y_;
glow_alpha_start_ = glow_alpha_;
glow_scale_y_start_ = glow_scale_y_;
- edge_alpha_finish_ = 0.f;
- edge_scale_y_finish_ = 0.f;
glow_alpha_finish_ = 0.f;
glow_scale_y_finish_ = 0.f;
@@ -201,51 +177,39 @@ void EdgeEffect::Release(base::TimeTicks current_time) {
void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
state_ = STATE_ABSORB;
+
velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity);
start_time_ = current_time;
// This should never be less than 1 millisecond.
duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f));
- // The edge should always be at least partially visible, regardless
- // of velocity.
- edge_alpha_start_ = 0.f;
- edge_scale_y_ = edge_scale_y_start_ = 0.f;
// The glow depends more on the velocity, and therefore starts out
// nearly invisible.
glow_alpha_start_ = 0.3f;
- glow_scale_y_start_ = 0.f;
-
- // Factor the velocity by 8. Testing on device shows this works best to
- // reflect the strength of the user's scrolling.
- edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
- // Edge should never get larger than the size of its asset.
- edge_scale_y_finish_ =
- Clamp(velocity * kVelocityEdgeFactor, kHeldEdgeScaleY, 1.f);
+ glow_scale_y_start_ = std::max(glow_scale_y_, 0.f);
- // Growth for the size of the glow should be quadratic to properly
- // respond
+ // Growth for the size of the glow should be quadratic to properly respond
// to a user's scrolling speed. The faster the scrolling speed, the more
// intense the effect should be for both the size and the saturation.
glow_scale_y_finish_ =
- std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+ std::min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2.f, 1.f);
// Alpha should change for the glow as well as size.
glow_alpha_finish_ = Clamp(
glow_alpha_start_, velocity * kVelocityGlowFactor * .00001f, kMaxAlpha);
+ target_displacement_ = 0.5;
}
bool EdgeEffect::Update(base::TimeTicks current_time) {
if (IsFinished())
return false;
- const double dt = (current_time - start_time_).InMilliseconds();
- const double t = std::min(dt / duration_.InMilliseconds(), 1.);
- const float interp = static_cast<float>(Damp(t, 1.));
+ const double t = std::min((current_time - start_time_) / duration_, 1.0);
+ const float interp = static_cast<float>(Damp(t, 1.0));
- edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
- edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
+ displacement_ = (displacement_ + target_displacement_) / 2.f;
if (t >= 1.f - kEpsilon) {
switch (state_) {
@@ -254,45 +218,27 @@ bool EdgeEffect::Update(base::TimeTicks current_time) {
start_time_ = current_time;
duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs);
- edge_alpha_start_ = edge_alpha_;
- edge_scale_y_start_ = edge_scale_y_;
glow_alpha_start_ = glow_alpha_;
glow_scale_y_start_ = glow_scale_y_;
- // After absorb, the glow and edge should fade to nothing.
- edge_alpha_finish_ = 0.f;
- edge_scale_y_finish_ = 0.f;
- glow_alpha_finish_ = 0.f;
- glow_scale_y_finish_ = 0.f;
+ glow_alpha_finish_ = 0.0f;
+ glow_scale_y_finish_ = 0.0f;
break;
case STATE_PULL:
state_ = STATE_PULL_DECAY;
start_time_ = current_time;
duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTimeMs);
- edge_alpha_start_ = edge_alpha_;
- edge_scale_y_start_ = edge_scale_y_;
glow_alpha_start_ = glow_alpha_;
glow_scale_y_start_ = glow_scale_y_;
- // After pull, the glow and edge should fade to nothing.
- edge_alpha_finish_ = 0.f;
- edge_scale_y_finish_ = 0.f;
- glow_alpha_finish_ = 0.f;
- glow_scale_y_finish_ = 0.f;
+ // After pull, the glow should fade to nothing.
+ glow_alpha_finish_ = 0.0f;
+ glow_scale_y_finish_ = 0.0f;
break;
- case STATE_PULL_DECAY: {
- // When receding, we want edge to decrease more slowly
- // than the glow.
- const float factor =
- glow_scale_y_finish_
- ? 1 / (glow_scale_y_finish_ * glow_scale_y_finish_)
- : std::numeric_limits<float>::max();
- edge_scale_y_ =
- edge_scale_y_start_ +
- (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
+ case STATE_PULL_DECAY:
state_ = STATE_RECEDE;
- } break;
+ break;
case STATE_RECEDE:
Finish();
break;
@@ -301,14 +247,17 @@ bool EdgeEffect::Update(base::TimeTicks current_time) {
}
}
- if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
+ bool one_last_frame = false;
+ if (state_ == STATE_RECEDE && glow_scale_y_ <= 0) {
Finish();
+ one_last_frame = true;
+ }
- return !IsFinished();
+ return !IsFinished() || one_last_frame;
}
float EdgeEffect::GetAlpha() const {
- return IsFinished() ? 0.f : std::max(glow_alpha_, edge_alpha_);
+ return IsFinished() ? 0.f : glow_alpha_;
}
void EdgeEffect::ApplyToLayers(Edge edge,
@@ -317,44 +266,71 @@ void EdgeEffect::ApplyToLayers(Edge edge,
if (IsFinished())
return;
- // An empty window size, while meaningless, is also relatively harmless, and
- // will simply prevent any drawing of the layers.
+ // An empty viewport, while meaningless, is also relatively harmless, and will
+ // simply prevent any drawing of the layers.
if (viewport_size.IsEmpty()) {
- edge_->Disable();
- glow_->Disable();
+ glow_->SetIsDrawable(false);
return;
}
gfx::SizeF size = ComputeOrientedSize(edge, viewport_size);
- gfx::Transform transform = ComputeTransform(edge, viewport_size, offset);
-
- // Glow
- const int scaled_glow_height = static_cast<int>(
- std::min(base_glow_height_ * glow_scale_y_ * kGlowHeightWidthRatio * 0.6f,
- base_glow_height_ * kMaxGlowHeight) +
- 0.5f);
- const gfx::Size glow_size(size.width(), scaled_glow_height);
- glow_->Update(glow_size, transform, glow_alpha_);
-
- // Edge
- const int scaled_edge_height =
- static_cast<int>(base_edge_height_ * edge_scale_y_);
- const gfx::Size edge_size(size.width(), scaled_edge_height);
- edge_->Update(edge_size, transform, edge_alpha_);
-}
+ const float r = size.width() * 0.75f / kSin;
+ const float y = kCos * r;
+ const float h = r - y;
+ const float o_r = size.height() * 0.75f / kSin;
+ const float o_y = kCos * o_r;
+ const float o_h = o_r - o_y;
+ const float base_glow_scale = h > 0.f ? std::min(o_h / h, 1.f) : 1.f;
+ bounds_ = gfx::Size(size.width(), (int)std::min(size.height(), h));
+ gfx::Size image_bounds(
+ r, std::min(1.f, glow_scale_y_) * base_glow_scale * bounds_.height());
+
+ // Compute the displaced image rect. This includes both the horizontal
+ // offset from the |displacement_| factor, as well as the vertical edge offset
+ // provided by the method call.
+ const float displacement = Clamp(displacement_, 0.f, 1.f) - 0.5f;
+ const float displacement_offset_x = bounds_.width() * displacement * 0.5f;
+ const float image_offset_x = (bounds_.width() - image_bounds.width()) * 0.5f;
+ gfx::RectF image_rect = gfx::RectF(gfx::SizeF(image_bounds));
+ image_rect.Offset(image_offset_x - displacement_offset_x, -std::abs(offset));
+
+ // Clip the image rect against the viewport. If either rect is empty there's
+ // no need to draw anything further.
+ gfx::RectF clipped_rect(size.width(), size.height());
+ clipped_rect.Intersect(image_rect);
+ if (clipped_rect.IsEmpty() || image_rect.IsEmpty()) {
+ glow_->SetIsDrawable(false);
+ return;
+ }
-void EdgeEffect::SetParent(cc::Layer* parent) {
- edge_->SetParent(parent);
- glow_->SetParent(parent);
+ // Compute the logical UV coordinates of the clipped rect relative to the
+ // displaced image rect.
+ gfx::PointF clipped_top_left = clipped_rect.origin();
+ gfx::PointF clipped_bottom_right = clipped_rect.bottom_right();
+ gfx::PointF uv_top_left(
+ (clipped_top_left.x() - image_rect.x()) / image_rect.width(),
+ (clipped_top_left.y() - image_rect.y()) / image_rect.height());
+ gfx::PointF uv_bottom_right(
+ (clipped_bottom_right.x() - image_rect.x()) / image_rect.width(),
+ (clipped_bottom_right.y() - image_rect.y()) / image_rect.height());
+ glow_->SetUV(uv_top_left, uv_bottom_right);
+
+ // There's no need to use the provided |offset| when computing the transform;
+ // the offset is built in to the computed UV coordinates.
+ glow_->SetTransform(ComputeTransform(edge, viewport_size, 0));
+
+ glow_->SetIsDrawable(true);
+ glow_->SetUIResourceId(resource_manager_->GetUIResourceId(
+ ui::ANDROID_RESOURCE_TYPE_SYSTEM, kResourceId));
+ glow_->SetTransformOrigin(gfx::Point3F(bounds_.width() * 0.5f, 0, 0));
+ glow_->SetBounds(gfx::ToRoundedSize(clipped_rect.size()));
+ glow_->SetContentsOpaque(false);
+ glow_->SetOpacity(Clamp(glow_alpha_, 0.f, 1.f));
}
-// static
-void EdgeEffect::PreloadResources(ui::ResourceManager* resource_manager) {
- DCHECK(resource_manager);
- resource_manager->PreloadResource(ui::ANDROID_RESOURCE_TYPE_SYSTEM,
- kEdgeResourceId);
- resource_manager->PreloadResource(ui::ANDROID_RESOURCE_TYPE_SYSTEM,
- kGlowResourceId);
+void EdgeEffect::SetParent(cc::Layer* parent) {
+ if (glow_->parent() != parent)
+ parent->AddChild(glow_);
}
} // namespace ui
diff --git a/chromium/ui/android/edge_effect.h b/chromium/ui/android/edge_effect.h
index 67dd6f37253..2e0039eb0ed 100644
--- a/chromium/ui/android/edge_effect.h
+++ b/chromium/ui/android/edge_effect.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,11 +8,17 @@
#include <memory>
#include "base/macros.h"
-#include "ui/android/edge_effect_base.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
#include "ui/android/ui_android_export.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/transform.h"
namespace cc {
class Layer;
+class UIResourceLayer;
}
namespace ui {
@@ -21,57 +27,54 @@ class ResourceManager;
namespace ui {
-// |EdgeEffect| mirrors its Android counterpart, EdgeEffect.java.
-// Conscious tradeoffs were made to align this as closely as possible with the
-// the original Android java version.
-// All coordinates and dimensions are in device pixels.
-class UI_ANDROID_EXPORT EdgeEffect : public EdgeEffectBase {
+// A base class for overscroll-related Android effects.
+class UI_ANDROID_EXPORT EdgeEffect {
public:
- explicit EdgeEffect(ui::ResourceManager* resource_manager,
- float device_scale_factor);
- ~EdgeEffect() override;
+ enum State {
+ STATE_IDLE = 0,
+ STATE_PULL,
+ STATE_ABSORB,
+ STATE_RECEDE,
+ STATE_PULL_DECAY
+ };
+
+ enum Edge { EDGE_TOP, EDGE_LEFT, EDGE_BOTTOM, EDGE_RIGHT, EDGE_COUNT };
+
+ explicit EdgeEffect(ui::ResourceManager* resource_manager);
+ ~EdgeEffect();
void Pull(base::TimeTicks current_time,
float delta_distance,
- float displacement) override;
- void Absorb(base::TimeTicks current_time, float velocity) override;
- bool Update(base::TimeTicks current_time) override;
- void Release(base::TimeTicks current_time) override;
+ float displacement);
+ void Absorb(base::TimeTicks current_time, float velocity);
+ bool Update(base::TimeTicks current_time);
+ void Release(base::TimeTicks current_time);
- void Finish() override;
- bool IsFinished() const override;
- float GetAlpha() const override;
+ void Finish();
+ bool IsFinished() const;
+ float GetAlpha() const;
- void ApplyToLayers(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset) override;
- void SetParent(cc::Layer* parent) override;
-
- // Thread-safe trigger to load resources.
- static void PreloadResources(ui::ResourceManager* resource_manager);
+ void ApplyToLayers(Edge edge, const gfx::SizeF& viewport_size, float offset);
+ void SetParent(cc::Layer* parent);
private:
- class EffectLayer;
- std::unique_ptr<EffectLayer> edge_;
- std::unique_ptr<EffectLayer> glow_;
+ ui::ResourceManager* const resource_manager_;
- float base_edge_height_;
- float base_glow_height_;
+ scoped_refptr<cc::UIResourceLayer> glow_;
- float edge_alpha_;
- float edge_scale_y_;
float glow_alpha_;
float glow_scale_y_;
- float edge_alpha_start_;
- float edge_alpha_finish_;
- float edge_scale_y_start_;
- float edge_scale_y_finish_;
float glow_alpha_start_;
float glow_alpha_finish_;
float glow_scale_y_start_;
float glow_scale_y_finish_;
+ gfx::RectF arc_rect_;
+ gfx::Size bounds_;
+ float displacement_;
+ float target_displacement_;
+
base::TimeTicks start_time_;
base::TimeDelta duration_;
diff --git a/chromium/ui/android/edge_effect_base.cc b/chromium/ui/android/edge_effect_base.cc
deleted file mode 100644
index 5d04175dfbf..00000000000
--- a/chromium/ui/android/edge_effect_base.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/android/edge_effect_base.h"
-
-#include "base/notreached.h"
-
-namespace ui {
-
-// static
-gfx::Transform EdgeEffectBase::ComputeTransform(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset) {
- // Transforms assume the edge layers are anchored to their *top center point*.
- switch (edge) {
- case EDGE_TOP:
- return gfx::Transform(1, 0, 0, 1, 0, offset);
- case EDGE_LEFT:
- return gfx::Transform(0, 1, -1, 0, -viewport_size.height() / 2.f + offset,
- viewport_size.height() / 2.f);
- case EDGE_BOTTOM:
- return gfx::Transform(-1, 0, 0, -1, 0, viewport_size.height() + offset);
- case EDGE_RIGHT:
- return gfx::Transform(0, -1, 1, 0, -viewport_size.height() / 2.f +
- viewport_size.width() + offset,
- viewport_size.height() / 2.f);
- default:
- NOTREACHED() << "Invalid edge: " << edge;
- return gfx::Transform();
- };
-}
-
-// static
-gfx::SizeF EdgeEffectBase::ComputeOrientedSize(
- Edge edge,
- const gfx::SizeF& viewport_size) {
- switch (edge) {
- case EDGE_TOP:
- case EDGE_BOTTOM:
- return viewport_size;
- case EDGE_LEFT:
- case EDGE_RIGHT:
- return gfx::SizeF(viewport_size.height(), viewport_size.width());
- default:
- NOTREACHED() << "Invalid edge: " << edge;
- return gfx::SizeF();
- };
-}
-
-} // namespace ui
diff --git a/chromium/ui/android/edge_effect_base.h b/chromium/ui/android/edge_effect_base.h
deleted file mode 100644
index ccd1b03eb73..00000000000
--- a/chromium/ui/android/edge_effect_base.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_ANDROID_EDGE_EFFECT_BASE_H_
-#define UI_ANDROID_EDGE_EFFECT_BASE_H_
-
-#include "base/time/time.h"
-#include "ui/gfx/geometry/size_f.h"
-#include "ui/gfx/transform.h"
-
-namespace cc {
-class Layer;
-}
-
-namespace ui {
-
-// A base class for overscroll-related Android effects.
-class EdgeEffectBase {
- public:
- enum State {
- STATE_IDLE = 0,
- STATE_PULL,
- STATE_ABSORB,
- STATE_RECEDE,
- STATE_PULL_DECAY
- };
-
- enum Edge { EDGE_TOP, EDGE_LEFT, EDGE_BOTTOM, EDGE_RIGHT, EDGE_COUNT };
-
- virtual ~EdgeEffectBase() {}
-
- virtual void Pull(base::TimeTicks current_time,
- float delta_distance,
- float displacement) = 0;
- virtual void Absorb(base::TimeTicks current_time, float velocity) = 0;
- virtual bool Update(base::TimeTicks current_time) = 0;
- virtual void Release(base::TimeTicks current_time) = 0;
-
- virtual void Finish() = 0;
- virtual bool IsFinished() const = 0;
- virtual float GetAlpha() const = 0;
-
- virtual void ApplyToLayers(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset) = 0;
- virtual void SetParent(cc::Layer* parent) = 0;
-
- protected:
- // Computes the transform for an edge effect given the |edge|, |viewport_size|
- // and edge |offset|. This assumes the the effect transform anchor is at the
- // centered edge of the effect.
- static gfx::Transform ComputeTransform(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset);
-
- // Computes the maximum effect size relative to the screen |edge|. For
- // top/bottom edges, thsi is simply |viewport_size|, while for left/right
- // edges this is |viewport_size| with coordinates swapped.
- static gfx::SizeF ComputeOrientedSize(Edge edge,
- const gfx::SizeF& viewport_size);
-};
-
-} // namespace ui
-
-#endif // UI_ANDROID_EDGE_EFFECT_BASE_H_
diff --git a/chromium/ui/android/edge_effect_l.cc b/chromium/ui/android/edge_effect_l.cc
deleted file mode 100644
index 305ca4971de..00000000000
--- a/chromium/ui/android/edge_effect_l.cc
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/android/edge_effect_l.h"
-
-#include "cc/layers/ui_resource_layer.h"
-#include "ui/android/animation_utils.h"
-#include "ui/android/resources/resource_manager.h"
-#include "ui/android/resources/system_ui_resource_type.h"
-#include "ui/android/window_android_compositor.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size_conversions.h"
-
-namespace ui {
-
-namespace {
-
-// Time it will take the effect to fully recede in ms
-const int kRecedeTimeMs = 600;
-
-// Time it will take before a pulled glow begins receding in ms
-const int kPullTimeMs = 167;
-
-// Time it will take for a pulled glow to decay to partial strength before
-// release
-const int kPullDecayTimeMs = 2000;
-
-const float kMaxAlpha = 0.5f;
-
-const float kPullGlowBegin = 0.f;
-
-// Min/max velocity that will be absorbed
-const float kMinVelocity = 100.f;
-const float kMaxVelocity = 10000.f;
-
-const float kEpsilon = 0.001f;
-
-const float kSin = 0.5f; // sin(PI / 6)
-const float kCos = 0.866f; // cos(PI / 6);
-
-// How much dragging should effect the height of the glow image.
-// Number determined by user testing.
-const float kPullDistanceAlphaGlowFactor = 0.8f;
-
-const int kVelocityGlowFactor = 6;
-
-const ui::SystemUIResourceType kResourceId = ui::OVERSCROLL_GLOW_L;
-
-} // namespace
-
-EdgeEffectL::EdgeEffectL(ui::ResourceManager* resource_manager)
- : resource_manager_(resource_manager),
- glow_(cc::UIResourceLayer::Create()),
- glow_alpha_(0),
- glow_scale_y_(0),
- glow_alpha_start_(0),
- glow_alpha_finish_(0),
- glow_scale_y_start_(0),
- glow_scale_y_finish_(0),
- displacement_(0.5f),
- target_displacement_(0.5f),
- state_(STATE_IDLE),
- pull_distance_(0) {
- // Prevent the provided layers from drawing until the effect is activated.
- glow_->SetIsDrawable(false);
-}
-
-EdgeEffectL::~EdgeEffectL() {
- glow_->RemoveFromParent();
-}
-
-bool EdgeEffectL::IsFinished() const {
- return state_ == STATE_IDLE;
-}
-
-void EdgeEffectL::Finish() {
- glow_->SetIsDrawable(false);
- pull_distance_ = 0;
- state_ = STATE_IDLE;
-}
-
-void EdgeEffectL::Pull(base::TimeTicks current_time,
- float delta_distance,
- float displacement) {
- target_displacement_ = displacement;
- if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
- return;
- }
- if (state_ != STATE_PULL) {
- glow_scale_y_ = std::max(kPullGlowBegin, glow_scale_y_);
- }
- state_ = STATE_PULL;
-
- start_time_ = current_time;
- duration_ = base::TimeDelta::FromMilliseconds(kPullTimeMs);
-
- float abs_delta_distance = std::abs(delta_distance);
- pull_distance_ += delta_distance;
-
- glow_alpha_ = glow_alpha_start_ = std::min(
- kMaxAlpha,
- glow_alpha_ + (abs_delta_distance * kPullDistanceAlphaGlowFactor));
-
- if (pull_distance_ == 0) {
- glow_scale_y_ = glow_scale_y_start_ = 0;
- } else {
- float scale = 1.f -
- 1.f / std::sqrt(std::abs(pull_distance_) * bounds_.height()) -
- 0.3f;
- glow_scale_y_ = glow_scale_y_start_ = std::max(0.f, scale) / 0.7f;
- }
-
- glow_alpha_finish_ = glow_alpha_;
- glow_scale_y_finish_ = glow_scale_y_;
-}
-
-void EdgeEffectL::Release(base::TimeTicks current_time) {
- pull_distance_ = 0;
-
- if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
- return;
-
- state_ = STATE_RECEDE;
- glow_alpha_start_ = glow_alpha_;
- glow_scale_y_start_ = glow_scale_y_;
-
- glow_alpha_finish_ = 0.f;
- glow_scale_y_finish_ = 0.f;
-
- start_time_ = current_time;
- duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs);
-}
-
-void EdgeEffectL::Absorb(base::TimeTicks current_time, float velocity) {
- state_ = STATE_ABSORB;
-
- velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity);
-
- start_time_ = current_time;
- // This should never be less than 1 millisecond.
- duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f));
-
- // The glow depends more on the velocity, and therefore starts out
- // nearly invisible.
- glow_alpha_start_ = 0.3f;
- glow_scale_y_start_ = std::max(glow_scale_y_, 0.f);
-
- // Growth for the size of the glow should be quadratic to properly respond
- // to a user's scrolling speed. The faster the scrolling speed, the more
- // intense the effect should be for both the size and the saturation.
- glow_scale_y_finish_ =
- std::min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2.f, 1.f);
- // Alpha should change for the glow as well as size.
- glow_alpha_finish_ = Clamp(
- glow_alpha_start_, velocity * kVelocityGlowFactor * .00001f, kMaxAlpha);
- target_displacement_ = 0.5;
-}
-
-bool EdgeEffectL::Update(base::TimeTicks current_time) {
- if (IsFinished())
- return false;
-
- const double dt = (current_time - start_time_).InMilliseconds();
- const double t = std::min(dt / duration_.InMilliseconds(), 1.);
- const float interp = static_cast<float>(Damp(t, 1.));
-
- glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
- glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
- displacement_ = (displacement_ + target_displacement_) / 2.f;
-
- if (t >= 1.f - kEpsilon) {
- switch (state_) {
- case STATE_ABSORB:
- state_ = STATE_RECEDE;
- start_time_ = current_time;
- duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs);
-
- glow_alpha_start_ = glow_alpha_;
- glow_scale_y_start_ = glow_scale_y_;
-
- glow_alpha_finish_ = 0.f;
- glow_scale_y_finish_ = 0.f;
- break;
- case STATE_PULL:
- state_ = STATE_PULL_DECAY;
- start_time_ = current_time;
- duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTimeMs);
-
- glow_alpha_start_ = glow_alpha_;
- glow_scale_y_start_ = glow_scale_y_;
-
- // After pull, the glow should fade to nothing.
- glow_alpha_finish_ = 0.f;
- glow_scale_y_finish_ = 0.f;
- break;
- case STATE_PULL_DECAY:
- state_ = STATE_RECEDE;
- break;
- case STATE_RECEDE:
- Finish();
- break;
- default:
- break;
- }
- }
-
- bool one_last_frame = false;
- if (state_ == STATE_RECEDE && glow_scale_y_ <= 0) {
- Finish();
- one_last_frame = true;
- }
-
- return !IsFinished() || one_last_frame;
-}
-
-float EdgeEffectL::GetAlpha() const {
- return IsFinished() ? 0.f : glow_alpha_;
-}
-
-void EdgeEffectL::ApplyToLayers(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset) {
- if (IsFinished())
- return;
-
- // An empty viewport, while meaningless, is also relatively harmless, and will
- // simply prevent any drawing of the layers.
- if (viewport_size.IsEmpty()) {
- glow_->SetIsDrawable(false);
- return;
- }
-
- gfx::SizeF size = ComputeOrientedSize(edge, viewport_size);
- const float r = size.width() * 0.75f / kSin;
- const float y = kCos * r;
- const float h = r - y;
- const float o_r = size.height() * 0.75f / kSin;
- const float o_y = kCos * o_r;
- const float o_h = o_r - o_y;
- const float base_glow_scale = h > 0.f ? std::min(o_h / h, 1.f) : 1.f;
- bounds_ = gfx::Size(size.width(), (int)std::min(size.height(), h));
- gfx::Size image_bounds(
- r, std::min(1.f, glow_scale_y_) * base_glow_scale * bounds_.height());
-
- // Compute the displaced image rect. This includes both the horizontal
- // offset from the |displacement_| factor, as well as the vertical edge offset
- // provided by the method call.
- const float displacement = Clamp(displacement_, 0.f, 1.f) - 0.5f;
- const float displacement_offset_x = bounds_.width() * displacement * 0.5f;
- const float image_offset_x = (bounds_.width() - image_bounds.width()) * 0.5f;
- gfx::RectF image_rect = gfx::RectF(gfx::SizeF(image_bounds));
- image_rect.Offset(image_offset_x - displacement_offset_x, -std::abs(offset));
-
- // Clip the image rect against the viewport. If either rect is empty there's
- // no need to draw anything further.
- gfx::RectF clipped_rect(size.width(), size.height());
- clipped_rect.Intersect(image_rect);
- if (clipped_rect.IsEmpty() || image_rect.IsEmpty()) {
- glow_->SetIsDrawable(false);
- return;
- }
-
- // Compute the logical UV coordinates of the clipped rect relative to the
- // displaced image rect.
- gfx::PointF clipped_top_left = clipped_rect.origin();
- gfx::PointF clipped_bottom_right = clipped_rect.bottom_right();
- gfx::PointF uv_top_left(
- (clipped_top_left.x() - image_rect.x()) / image_rect.width(),
- (clipped_top_left.y() - image_rect.y()) / image_rect.height());
- gfx::PointF uv_bottom_right(
- (clipped_bottom_right.x() - image_rect.x()) / image_rect.width(),
- (clipped_bottom_right.y() - image_rect.y()) / image_rect.height());
- glow_->SetUV(uv_top_left, uv_bottom_right);
-
- // There's no need to use the provided |offset| when computing the transform;
- // the offset is built in to the computed UV coordinates.
- glow_->SetTransform(ComputeTransform(edge, viewport_size, 0));
-
- glow_->SetIsDrawable(true);
- glow_->SetUIResourceId(resource_manager_->GetUIResourceId(
- ui::ANDROID_RESOURCE_TYPE_SYSTEM, kResourceId));
- glow_->SetTransformOrigin(gfx::Point3F(bounds_.width() * 0.5f, 0, 0));
- glow_->SetBounds(gfx::ToRoundedSize(clipped_rect.size()));
- glow_->SetContentsOpaque(false);
- glow_->SetOpacity(Clamp(glow_alpha_, 0.f, 1.f));
-}
-
-void EdgeEffectL::SetParent(cc::Layer* parent) {
- if (glow_->parent() != parent)
- parent->AddChild(glow_);
-}
-
-// static
-void EdgeEffectL::PreloadResources(ui::ResourceManager* resource_manager) {
- DCHECK(resource_manager);
- resource_manager->PreloadResource(ui::ANDROID_RESOURCE_TYPE_SYSTEM,
- kResourceId);
-}
-
-} // namespace ui
diff --git a/chromium/ui/android/edge_effect_l.h b/chromium/ui/android/edge_effect_l.h
deleted file mode 100644
index 4f18fef1569..00000000000
--- a/chromium/ui/android/edge_effect_l.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_ANDROID_EDGE_EFFECT_L_H_
-#define UI_ANDROID_EDGE_EFFECT_L_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "ui/android/edge_effect_base.h"
-#include "ui/android/ui_android_export.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace cc {
-class Layer;
-class UIResourceLayer;
-}
-
-namespace ui {
-class ResourceManager;
-}
-
-namespace ui {
-
-// |EdgeEffectL| mirrors its Android L counterpart, EdgeEffect.java.
-// Conscious tradeoffs were made to align this as closely as possible with the
-// the original Android java version.
-// All coordinates and dimensions are in device pixels.
-class UI_ANDROID_EXPORT EdgeEffectL : public EdgeEffectBase {
- public:
- explicit EdgeEffectL(ui::ResourceManager* resource_manager);
- ~EdgeEffectL() override;
-
- void Pull(base::TimeTicks current_time,
- float delta_distance,
- float displacement) override;
- void Absorb(base::TimeTicks current_time, float velocity) override;
- bool Update(base::TimeTicks current_time) override;
- void Release(base::TimeTicks current_time) override;
-
- void Finish() override;
- bool IsFinished() const override;
- float GetAlpha() const override;
-
- void ApplyToLayers(Edge edge,
- const gfx::SizeF& viewport_size,
- float offset) override;
- void SetParent(cc::Layer* parent) override;
-
- // Thread-safe trigger to load resources.
- static void PreloadResources(ui::ResourceManager* resource_manager);
-
- private:
- ui::ResourceManager* const resource_manager_;
-
- scoped_refptr<cc::UIResourceLayer> glow_;
-
- float glow_alpha_;
- float glow_scale_y_;
-
- float glow_alpha_start_;
- float glow_alpha_finish_;
- float glow_scale_y_start_;
- float glow_scale_y_finish_;
-
- gfx::RectF arc_rect_;
- gfx::Size bounds_;
- float displacement_;
- float target_displacement_;
-
- base::TimeTicks start_time_;
- base::TimeDelta duration_;
-
- State state_;
-
- float pull_distance_;
-
- DISALLOW_COPY_AND_ASSIGN(EdgeEffectL);
-};
-
-} // namespace ui
-
-#endif // UI_ANDROID_EDGE_EFFECT_L_H_
diff --git a/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java
index 37e04ef6b85..fc11571c30c 100644
--- a/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java
+++ b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java
@@ -23,6 +23,7 @@ import org.robolectric.shadows.ShadowLooper;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.test.ui.R;
import org.chromium.ui.shadows.ShadowAsyncLayoutInflater;
import java.util.concurrent.atomic.AtomicInteger;
@@ -37,12 +38,11 @@ public class AsyncViewProviderTest {
private AsyncViewStub mAsyncViewStub;
private AsyncViewProvider<View> mAsyncViewProvider;
private final AtomicInteger mEventCount = new AtomicInteger();
- private static final int MAIN_LAYOUT_RESOURCE_ID = org.chromium.test.ui.R.layout.main_view;
- private static final int INFLATE_LAYOUT_RESOURCE_ID =
- org.chromium.test.ui.R.layout.inflated_view;
- private static final int STUB_ID = org.chromium.test.ui.R.id.view_stub;
- private static final int INFLATED_VIEW_ID = org.chromium.test.ui.R.id.inflated_view;
- private static final int PREINFLATED_VIEW_ID = org.chromium.test.ui.R.id.pre_inflated_view;
+ private static final int MAIN_LAYOUT_RESOURCE_ID = R.layout.main_view;
+ private static final int INFLATE_LAYOUT_RESOURCE_ID = R.layout.inflated_view;
+ private static final int STUB_ID = R.id.view_stub;
+ private static final int INFLATED_VIEW_ID = R.id.inflated_view;
+ private static final int PREINFLATED_VIEW_ID = R.id.pre_inflated_view;
@Before
public void setUp() {
@@ -128,4 +128,4 @@ public class AsyncViewProviderTest {
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
assertEquals(mEventCount.get(), 1);
}
-} \ No newline at end of file
+}
diff --git a/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java b/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
index 403b8a8e06c..22e430bd774 100644
--- a/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
+++ b/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java
@@ -7,8 +7,12 @@ package org.chromium.ui.base;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Intent;
import android.net.Uri;
import android.text.SpannableString;
@@ -16,6 +20,8 @@ import android.text.style.RelativeSizeSpan;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.robolectric.annotation.Config;
import org.chromium.base.ContentUriUtils;
@@ -98,4 +104,35 @@ public class ClipboardTest {
assertEquals(imageUri, clipboard.getImageUri());
}
+
+ @Test
+ public void testClipboardCopyUrlToClipboard() {
+ Clipboard clipboard = Clipboard.getInstance();
+ ClipboardManager clipboardManager = Mockito.mock(ClipboardManager.class);
+ clipboard.overrideClipboardManagerForTesting(clipboardManager);
+
+ String url = "https://google.com";
+ clipboard.copyUrlToClipboard(url);
+
+ ArgumentCaptor<ClipData> clipCaptor = ArgumentCaptor.forClass(ClipData.class);
+ verify(clipboardManager).setPrimaryClip(clipCaptor.capture());
+ assertEquals("url", clipCaptor.getValue().getDescription().getLabel());
+ assertEquals(url, clipCaptor.getValue().getItemAt(0).getText());
+ }
+
+ @Test
+ public void testClipboardCopyUrlToClipboardNoException() {
+ Clipboard clipboard = Clipboard.getInstance();
+ ClipboardManager clipboardManager = Mockito.mock(ClipboardManager.class);
+ clipboard.overrideClipboardManagerForTesting(clipboardManager);
+
+ doThrow(SecurityException.class).when(clipboardManager).setPrimaryClip(any(ClipData.class));
+ String url = "https://google.com";
+ clipboard.copyUrlToClipboard(url);
+
+ ArgumentCaptor<ClipData> clipCaptor = ArgumentCaptor.forClass(ClipData.class);
+ verify(clipboardManager).setPrimaryClip(clipCaptor.capture());
+ assertEquals("url", clipCaptor.getValue().getDescription().getLabel());
+ assertEquals(url, clipCaptor.getValue().getItemAt(0).getText());
+ }
}
diff --git a/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java b/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java
index bbbbd3a6575..632a93ed9b6 100644
--- a/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java
+++ b/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java
@@ -179,40 +179,15 @@ public class SpanApplierTest {
/*
* Tests the attributes of two SpannableStrings and asserts expected equality.
- *
- * Prior to KitKat, there was no equals method for SpannableString, so we have to
- * manually check that the objects are the same.
*/
private void assertSpannableStringEquality(
SpannableString expected, SpannableString actual) {
- if (!areSpannableStringsEqual(expected, actual)) {
+ if (!expected.equals(actual)) {
Assert.fail("Expected string is " + getSpannableStringDescription(expected)
+ " Actual string is " + getSpannableStringDescription(actual));
}
}
- private boolean areSpannableStringsEqual(SpannableString expected, SpannableString actual) {
- Object[] expectedSpans = expected.getSpans(0, expected.length(), Object.class);
- Object[] actualSpans = actual.getSpans(0, actual.length(), Object.class);
-
- if (!expected.toString().equals(actual.toString())
- || expectedSpans.length != actualSpans.length) {
- return false;
- }
-
- for (int i = 0; i < expectedSpans.length; i++) {
- Object expectedSpan = expectedSpans[i];
- Object actualSpan = actualSpans[i];
- if (expectedSpan != actualSpan
- || expected.getSpanStart(expectedSpan) != actual.getSpanStart(actualSpan)
- || expected.getSpanEnd(expectedSpan) != actual.getSpanEnd(actualSpan)) {
- return false;
- }
- }
-
- return true;
- }
-
private String getSpannableStringDescription(SpannableString spannableString) {
Object[] spans = spannableString.getSpans(0, spannableString.length(), Object.class);
StringBuilder description = new StringBuilder();
diff --git a/chromium/ui/android/overscroll_glow.cc b/chromium/ui/android/overscroll_glow.cc
index 0470d11ad17..b20b433bb63 100644
--- a/chromium/ui/android/overscroll_glow.cc
+++ b/chromium/ui/android/overscroll_glow.cc
@@ -7,7 +7,7 @@
#include <stddef.h>
#include "cc/layers/layer.h"
-#include "ui/android/edge_effect_base.h"
+#include "ui/android/edge_effect.h"
#include "ui/android/window_android_compositor.h"
using std::max;
@@ -128,7 +128,7 @@ bool OverscrollGlow::Animate(base::TimeTicks current_time,
for (size_t i = 0; i < EDGE_COUNT; ++i) {
if (edge_effects_[i]->Update(current_time)) {
- EdgeEffectBase::Edge edge = static_cast<EdgeEffectBase::Edge>(i);
+ EdgeEffect::Edge edge = static_cast<EdgeEffect::Edge>(i);
edge_effects_[i]->ApplyToLayers(edge, viewport_size_, edge_offsets_[i]);
}
}
@@ -141,12 +141,12 @@ void OverscrollGlow::OnFrameUpdated(
const gfx::SizeF& content_size,
const gfx::Vector2dF& content_scroll_offset) {
viewport_size_ = viewport_size;
- edge_offsets_[EdgeEffectBase::EDGE_TOP] = -content_scroll_offset.y();
- edge_offsets_[EdgeEffectBase::EDGE_LEFT] = -content_scroll_offset.x();
- edge_offsets_[EdgeEffectBase::EDGE_BOTTOM] = content_size.height() -
- content_scroll_offset.y() -
- viewport_size.height();
- edge_offsets_[EdgeEffectBase::EDGE_RIGHT] =
+ edge_offsets_[EdgeEffect::EDGE_TOP] = -content_scroll_offset.y();
+ edge_offsets_[EdgeEffect::EDGE_LEFT] = -content_scroll_offset.x();
+ edge_offsets_[EdgeEffect::EDGE_BOTTOM] = content_size.height() -
+ content_scroll_offset.y() -
+ viewport_size.height();
+ edge_offsets_[EdgeEffect::EDGE_RIGHT] =
content_size.width() - content_scroll_offset.x() - viewport_size.width();
// Only allow overscroll on scrollable axes, matching platform behavior.
@@ -273,7 +273,7 @@ void OverscrollGlow::Release(base::TimeTicks current_time) {
edge_effects_[i]->Release(current_time);
}
-EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
+EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) {
DCHECK(initialized_);
return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
}
diff --git a/chromium/ui/android/overscroll_glow.h b/chromium/ui/android/overscroll_glow.h
index cdefeb9da6c..e00d4fc1eee 100644
--- a/chromium/ui/android/overscroll_glow.h
+++ b/chromium/ui/android/overscroll_glow.h
@@ -10,7 +10,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
-#include "ui/android/edge_effect_base.h"
+#include "ui/android/edge_effect.h"
#include "ui/android/ui_android_export.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
@@ -27,7 +27,7 @@ class UI_ANDROID_EXPORT OverscrollGlowClient {
virtual ~OverscrollGlowClient() {}
// Called lazily, after the initial overscrolling event.
- virtual std::unique_ptr<EdgeEffectBase> CreateEdgeEffect() = 0;
+ virtual std::unique_ptr<EdgeEffect> CreateEdgeEffect() = 0;
};
/* |OverscrollGlow| mirrors its Android counterpart, OverscrollGlow.java.
@@ -77,7 +77,7 @@ class UI_ANDROID_EXPORT OverscrollGlow {
private:
enum Axis { AXIS_X, AXIS_Y };
- enum { EDGE_COUNT = EdgeEffectBase::EDGE_COUNT };
+ enum { EDGE_COUNT = EdgeEffect::EDGE_COUNT };
// Returns whether the effect has been properly initialized.
bool InitializeIfNecessary();
@@ -93,10 +93,10 @@ class UI_ANDROID_EXPORT OverscrollGlow {
bool y_overscroll_started);
void Release(base::TimeTicks current_time);
- EdgeEffectBase* GetOppositeEdge(int edge_index);
+ EdgeEffect* GetOppositeEdge(int edge_index);
OverscrollGlowClient* const client_;
- std::unique_ptr<EdgeEffectBase> edge_effects_[EDGE_COUNT];
+ std::unique_ptr<EdgeEffect> edge_effects_[EDGE_COUNT];
gfx::SizeF viewport_size_;
float edge_offsets_[EDGE_COUNT];
diff --git a/chromium/ui/android/overscroll_refresh.cc b/chromium/ui/android/overscroll_refresh.cc
index 14a9bf2b3ac..1192c2d1399 100644
--- a/chromium/ui/android/overscroll_refresh.cc
+++ b/chromium/ui/android/overscroll_refresh.cc
@@ -17,10 +17,6 @@ namespace {
// release results in a small upward fling (quite common during a slow scroll).
const float kMinFlingVelocityForActivation = -500.f;
-// The default distance in dp from a side of the device to start a navigation
-// from.
-const float kNavigationEdgeWidth = 48.f;
-
// Weighted value used to determine whether a scroll should trigger vertical
// scroll or horizontal navigation.
const float kWeightAngle30 = 1.73f;
@@ -28,12 +24,12 @@ const float kWeightAngle30 = 1.73f;
} // namespace
OverscrollRefresh::OverscrollRefresh(OverscrollRefreshHandler* handler,
- float dpi_scale)
+ float edge_width)
: scrolled_to_top_(true),
top_at_scroll_start_(true),
overflow_y_hidden_(false),
scroll_consumption_state_(DISABLED),
- edge_width_(kNavigationEdgeWidth * dpi_scale),
+ edge_width_(edge_width),
handler_(handler) {
DCHECK(handler);
}
@@ -42,7 +38,7 @@ OverscrollRefresh::OverscrollRefresh()
: scrolled_to_top_(true),
overflow_y_hidden_(false),
scroll_consumption_state_(DISABLED),
- edge_width_(kNavigationEdgeWidth * 1.f),
+ edge_width_(kDefaultNavigationEdgeWidth * 1.f),
handler_(nullptr) {}
OverscrollRefresh::~OverscrollRefresh() {
diff --git a/chromium/ui/android/overscroll_refresh.h b/chromium/ui/android/overscroll_refresh.h
index b650d46c012..57c9c7a2356 100644
--- a/chromium/ui/android/overscroll_refresh.h
+++ b/chromium/ui/android/overscroll_refresh.h
@@ -36,12 +36,11 @@ class OverscrollRefreshHandler;
// provided refresh handler.
class UI_ANDROID_EXPORT OverscrollRefresh {
public:
- // Minmum number of overscrolling pull events required to activate the effect.
- // Useful for avoiding accidental triggering when a scroll janks (is delayed),
- // capping the impulse per event.
- enum { kMinPullsToActivate = 3 };
+ // The default distance in dp from a side of the device to start a navigation
+ // from.
+ enum { kDefaultNavigationEdgeWidth = 24 };
- OverscrollRefresh(OverscrollRefreshHandler* handler, float dpi_scale);
+ OverscrollRefresh(OverscrollRefreshHandler* handler, float edge_width);
virtual ~OverscrollRefresh();
@@ -105,7 +104,7 @@ class UI_ANDROID_EXPORT OverscrollRefresh {
float viewport_width_;
float scroll_begin_x_;
float scroll_begin_y_;
- const float edge_width_;
+ const float edge_width_; // in px
gfx::Vector2dF cumulative_scroll_;
OverscrollRefreshHandler* const handler_;
diff --git a/chromium/ui/android/overscroll_refresh_unittest.cc b/chromium/ui/android/overscroll_refresh_unittest.cc
index fb446860ee8..c683c4b3dc5 100644
--- a/chromium/ui/android/overscroll_refresh_unittest.cc
+++ b/chromium/ui/android/overscroll_refresh_unittest.cc
@@ -13,6 +13,8 @@ namespace ui {
const float kDipScale = 1.f;
const gfx::PointF kStartPos(2.f, 2.f);
+const float kDefaultEdgeWidth =
+ OverscrollRefresh::kDefaultNavigationEdgeWidth * kDipScale;
class OverscrollRefreshTest : public OverscrollRefreshHandler,
public testing::Test {
@@ -70,7 +72,7 @@ class OverscrollRefreshTest : public OverscrollRefreshHandler,
void TestOverscrollBehavior(const cc::OverscrollBehavior& ob,
const gfx::Vector2dF& scroll_delta,
bool started) {
- OverscrollRefresh effect(this, 1.f);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_delta));
EXPECT_FALSE(effect.IsActive());
@@ -89,7 +91,7 @@ class OverscrollRefreshTest : public OverscrollRefreshHandler,
};
TEST_F(OverscrollRefreshTest, Basic) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
EXPECT_FALSE(effect.IsActive());
EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
@@ -131,7 +133,7 @@ TEST_F(OverscrollRefreshTest, Basic) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
// A positive y scroll offset at the start of scroll will prevent activation,
// even if the subsequent scroll overscrolls upward.
@@ -155,7 +157,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
// overflow-y:hidden at the start of scroll will prevent activation.
gfx::Vector2dF zero_offset;
@@ -177,7 +179,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
// A downward initial scroll will prevent activation, even if the subsequent
@@ -195,7 +197,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
@@ -217,7 +219,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
@@ -232,7 +234,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
@@ -248,7 +250,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) {
}
TEST_F(OverscrollRefreshTest, NotTriggeredIfReset) {
- OverscrollRefresh effect(this, kDipScale);
+ OverscrollRefresh effect(this, kDefaultEdgeWidth);
effect.OnScrollBegin(kStartPos);
ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
diff --git a/chromium/ui/android/resources/resource_manager.h b/chromium/ui/android/resources/resource_manager.h
index a8852893364..27b2e28d014 100644
--- a/chromium/ui/android/resources/resource_manager.h
+++ b/chromium/ui/android/resources/resource_manager.h
@@ -49,11 +49,6 @@ class UI_ANDROID_EXPORT ResourceManager {
virtual Resource* GetStaticResourceWithTint(int res_id,
SkColor tint_color) = 0;
- // Remove tints that were unused in the current frame being built. This
- // function takes a set |used_tints| and removes all the tints not in the set
- // from the cache.
- virtual void RemoveUnusedTints(const std::unordered_set<int>& used_tints) = 0;
-
// Trigger asynchronous loading of the resource specified by |res_type| and
// |res_id|, if it has not yet been loaded.
virtual void PreloadResource(AndroidResourceType res_type, int res_id) = 0;
@@ -65,6 +60,9 @@ class UI_ANDROID_EXPORT ResourceManager {
: 0;
}
+ // A notification that all updates have finished for the current frame.
+ virtual void OnFrameUpdatesFinished() = 0;
+
protected:
virtual ~ResourceManager() {}
};
diff --git a/chromium/ui/android/resources/resource_manager_impl.cc b/chromium/ui/android/resources/resource_manager_impl.cc
index 586b0a549a9..db7843b1efb 100644
--- a/chromium/ui/android/resources/resource_manager_impl.cc
+++ b/chromium/ui/android/resources/resource_manager_impl.cc
@@ -117,24 +117,30 @@ Resource* ResourceManagerImpl::GetResource(AndroidResourceType res_type,
return item->second.get();
}
-void ResourceManagerImpl::RemoveUnusedTints(
- const std::unordered_set<int>& used_tints) {
+void ResourceManagerImpl::RemoveUnusedTints() {
// Iterate over the currently cached tints and remove ones that were not
// used as defined in |used_tints|.
for (auto it = tinted_resources_.cbegin(); it != tinted_resources_.cend();) {
- if (used_tints.find(it->first) == used_tints.end()) {
- it = tinted_resources_.erase(it);
+ if (used_tints_.find(it->first) == used_tints_.end()) {
+ it = tinted_resources_.erase(it);
} else {
- ++it;
+ ++it;
}
}
}
+void ResourceManagerImpl::OnFrameUpdatesFinished() {
+ RemoveUnusedTints();
+ used_tints_.clear();
+}
+
Resource* ResourceManagerImpl::GetStaticResourceWithTint(int res_id,
SkColor tint_color) {
if (tinted_resources_.find(tint_color) == tinted_resources_.end()) {
tinted_resources_[tint_color] = std::make_unique<ResourceMap>();
}
+
+ used_tints_.insert(tint_color);
ResourceMap* resource_map = tinted_resources_[tint_color].get();
// If the resource is already cached, use it.
diff --git a/chromium/ui/android/resources/resource_manager_impl.h b/chromium/ui/android/resources/resource_manager_impl.h
index 44a0827fe81..a737d566d9a 100644
--- a/chromium/ui/android/resources/resource_manager_impl.h
+++ b/chromium/ui/android/resources/resource_manager_impl.h
@@ -39,8 +39,8 @@ class UI_ANDROID_EXPORT ResourceManagerImpl
Resource* GetResource(AndroidResourceType res_type, int res_id) override;
Resource* GetStaticResourceWithTint(
int res_id, SkColor tint_color) override;
- void RemoveUnusedTints(const std::unordered_set<int>& used_tints) override;
void PreloadResource(AndroidResourceType res_type, int res_id) override;
+ void OnFrameUpdatesFinished() override;
// Called from Java
// ----------------------------------------------------------
@@ -73,6 +73,11 @@ class UI_ANDROID_EXPORT ResourceManagerImpl
virtual void RequestResourceFromJava(AndroidResourceType res_type,
int res_id);
+ // Remove tints that were unused in the current frame being built. This
+ // function takes a set |used_tints| and removes all the tints not in the set
+ // from the cache.
+ void RemoveUnusedTints();
+
using ResourceMap = std::unordered_map<int, std::unique_ptr<Resource>>;
using TintedResourceMap =
std::unordered_map<SkColor, std::unique_ptr<ResourceMap>>;
@@ -81,6 +86,9 @@ class UI_ANDROID_EXPORT ResourceManagerImpl
ResourceMap resources_[ANDROID_RESOURCE_TYPE_COUNT];
TintedResourceMap tinted_resources_;
+ // The set of tints that are used for resources in the current frame.
+ std::unordered_set<int> used_tints_;
+
base::android::ScopedJavaGlobalRef<jobject> java_obj_;
DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl);
diff --git a/chromium/ui/android/resources/system_ui_resource_type.h b/chromium/ui/android/resources/system_ui_resource_type.h
index dad9b05f9cb..170fb17887e 100644
--- a/chromium/ui/android/resources/system_ui_resource_type.h
+++ b/chromium/ui/android/resources/system_ui_resource_type.h
@@ -10,9 +10,7 @@ namespace ui {
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.resources
enum SystemUIResourceType {
- OVERSCROLL_EDGE,
OVERSCROLL_GLOW,
- OVERSCROLL_GLOW_L,
};
} // namespace ui
diff --git a/chromium/ui/android/run_all_unittests.cc b/chromium/ui/android/run_all_unittests.cc
index 32ea6ca2cda..ee920901bd4 100644
--- a/chromium/ui/android/run_all_unittests.cc
+++ b/chromium/ui/android/run_all_unittests.cc
@@ -5,8 +5,8 @@
#include "base/android/jni_android.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/path_service.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,9 +25,7 @@ class UIAndroidTestSuite : public base::TestSuite {
ui::RegisterPathProvider();
- base::FilePath ui_test_pak_path;
- ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
- ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+ ui::ResourceBundle::InitSharedInstanceWithPakPath(base::FilePath());
}
void Shutdown() override {
diff --git a/chromium/ui/android/view_android.cc b/chromium/ui/android/view_android.cc
index 9de5d840506..bb6d1852c35 100644
--- a/chromium/ui/android/view_android.cc
+++ b/chromium/ui/android/view_android.cc
@@ -143,6 +143,8 @@ void ViewAndroid::AddChild(ViewAndroid* child) {
if (!physical_size_.IsEmpty())
child->OnPhysicalBackingSizeChanged(physical_size_);
+ child->OnControlsResizeViewChanged(controls_resize_view_);
+
// Empty view size also need not propagating down in order to prevent
// spurious events with empty size from being sent down.
if (child->match_parent() && !bounds_.IsEmpty() &&
@@ -509,15 +511,32 @@ void ViewAndroid::DispatchOnSizeChanged() {
}
}
-void ViewAndroid::OnPhysicalBackingSizeChanged(const gfx::Size& size) {
+void ViewAndroid::OnPhysicalBackingSizeChanged(
+ const gfx::Size& size,
+ base::Optional<base::TimeDelta> deadline_override) {
if (physical_size_ == size)
return;
physical_size_ = size;
if (event_handler_)
- event_handler_->OnPhysicalBackingSizeChanged();
+ event_handler_->OnPhysicalBackingSizeChanged(deadline_override);
for (auto* child : children_)
- child->OnPhysicalBackingSizeChanged(size);
+ child->OnPhysicalBackingSizeChanged(size, deadline_override);
+}
+
+void ViewAndroid::OnControlsResizeViewChanged(bool controls_resize_view) {
+ if (controls_resize_view == controls_resize_view_)
+ return;
+ controls_resize_view_ = controls_resize_view;
+ if (event_handler_)
+ event_handler_->OnControlsResizeViewChanged();
+
+ for (auto* child : children_)
+ child->OnControlsResizeViewChanged(controls_resize_view);
+}
+
+bool ViewAndroid::ControlsResizeView() {
+ return controls_resize_view_;
}
gfx::Size ViewAndroid::GetPhysicalBackingSize() const {
diff --git a/chromium/ui/android/view_android.h b/chromium/ui/android/view_android.h
index 5c989d6b05e..680f4634e8f 100644
--- a/chromium/ui/android/view_android.h
+++ b/chromium/ui/android/view_android.h
@@ -14,6 +14,8 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
+#include "base/optional.h"
+#include "base/time/time.h"
#include "ui/android/ui_android_export.h"
#include "ui/android/view_android_observer.h"
#include "ui/gfx/geometry/rect_f.h"
@@ -152,7 +154,11 @@ class UI_ANDROID_EXPORT ViewAndroid {
gfx::Rect bounds() const { return bounds_; }
void OnSizeChanged(int width, int height);
- void OnPhysicalBackingSizeChanged(const gfx::Size& size);
+ // |deadline_override| if not nullopt will be used as the cc::DeadlinePolicy
+ // timeout for this resize.
+ void OnPhysicalBackingSizeChanged(
+ const gfx::Size& size,
+ base::Optional<base::TimeDelta> deadline_override = base::nullopt);
void OnCursorChanged(const Cursor& cursor);
void OnBackgroundColorChanged(unsigned int color);
void OnTopControlsChanged(float top_controls_offset,
@@ -166,6 +172,8 @@ class UI_ANDROID_EXPORT ViewAndroid {
// if page is not scrollable, though this should not be called in that case.
void OnVerticalScrollDirectionChanged(bool direction_up,
float current_scroll_ratio);
+ void OnControlsResizeViewChanged(bool controls_resize_view);
+ bool ControlsResizeView();
// Gets the Visual Viewport inset to apply in physical pixels.
int GetViewportInsetBottom();
@@ -300,6 +308,8 @@ class UI_ANDROID_EXPORT ViewAndroid {
// Copy output of View rather than window.
CopyViewCallback copy_view_callback_;
+ bool controls_resize_view_ = false;
+
DISALLOW_COPY_AND_ASSIGN(ViewAndroid);
};
diff --git a/chromium/ui/android/window_android.cc b/chromium/ui/android/window_android.cc
index 8f8e75ff09e..b1aa28f4428 100644
--- a/chromium/ui/android/window_android.cc
+++ b/chromium/ui/android/window_android.cc
@@ -120,7 +120,7 @@ void WindowAndroid::AttachCompositor(WindowAndroidCompositor* compositor) {
}
void WindowAndroid::DetachCompositor() {
- compositor_ = NULL;
+ compositor_ = nullptr;
for (WindowAndroidObserver& observer : observer_list_)
observer.OnDetachCompositor();
observer_list_.Clear();
diff --git a/chromium/ui/aura/BUILD.gn b/chromium/ui/aura/BUILD.gn
index fd917270bb3..0b7516f4ee7 100644
--- a/chromium/ui/aura/BUILD.gn
+++ b/chromium/ui/aura/BUILD.gn
@@ -2,11 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
-jumbo_component("aura") {
+component("aura") {
public = [
"client/aura_constants.h",
"client/capture_client.h",
@@ -136,6 +135,7 @@ jumbo_component("aura") {
public_deps = [
"//ui/base/cursor:cursor_base",
+ "//ui/base/dragdrop/mojom:mojom_headers",
"//ui/base/ime",
"//ui/compositor",
]
@@ -174,7 +174,7 @@ jumbo_component("aura") {
configs += [ "//build/config/compiler:noshadowing" ]
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"test/aura_test_base.cc",
@@ -247,9 +247,14 @@ jumbo_static_library("test_support") {
sources += [ "test/ui_controls_factory_aurawin.cc" ]
}
+ if (is_linux) {
+ sources += [ "test/ui_controls_factory_aura_linux.cc" ]
+ }
+
if (use_x11) {
sources += [
- "test/ui_controls_factory_aurax11.cc",
+ "test/ui_controls_aurax11.cc",
+ "test/ui_controls_aurax11.h",
"test/x11_event_sender.cc",
"test/x11_event_sender.h",
]
@@ -264,10 +269,13 @@ jumbo_static_library("test_support") {
if (is_fuchsia) {
deps += [ "//ui/ozone" ]
}
- sources += [ "test/ui_controls_factory_ozone.cc" ]
+ sources += [
+ "test/ui_controls_ozone.cc",
+ "test/ui_controls_ozone.h",
+ ]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//ui/platform_window/common" ]
}
}
@@ -306,6 +314,10 @@ executable("aura_demo") {
}
test("aura_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [
"../compositor_extra/shadow_unittest.cc",
"gestures/gesture_recognizer_unittest.cc",
diff --git a/chromium/ui/aura/DEPS b/chromium/ui/aura/DEPS
index 06cfc0080e1..5c4046ee903 100644
--- a/chromium/ui/aura/DEPS
+++ b/chromium/ui/aura/DEPS
@@ -21,6 +21,7 @@ include_rules = [
"+ui/gl/test",
"+ui/ozone/public",
"+ui/platform_window",
+ "+ui/base/ui_base_features.h",
]
specific_include_rules = {
diff --git a/chromium/ui/aura/client/drag_drop_client.h b/chromium/ui/aura/client/drag_drop_client.h
index 3d082567ca7..a6fa5efeb1d 100644
--- a/chromium/ui/aura/client/drag_drop_client.h
+++ b/chromium/ui/aura/client/drag_drop_client.h
@@ -8,7 +8,7 @@
#include <memory>
#include "ui/aura/aura_export.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/gfx/native_widget_types.h"
namespace gfx {
@@ -39,7 +39,7 @@ class AURA_EXPORT DragDropClient {
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) = 0;
+ ui::mojom::DragEventSource source) = 0;
// Called when a drag and drop session is cancelled.
virtual void DragCancel() = 0;
diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc
index 22b7aac67d4..ae56d8fcb85 100644
--- a/chromium/ui/aura/env.cc
+++ b/chromium/ui/aura/env.cc
@@ -224,14 +224,15 @@ void Env::Init() {
// The ozone platform can provide its own event source. So initialize the
// platform before creating the default event source. If running inside mus
// let the mus process initialize ozone instead.
- ui::OzonePlatform::InitParams params;
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- // TODO(kylechar): Pass in single process information to Env::CreateInstance()
- // instead of checking flags here.
- params.single_process = command_line->HasSwitch("single-process") ||
- command_line->HasSwitch("in-process-gpu");
-
- ui::OzonePlatform::InitializeForUI(params);
+ if (features::IsUsingOzonePlatform()) {
+ ui::OzonePlatform::InitParams params;
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ // TODO(kylechar): Pass in single process information to
+ // Env::CreateInstance() instead of checking flags here.
+ params.single_process = command_line->HasSwitch("single-process") ||
+ command_line->HasSwitch("in-process-gpu");
+ ui::OzonePlatform::InitializeForUI(params);
+ }
#endif
if (!ui::PlatformEventSource::GetInstance())
event_source_ = ui::PlatformEventSource::CreateDefault();
diff --git a/chromium/ui/aura/test/ui_controls_factory_ozone.cc b/chromium/ui/aura/test/ui_controls_factory_ozone.cc
deleted file mode 100644
index 23e318e82c9..00000000000
--- a/chromium/ui/aura/test/ui_controls_factory_ozone.cc
+++ /dev/null
@@ -1,358 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "ui/aura/env.h"
-#include "ui/aura/test/aura_test_utils.h"
-#include "ui/aura/test/env_test_helper.h"
-#include "ui/aura/test/ui_controls_factory_aura.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/test/ui_controls_aura.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/test/events_test_utils.h"
-#include "ui/gfx/geometry/point_conversions.h"
-
-namespace aura {
-namespace test {
-namespace {
-
-class UIControlsOzone : public ui_controls::UIControlsAura {
- public:
- UIControlsOzone(WindowTreeHost* host) : host_(host) {
- }
- ~UIControlsOzone() override = default;
-
- private:
- // ui_controls::UIControlsAura:
- bool SendKeyPress(gfx::NativeWindow window,
- ui::KeyboardCode key,
- bool control,
- bool shift,
- bool alt,
- bool command) override {
- return SendKeyPressNotifyWhenDone(window, key, control, shift, alt, command,
- base::OnceClosure());
- }
- bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
- ui::KeyboardCode key,
- bool control,
- bool shift,
- bool alt,
- bool command,
- base::OnceClosure closure) override {
- int flags = button_down_mask_;
- int64_t display_id =
- display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
-
- if (control) {
- flags |= ui::EF_CONTROL_DOWN;
- PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags, display_id,
- base::OnceClosure());
- }
-
- if (shift) {
- flags |= ui::EF_SHIFT_DOWN;
- PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags, display_id,
- base::OnceClosure());
- }
-
- if (alt) {
- flags |= ui::EF_ALT_DOWN;
- PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags, display_id,
- base::OnceClosure());
- }
-
- if (command) {
- flags |= ui::EF_COMMAND_DOWN;
- PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags, display_id,
- base::OnceClosure());
- }
-
- PostKeyEvent(ui::ET_KEY_PRESSED, key, flags, display_id,
- base::OnceClosure());
- const bool has_modifier = control || shift || alt || command;
- // Pass the real closure to the last generated KeyEvent.
- PostKeyEvent(ui::ET_KEY_RELEASED, key, flags, display_id,
- has_modifier ? base::OnceClosure() : std::move(closure));
-
- if (alt) {
- flags &= ~ui::EF_ALT_DOWN;
- PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags, display_id,
- (shift || control || command) ? base::OnceClosure()
- : std::move(closure));
- }
-
- if (shift) {
- flags &= ~ui::EF_SHIFT_DOWN;
- PostKeyEvent(
- ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags, display_id,
- (control || command) ? base::OnceClosure() : std::move(closure));
- }
-
- if (control) {
- flags &= ~ui::EF_CONTROL_DOWN;
- PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags, display_id,
- command ? base::OnceClosure() : std::move(closure));
- }
-
- if (command) {
- flags &= ~ui::EF_COMMAND_DOWN;
- PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags, display_id,
- std::move(closure));
- }
-
- return true;
- }
-
- bool SendMouseMove(int screen_x, int screen_y) override {
- return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::OnceClosure());
- }
- bool SendMouseMoveNotifyWhenDone(int screen_x,
- int screen_y,
- base::OnceClosure closure) override {
- gfx::PointF host_location(screen_x, screen_y);
- int64_t display_id = display::kInvalidDisplayId;
- if (!ScreenDIPToHostPixels(&host_location, &display_id))
- return false;
- ui::EventType event_type;
-
- if (button_down_mask_)
- event_type = ui::ET_MOUSE_DRAGGED;
- else
- event_type = ui::ET_MOUSE_MOVED;
-
- PostMouseEvent(event_type, host_location, button_down_mask_, 0, display_id,
- std::move(closure));
-
- return true;
- }
- bool SendMouseEvents(ui_controls::MouseButton type,
- int button_state,
- int accelerator_state) override {
- return SendMouseEventsNotifyWhenDone(
- type, button_state, base::OnceClosure(), accelerator_state);
- }
- bool SendMouseEventsNotifyWhenDone(ui_controls::MouseButton type,
- int button_state,
- base::OnceClosure closure,
- int accelerator_state) override {
- gfx::PointF host_location(Env::GetInstance()->last_mouse_location());
- int64_t display_id = display::kInvalidDisplayId;
- if (!ScreenDIPToHostPixels(&host_location, &display_id))
- return false;
-
- int changed_button_flag = 0;
-
- switch (type) {
- case ui_controls::LEFT:
- changed_button_flag = ui::EF_LEFT_MOUSE_BUTTON;
- break;
- case ui_controls::MIDDLE:
- changed_button_flag = ui::EF_MIDDLE_MOUSE_BUTTON;
- break;
- case ui_controls::RIGHT:
- changed_button_flag = ui::EF_RIGHT_MOUSE_BUTTON;
- break;
- default:
- NOTREACHED();
- break;
- }
-
- // Process the accelerator key state.
- int flag = changed_button_flag;
- if (accelerator_state & ui_controls::kShift)
- flag |= ui::EF_SHIFT_DOWN;
- if (accelerator_state & ui_controls::kControl)
- flag |= ui::EF_CONTROL_DOWN;
- if (accelerator_state & ui_controls::kAlt)
- flag |= ui::EF_ALT_DOWN;
- if (accelerator_state & ui_controls::kCommand)
- flag |= ui::EF_COMMAND_DOWN;
-
- if (button_state & ui_controls::DOWN) {
- button_down_mask_ |= flag;
- // Pass the real closure to the last generated MouseEvent.
- PostMouseEvent(ui::ET_MOUSE_PRESSED, host_location,
- button_down_mask_ | flag, changed_button_flag, display_id,
- (button_state & ui_controls::UP) ? base::OnceClosure()
- : std::move(closure));
- }
- if (button_state & ui_controls::UP) {
- button_down_mask_ &= ~flag;
- PostMouseEvent(ui::ET_MOUSE_RELEASED, host_location,
- button_down_mask_ | flag, changed_button_flag, display_id,
- std::move(closure));
- }
-
- return true;
- }
- bool SendMouseClick(ui_controls::MouseButton type) override {
- return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN,
- ui_controls::kNoAccelerator);
- }
-#if defined(OS_CHROMEOS)
- bool SendTouchEvents(int action, int id, int x, int y) override {
- return SendTouchEventsNotifyWhenDone(action, id, x, y, base::OnceClosure());
- }
- bool SendTouchEventsNotifyWhenDone(int action,
- int id,
- int x,
- int y,
- base::OnceClosure task) override {
- DCHECK_NE(0, action);
- gfx::PointF host_location(x, y);
- int64_t display_id = display::kInvalidDisplayId;
- if (!ScreenDIPToHostPixels(&host_location, &display_id))
- return false;
- bool has_move = action & ui_controls::MOVE;
- bool has_release = action & ui_controls::RELEASE;
- if (action & ui_controls::PRESS) {
- PostTouchEvent(
- ui::ET_TOUCH_PRESSED, host_location, id, display_id,
- (has_move || has_release) ? base::OnceClosure() : std::move(task));
- }
- if (has_move) {
- PostTouchEvent(ui::ET_TOUCH_MOVED, host_location, id, display_id,
- has_release ? base::OnceClosure() : std::move(task));
- }
- if (has_release) {
- PostTouchEvent(ui::ET_TOUCH_RELEASED, host_location, id, display_id,
- std::move(task));
- }
- return true;
- }
-#endif
-
- void SendEventToSink(ui::Event* event,
- int64_t display_id,
- base::OnceClosure closure) {
- // Post the task before processing the event. This is necessary in case
- // processing the event results in a nested message loop.
- if (closure) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- std::move(closure));
- }
-
- ui::EventSourceTestApi event_source_test(host_->GetEventSource());
- ignore_result(event_source_test.SendEventToSink(event));
- }
-
- void PostKeyEvent(ui::EventType type,
- ui::KeyboardCode key_code,
- int flags,
- int64_t display_id,
- base::OnceClosure closure) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&UIControlsOzone::PostKeyEventTask,
- base::Unretained(this), type, key_code, flags,
- display_id, std::move(closure)));
- }
-
- void PostKeyEventTask(ui::EventType type,
- ui::KeyboardCode key_code,
- int flags,
- int64_t display_id,
- base::OnceClosure closure) {
- // Do not rewrite injected events. See crbug.com/136465.
- flags |= ui::EF_FINAL;
-
- ui::KeyEvent key_event(type, key_code, flags);
- SendEventToSink(&key_event, display_id, std::move(closure));
- }
-
- void PostMouseEvent(ui::EventType type,
- const gfx::PointF& host_location,
- int flags,
- int changed_button_flags,
- int64_t display_id,
- base::OnceClosure closure) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&UIControlsOzone::PostMouseEventTask,
- base::Unretained(this), type, host_location, flags,
- changed_button_flags, display_id, std::move(closure)));
- }
-
- void PostMouseEventTask(ui::EventType type,
- const gfx::PointF& host_location,
- int flags,
- int changed_button_flags,
- int64_t display_id,
- base::OnceClosure closure) {
- ui::MouseEvent mouse_event(type, host_location, host_location,
- ui::EventTimeForNow(), flags,
- changed_button_flags);
-
- // This hack is necessary to set the repeat count for clicks.
- ui::MouseEvent mouse_event2(&mouse_event);
-
- SendEventToSink(&mouse_event2, display_id, std::move(closure));
- }
-
- void PostTouchEvent(ui::EventType type,
- const gfx::PointF& host_location,
- int id,
- int64_t display_id,
- base::OnceClosure closure) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&UIControlsOzone::PostTouchEventTask,
- base::Unretained(this), type, host_location,
- id, display_id, std::move(closure)));
- }
-
- void PostTouchEventTask(ui::EventType type,
- const gfx::PointF& host_location,
- int id,
- int64_t display_id,
- base::OnceClosure closure) {
- ui::PointerDetails details(ui::EventPointerType::kTouch, id, 1.0f, 1.0f,
- 0.0f);
- ui::TouchEvent touch_event(type, host_location, host_location,
- ui::EventTimeForNow(), details);
- SendEventToSink(&touch_event, display_id, std::move(closure));
- }
-
- bool ScreenDIPToHostPixels(gfx::PointF* location, int64_t* display_id) {
- // The location needs to be in display's coordinate.
- display::Display display =
- display::Screen::GetScreen()->GetDisplayNearestPoint(
- gfx::ToFlooredPoint(*location));
- if (!display.is_valid()) {
- LOG(ERROR) << "Failed to find the display for " << location->ToString();
- return false;
- }
- *display_id = display.id();
- *location -= display.bounds().OffsetFromOrigin();
- location->Scale(display.device_scale_factor());
- return true;
- }
-
- WindowTreeHost* host_;
-
- // Mask of the mouse buttons currently down. This is static as it needs to
- // track the state globally for all displays. A UIControlsOzone instance is
- // created for each display host.
- static unsigned button_down_mask_;
-
- DISALLOW_COPY_AND_ASSIGN(UIControlsOzone);
-};
-
-// static
-unsigned UIControlsOzone::button_down_mask_ = 0;
-
-} // namespace
-
-ui_controls::UIControlsAura* CreateUIControlsAura(WindowTreeHost* host) {
- return new UIControlsOzone(host);
-}
-
-} // namespace test
-} // namespace aura
diff --git a/chromium/ui/aura/test/ui_controls_ozone.cc b/chromium/ui/aura/test/ui_controls_ozone.cc
new file mode 100644
index 00000000000..93ee63a84c0
--- /dev/null
+++ b/chromium/ui/aura/test/ui_controls_ozone.cc
@@ -0,0 +1,358 @@
+// 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 "ui/aura/test/ui_controls_ozone.h"
+
+#include "build/build_config.h"
+
+namespace aura {
+namespace test {
+
+// static
+unsigned UIControlsOzone::button_down_mask_ = 0;
+
+UIControlsOzone::UIControlsOzone(WindowTreeHost* host) : host_(host) {}
+
+UIControlsOzone::~UIControlsOzone() = default;
+
+bool UIControlsOzone::SendKeyPress(gfx::NativeWindow window,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command) {
+ return SendKeyPressNotifyWhenDone(window, key, control, shift, alt, command,
+ base::OnceClosure());
+}
+
+bool UIControlsOzone::SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command,
+ base::OnceClosure closure) {
+ WindowTreeHost* optional_host = nullptr;
+ // Send the key event to the window's host, which may not match |host_|.
+ // This logic should probably exist for the non-aura path as well.
+ // TODO(https://crbug.com/1116649) Support non-aura path.
+#if defined(USE_AURA)
+ if (window != nullptr && window->GetHost() != nullptr &&
+ window->GetHost() != host_)
+ optional_host = window->GetHost();
+#endif
+
+ int flags = button_down_mask_;
+ int64_t display_id =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
+
+ if (control) {
+ flags |= ui::EF_CONTROL_DOWN;
+ PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags, display_id,
+ base::OnceClosure(), optional_host);
+ }
+
+ if (shift) {
+ flags |= ui::EF_SHIFT_DOWN;
+ PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags, display_id,
+ base::OnceClosure(), optional_host);
+ }
+
+ if (alt) {
+ flags |= ui::EF_ALT_DOWN;
+ PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags, display_id,
+ base::OnceClosure(), optional_host);
+ }
+
+ if (command) {
+ flags |= ui::EF_COMMAND_DOWN;
+ PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags, display_id,
+ base::OnceClosure(), optional_host);
+ }
+
+ PostKeyEvent(ui::ET_KEY_PRESSED, key, flags, display_id, base::OnceClosure(),
+ optional_host);
+ const bool has_modifier = control || shift || alt || command;
+ // Pass the real closure to the last generated KeyEvent.
+ PostKeyEvent(ui::ET_KEY_RELEASED, key, flags, display_id,
+ has_modifier ? base::OnceClosure() : std::move(closure),
+ optional_host);
+
+ if (alt) {
+ flags &= ~ui::EF_ALT_DOWN;
+ PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags, display_id,
+ (shift || control || command) ? base::OnceClosure()
+ : std::move(closure),
+ optional_host);
+ }
+
+ if (shift) {
+ flags &= ~ui::EF_SHIFT_DOWN;
+ PostKeyEvent(
+ ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags, display_id,
+ (control || command) ? base::OnceClosure() : std::move(closure),
+ optional_host);
+ }
+
+ if (control) {
+ flags &= ~ui::EF_CONTROL_DOWN;
+ PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags, display_id,
+ command ? base::OnceClosure() : std::move(closure),
+ optional_host);
+ }
+
+ if (command) {
+ flags &= ~ui::EF_COMMAND_DOWN;
+ PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags, display_id,
+ std::move(closure), optional_host);
+ }
+
+ return true;
+}
+
+bool UIControlsOzone::SendMouseMove(int screen_x, int screen_y) {
+ return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::OnceClosure());
+}
+
+bool UIControlsOzone::SendMouseMoveNotifyWhenDone(int screen_x,
+ int screen_y,
+ base::OnceClosure closure) {
+ gfx::PointF host_location(screen_x, screen_y);
+ int64_t display_id = display::kInvalidDisplayId;
+ if (!ScreenDIPToHostPixels(&host_location, &display_id))
+ return false;
+ ui::EventType event_type;
+
+ if (button_down_mask_)
+ event_type = ui::ET_MOUSE_DRAGGED;
+ else
+ event_type = ui::ET_MOUSE_MOVED;
+
+ PostMouseEvent(event_type, host_location, button_down_mask_, 0, display_id,
+ std::move(closure));
+
+ return true;
+}
+
+bool UIControlsOzone::SendMouseEvents(ui_controls::MouseButton type,
+ int button_state,
+ int accelerator_state) {
+ return SendMouseEventsNotifyWhenDone(type, button_state, base::OnceClosure(),
+ accelerator_state);
+}
+
+bool UIControlsOzone::SendMouseEventsNotifyWhenDone(
+ ui_controls::MouseButton type,
+ int button_state,
+ base::OnceClosure closure,
+ int accelerator_state) {
+ gfx::PointF host_location(Env::GetInstance()->last_mouse_location());
+ int64_t display_id = display::kInvalidDisplayId;
+ if (!ScreenDIPToHostPixels(&host_location, &display_id))
+ return false;
+
+ int changed_button_flag = 0;
+
+ switch (type) {
+ case ui_controls::LEFT:
+ changed_button_flag = ui::EF_LEFT_MOUSE_BUTTON;
+ break;
+ case ui_controls::MIDDLE:
+ changed_button_flag = ui::EF_MIDDLE_MOUSE_BUTTON;
+ break;
+ case ui_controls::RIGHT:
+ changed_button_flag = ui::EF_RIGHT_MOUSE_BUTTON;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // Process the accelerator key state.
+ int flag = changed_button_flag;
+ if (accelerator_state & ui_controls::kShift)
+ flag |= ui::EF_SHIFT_DOWN;
+ if (accelerator_state & ui_controls::kControl)
+ flag |= ui::EF_CONTROL_DOWN;
+ if (accelerator_state & ui_controls::kAlt)
+ flag |= ui::EF_ALT_DOWN;
+ if (accelerator_state & ui_controls::kCommand)
+ flag |= ui::EF_COMMAND_DOWN;
+
+ if (button_state & ui_controls::DOWN) {
+ button_down_mask_ |= flag;
+ // Pass the real closure to the last generated MouseEvent.
+ PostMouseEvent(ui::ET_MOUSE_PRESSED, host_location,
+ button_down_mask_ | flag, changed_button_flag, display_id,
+ (button_state & ui_controls::UP) ? base::OnceClosure()
+ : std::move(closure));
+ }
+ if (button_state & ui_controls::UP) {
+ button_down_mask_ &= ~flag;
+ PostMouseEvent(ui::ET_MOUSE_RELEASED, host_location,
+ button_down_mask_ | flag, changed_button_flag, display_id,
+ std::move(closure));
+ }
+
+ return true;
+}
+
+bool UIControlsOzone::SendMouseClick(ui_controls::MouseButton type) {
+ return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN,
+ ui_controls::kNoAccelerator);
+}
+
+#if defined(OS_CHROMEOS)
+bool UIControlsOzone::SendTouchEvents(int action, int id, int x, int y) {
+ return SendTouchEventsNotifyWhenDone(action, id, x, y, base::OnceClosure());
+}
+
+bool UIControlsOzone::SendTouchEventsNotifyWhenDone(int action,
+ int id,
+ int x,
+ int y,
+ base::OnceClosure task) {
+ DCHECK_NE(0, action);
+ gfx::PointF host_location(x, y);
+ int64_t display_id = display::kInvalidDisplayId;
+ if (!ScreenDIPToHostPixels(&host_location, &display_id))
+ return false;
+ bool has_move = action & ui_controls::MOVE;
+ bool has_release = action & ui_controls::RELEASE;
+ if (action & ui_controls::PRESS) {
+ PostTouchEvent(
+ ui::ET_TOUCH_PRESSED, host_location, id, display_id,
+ (has_move || has_release) ? base::OnceClosure() : std::move(task));
+ }
+ if (has_move) {
+ PostTouchEvent(ui::ET_TOUCH_MOVED, host_location, id, display_id,
+ has_release ? base::OnceClosure() : std::move(task));
+ }
+ if (has_release) {
+ PostTouchEvent(ui::ET_TOUCH_RELEASED, host_location, id, display_id,
+ std::move(task));
+ }
+ return true;
+}
+#endif
+
+void UIControlsOzone::SendEventToSink(ui::Event* event,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host) {
+ // Post the task before processing the event. This is necessary in case
+ // processing the event results in a nested message loop.
+ if (closure) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(closure));
+ }
+ WindowTreeHost* host = optional_host ? optional_host : host_;
+ ui::EventSourceTestApi event_source_test(host->GetEventSource());
+ ignore_result(event_source_test.SendEventToSink(event));
+}
+
+void UIControlsOzone::PostKeyEvent(ui::EventType type,
+ ui::KeyboardCode key_code,
+ int flags,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&UIControlsOzone::PostKeyEventTask,
+ base::Unretained(this), type, key_code, flags,
+ display_id, std::move(closure), optional_host));
+}
+
+void UIControlsOzone::PostKeyEventTask(ui::EventType type,
+ ui::KeyboardCode key_code,
+ int flags,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host) {
+ // Do not rewrite injected events. See crbug.com/136465.
+ flags |= ui::EF_FINAL;
+
+ ui::KeyEvent key_event(type, key_code, flags);
+ SendEventToSink(&key_event, display_id, std::move(closure), optional_host);
+}
+
+void UIControlsOzone::PostMouseEvent(ui::EventType type,
+ const gfx::PointF& host_location,
+ int flags,
+ int changed_button_flags,
+ int64_t display_id,
+ base::OnceClosure closure) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UIControlsOzone::PostMouseEventTask,
+ base::Unretained(this), type, host_location, flags,
+ changed_button_flags, display_id, std::move(closure)));
+}
+
+void UIControlsOzone::PostMouseEventTask(ui::EventType type,
+ const gfx::PointF& host_location,
+ int flags,
+ int changed_button_flags,
+ int64_t display_id,
+ base::OnceClosure closure) {
+ ui::MouseEvent mouse_event(type, host_location, host_location,
+ ui::EventTimeForNow(), flags,
+ changed_button_flags);
+
+ // This hack is necessary to set the repeat count for clicks.
+ ui::MouseEvent mouse_event2(&mouse_event);
+
+ SendEventToSink(&mouse_event2, display_id, std::move(closure));
+}
+
+void UIControlsOzone::PostTouchEvent(ui::EventType type,
+ const gfx::PointF& host_location,
+ int id,
+ int64_t display_id,
+ base::OnceClosure closure) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&UIControlsOzone::PostTouchEventTask,
+ base::Unretained(this), type, host_location, id,
+ display_id, std::move(closure)));
+}
+
+void UIControlsOzone::PostTouchEventTask(ui::EventType type,
+ const gfx::PointF& host_location,
+ int id,
+ int64_t display_id,
+ base::OnceClosure closure) {
+ ui::PointerDetails details(ui::EventPointerType::kTouch, id, 1.0f, 1.0f,
+ 0.0f);
+ ui::TouchEvent touch_event(type, host_location, host_location,
+ ui::EventTimeForNow(), details);
+ SendEventToSink(&touch_event, display_id, std::move(closure));
+}
+
+bool UIControlsOzone::ScreenDIPToHostPixels(gfx::PointF* location,
+ int64_t* display_id) {
+ // The location needs to be in display's coordinate.
+ display::Display display =
+ display::Screen::GetScreen()->GetDisplayNearestPoint(
+ gfx::ToFlooredPoint(*location));
+ if (!display.is_valid()) {
+ LOG(ERROR) << "Failed to find the display for " << location->ToString();
+ return false;
+ }
+ *display_id = display.id();
+ *location -= display.bounds().OffsetFromOrigin();
+ location->Scale(display.device_scale_factor());
+ return true;
+}
+
+// To avoid multiple definitions when use_x11 && use_ozone is true, disable this
+// factory method for OS_LINUX as Linux has a factory method that decides what
+// UIControls to use based on IsUsingOzonePlatform feature flag.
+#if !defined(OS_LINUX)
+ui_controls::UIControlsAura* CreateUIControlsAura(WindowTreeHost* host) {
+ return new UIControlsOzone(host);
+}
+#endif
+
+} // namespace test
+} // namespace aura
diff --git a/chromium/ui/aura/test/ui_controls_ozone.h b/chromium/ui/aura/test/ui_controls_ozone.h
new file mode 100644
index 00000000000..e85844fba15
--- /dev/null
+++ b/chromium/ui/aura/test/ui_controls_ozone.h
@@ -0,0 +1,135 @@
+// 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 UI_AURA_TEST_UI_CONTROLS_OZONE_H_
+#define UI_AURA_TEST_UI_CONTROLS_OZONE_H_
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "ui/aura/env.h"
+#include "ui/aura/test/aura_test_utils.h"
+#include "ui/aura/test/env_test_helper.h"
+#include "ui/aura/test/ui_controls_factory_aura.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/test/ui_controls_aura.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/test/events_test_utils.h"
+#include "ui/gfx/geometry/point_conversions.h"
+
+namespace aura {
+namespace test {
+
+class UIControlsOzone : public ui_controls::UIControlsAura {
+ public:
+ explicit UIControlsOzone(WindowTreeHost* host);
+ UIControlsOzone(const UIControlsOzone&) = delete;
+ UIControlsOzone& operator=(const UIControlsOzone&) = delete;
+ ~UIControlsOzone() override;
+
+ private:
+ // ui_controls::UIControlsAura:
+ bool SendKeyPress(gfx::NativeWindow window,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command) override;
+ bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command,
+ base::OnceClosure closure) override;
+ bool SendMouseMove(int screen_x, int screen_y) override;
+ bool SendMouseMoveNotifyWhenDone(int screen_x,
+ int screen_y,
+ base::OnceClosure closure) override;
+ bool SendMouseEvents(ui_controls::MouseButton type,
+ int button_state,
+ int accelerator_state) override;
+ bool SendMouseEventsNotifyWhenDone(ui_controls::MouseButton type,
+ int button_state,
+ base::OnceClosure closure,
+ int accelerator_state) override;
+ bool SendMouseClick(ui_controls::MouseButton type) override;
+#if defined(OS_CHROMEOS)
+ bool SendTouchEvents(int action, int id, int x, int y) override;
+ bool SendTouchEventsNotifyWhenDone(int action,
+ int id,
+ int x,
+ int y,
+ base::OnceClosure task) override;
+#endif
+
+ // Use |optional_host| to specify the host.
+ // When |optional_host| is not null, event will be sent to |optional_host|.
+ // When |optional_host| is null, event will be sent to the default host.
+ void SendEventToSink(ui::Event* event,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host = nullptr);
+
+ void PostKeyEvent(ui::EventType type,
+ ui::KeyboardCode key_code,
+ int flags,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host = nullptr);
+
+ void PostKeyEventTask(ui::EventType type,
+ ui::KeyboardCode key_code,
+ int flags,
+ int64_t display_id,
+ base::OnceClosure closure,
+ WindowTreeHost* optional_host);
+
+ void PostMouseEvent(ui::EventType type,
+ const gfx::PointF& host_location,
+ int flags,
+ int changed_button_flags,
+ int64_t display_id,
+ base::OnceClosure closure);
+
+ void PostMouseEventTask(ui::EventType type,
+ const gfx::PointF& host_location,
+ int flags,
+ int changed_button_flags,
+ int64_t display_id,
+ base::OnceClosure closure);
+
+ void PostTouchEvent(ui::EventType type,
+ const gfx::PointF& host_location,
+ int id,
+ int64_t display_id,
+ base::OnceClosure closure);
+
+ void PostTouchEventTask(ui::EventType type,
+ const gfx::PointF& host_location,
+ int id,
+ int64_t display_id,
+ base::OnceClosure closure);
+
+ bool ScreenDIPToHostPixels(gfx::PointF* location, int64_t* display_id);
+
+ // This is the default host used for events that are not scoped to a window.
+ // Events scoped to a window always use the window's host.
+ WindowTreeHost* const host_;
+
+ // Mask of the mouse buttons currently down. This is static as it needs to
+ // track the state globally for all displays. A UIControlsOzone instance is
+ // created for each display host.
+ static unsigned button_down_mask_;
+};
+
+} // namespace test
+} // namespace aura
+
+#endif // UI_AURA_TEST_UI_CONTROLS_OZONE_H_
diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc
index 7d6cff67049..6151f197ad6 100644
--- a/chromium/ui/aura/window.cc
+++ b/chromium/ui/aura/window.cc
@@ -900,16 +900,17 @@ void Window::SetVisible(bool visible) {
void Window::SetOcclusionInfo(OcclusionState occlusion_state,
const SkRegion& occluded_region) {
- if (occlusion_state != occlusion_state_ ||
- occluded_region_ != occluded_region) {
- occlusion_state_ = occlusion_state;
- occluded_region_ = occluded_region;
- if (delegate_)
- delegate_->OnWindowOcclusionChanged(occlusion_state, occluded_region);
-
- for (WindowObserver& observer : observers_)
- observer.OnWindowOcclusionChanged(this);
+ if (occlusion_state == occlusion_state_ &&
+ occluded_region_in_root_ == occluded_region) {
+ return;
}
+ occlusion_state_ = occlusion_state;
+ occluded_region_in_root_ = occluded_region;
+ if (delegate_)
+ delegate_->OnWindowOcclusionChanged(occlusion_state);
+
+ for (WindowObserver& observer : observers_)
+ observer.OnWindowOcclusionChanged(this);
}
void Window::SchedulePaint() {
diff --git a/chromium/ui/aura/window.h b/chromium/ui/aura/window.h
index 6251d9a30e8..e2a908f7ab3 100644
--- a/chromium/ui/aura/window.h
+++ b/chromium/ui/aura/window.h
@@ -41,7 +41,7 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#error This file must not be included on macOS; Chromium Mac doesn't use Aura.
#endif
@@ -219,17 +219,19 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// WindowOcclusionTracker::ScopedPause.
OcclusionState occlusion_state() const { return occlusion_state_; }
- // Returns the currently occluded region. This will be empty unless
- // the window is tracked and has a VISIBLE occlusion state. That is,
- // this is only maintained when the window is partially occluded. Further,
- // this region may extend outside the window bounds. For performance reasons,
- // the actual intersection with the window is not computed. The occluded
- // region is the set of window rectangles that may occlude this window.
- // Note that this means that the occluded region may be updated if one of
- // those windows moves, even if the actual intersection of the occluded
- // region with this window does not change. Clients may compute the actual
- // intersection region if necessary.
- const SkRegion& occluded_region() const { return occluded_region_; }
+ // Returns the currently occluded region in the root Window coordinates. This
+ // will be empty unless the window is tracked and has a VISIBLE occlusion
+ // state. That is, this is only maintained when the window is partially
+ // occluded. Further, this region may extend outside the window bounds. For
+ // performance reasons, the actual intersection with the window is not
+ // computed. The occluded region is the set of window rectangles that may
+ // occlude this window. Note that this means that the occluded region may be
+ // updated if one of those windows moves, even if the actual intersection of
+ // the occluded region with this window does not change. Clients may compute
+ // the actual intersection region if necessary.
+ const SkRegion& occluded_region_in_root() const {
+ return occluded_region_in_root_;
+ }
// Returns the window's bounds in root window's coordinates.
gfx::Rect GetBoundsInRootWindow() const;
@@ -662,8 +664,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// Occlusion state of the window.
OcclusionState occlusion_state_ = OcclusionState::UNKNOWN;
- // Occluded region of the window.
- SkRegion occluded_region_;
+ // Occluded region of the window in the root window coordiantes.
+ SkRegion occluded_region_in_root_;
int id_ = kInitialId;
diff --git a/chromium/ui/aura/window_delegate.h b/chromium/ui/aura/window_delegate.h
index a4eb06ad390..67fd3e78394 100644
--- a/chromium/ui/aura/window_delegate.h
+++ b/chromium/ui/aura/window_delegate.h
@@ -89,10 +89,9 @@ class AURA_EXPORT WindowDelegate : public ui::EventHandler {
// Called when the occlusion state or occluded region of the Window changes
// while tracked (see WindowOcclusionTracker::Track). |occlusion_state| is
- // the new occlusion state of the Window. |occluded_region| is the new
- // occluded region of the Window.
- virtual void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) {}
+ // the new occlusion state of the Window.
+ virtual void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) {}
// Called from Window::HitTest to check if the window has a custom hit test
// mask. It works similar to the views counterparts. That is, if the function
diff --git a/chromium/ui/aura/window_occlusion_change_builder_unittest.cc b/chromium/ui/aura/window_occlusion_change_builder_unittest.cc
index 6291fc0d4fa..8338e77a86a 100644
--- a/chromium/ui/aura/window_occlusion_change_builder_unittest.cc
+++ b/chromium/ui/aura/window_occlusion_change_builder_unittest.cc
@@ -21,6 +21,8 @@ class OcclusionTrackWindowDelegate : public test::TestWindowDelegate {
OcclusionTrackWindowDelegate() = default;
~OcclusionTrackWindowDelegate() override = default;
+ void set_window(Window* window) { window_ = window; }
+
bool occlusion_change_count() const { return occlusion_change_count_; }
Window::OcclusionState last_occlusion_state() const {
return last_occlusion_state_;
@@ -29,13 +31,14 @@ class OcclusionTrackWindowDelegate : public test::TestWindowDelegate {
private:
// test::TestWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
++occlusion_change_count_;
last_occlusion_state_ = occlusion_state;
- last_occluded_region_ = occluded_region;
+ last_occluded_region_ = window_->occluded_region_in_root();
}
+ Window* window_ = nullptr;
int occlusion_change_count_ = 0;
Window::OcclusionState last_occlusion_state_ =
Window::OcclusionState::UNKNOWN;
@@ -54,6 +57,7 @@ class WindowOcclusionChangeBuilderTest : public test::AuraTestBase {
std::unique_ptr<Window> CreateTestWindow(
OcclusionTrackWindowDelegate* delegate) {
auto window = std::make_unique<Window>(delegate);
+ delegate->set_window(window.get());
window->set_owned_by_parent(false);
window->SetType(client::WINDOW_TYPE_NORMAL);
window->Init(ui::LAYER_TEXTURED);
diff --git a/chromium/ui/aura/window_occlusion_tracker.cc b/chromium/ui/aura/window_occlusion_tracker.cc
index fe5146c337e..8a13839bfdb 100644
--- a/chromium/ui/aura/window_occlusion_tracker.cc
+++ b/chromium/ui/aura/window_occlusion_tracker.cc
@@ -14,7 +14,6 @@
#include "ui/aura/window_occlusion_change_builder.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/transform.h"
diff --git a/chromium/ui/aura/window_occlusion_tracker_unittest.cc b/chromium/ui/aura/window_occlusion_tracker_unittest.cc
index b252a999cb5..31ba4e71245 100644
--- a/chromium/ui/aura/window_occlusion_tracker_unittest.cc
+++ b/chromium/ui/aura/window_occlusion_tracker_unittest.cc
@@ -39,6 +39,7 @@ class MockWindowDelegate : public test::ColorTestWindowDelegate {
~MockWindowDelegate() override { EXPECT_FALSE(is_expecting_call()); }
void set_window(Window* window) { window_ = window; }
+ Window* window() { return window_; }
void SetName(const std::string& name) { window_->SetName(name); }
@@ -52,13 +53,13 @@ class MockWindowDelegate : public test::ColorTestWindowDelegate {
return expected_occlusion_state_ != Window::OcclusionState::UNKNOWN;
}
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
SCOPED_TRACE(window_->GetName());
ASSERT_TRUE(window_);
EXPECT_NE(occlusion_state, Window::OcclusionState::UNKNOWN);
EXPECT_EQ(occlusion_state, expected_occlusion_state_);
- EXPECT_EQ(occluded_region, expected_occluded_region_);
+ EXPECT_EQ(window_->occluded_region_in_root(), expected_occluded_region_);
expected_occlusion_state_ = Window::OcclusionState::UNKNOWN;
expected_occluded_region_ = SkRegion();
}
@@ -1561,17 +1562,15 @@ namespace {
class WindowDelegateHidingWindowIfOccluded : public MockWindowDelegate {
public:
- WindowDelegateHidingWindowIfOccluded(Window* other_window)
+ explicit WindowDelegateHidingWindowIfOccluded(Window* other_window)
: other_window_(other_window) {}
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
- if (occlusion_state == Window::OcclusionState::HIDDEN) {
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
+ if (occlusion_state == Window::OcclusionState::HIDDEN)
other_window_->Hide();
- }
}
private:
@@ -1591,10 +1590,9 @@ class WindowDelegateWithQueuedExpectation : public MockWindowDelegate {
}
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
if (queued_expected_occlusion_state_ != Window::OcclusionState::UNKNOWN) {
set_expectation(queued_expected_occlusion_state_,
queued_expected_occluded_region_);
@@ -1657,10 +1655,9 @@ class WindowDelegateDeletingWindow : public MockWindowDelegate {
void set_other_window(Window* other_window) { other_window_ = other_window; }
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
if (occlusion_state == Window::OcclusionState::OCCLUDED) {
delete other_window_;
other_window_ = nullptr;
@@ -1728,10 +1725,9 @@ class WindowDelegateChangingWindowVisibility : public MockWindowDelegate {
void set_window_to_update(Window* window) { window_to_update_ = window; }
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
if (!window_to_update_)
return;
@@ -1920,10 +1916,9 @@ class WindowDelegateHidingWindow : public MockWindowDelegate {
void set_window_to_update(Window* window) { window_to_update_ = window; }
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
if (!window_to_update_)
return;
@@ -1950,10 +1945,9 @@ class WindowDelegateAddingAndHidingChild : public MockWindowDelegate {
void set_window_to_update(Window* window) { window_to_update_ = window; }
// MockWindowDelegate:
- void OnWindowOcclusionChanged(Window::OcclusionState occlusion_state,
- const SkRegion& occluded_region) override {
- MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state,
- occluded_region);
+ void OnWindowOcclusionChanged(
+ Window::OcclusionState occlusion_state) override {
+ MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state);
if (queued_expected_occlusion_state_ != Window::OcclusionState::UNKNOWN) {
set_expectation(queued_expected_occlusion_state_,
queued_expected_occluded_region_);
diff --git a/chromium/ui/aura/window_tree_host_platform.cc b/chromium/ui/aura/window_tree_host_platform.cc
index 917b45bf3f8..01843f47b17 100644
--- a/chromium/ui/aura/window_tree_host_platform.cc
+++ b/chromium/ui/aura/window_tree_host_platform.cc
@@ -29,6 +29,7 @@
#if defined(USE_OZONE)
#include "ui/base/ui_base_features.h"
+#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
@@ -39,8 +40,6 @@
#if defined(USE_X11)
#include "ui/platform_window/x11/x11_window.h" // nogncheck
-#else
-#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
#endif
namespace aura {
@@ -172,12 +171,13 @@ bool WindowTreeHostPlatform::IsKeyLocked(ui::DomCode dom_code) {
base::flat_map<std::string, std::string>
WindowTreeHostPlatform::GetKeyboardLayoutMap() {
-#if !defined(USE_X11)
- return ui::GenerateDomKeyboardLayoutMap();
-#else
+#if defined(USE_OZONE)
+ // USE_X11 supports keyboard layout map through LinuxUI.
+ if (features::IsUsingOzonePlatform())
+ return ui::GenerateDomKeyboardLayoutMap();
+#endif
NOTIMPLEMENTED();
return {};
-#endif
}
void WindowTreeHostPlatform::SetCursorNative(gfx::NativeCursor cursor) {
@@ -261,6 +261,8 @@ void WindowTreeHostPlatform::OnAcceleratedWidgetAvailable(
WindowTreeHost::OnAcceleratedWidgetAvailable();
}
+void WindowTreeHostPlatform::OnWillDestroyAcceleratedWidget() {}
+
void WindowTreeHostPlatform::OnAcceleratedWidgetDestroyed() {
gfx::AcceleratedWidget widget = compositor()->ReleaseAcceleratedWidget();
DCHECK_EQ(widget, widget_);
diff --git a/chromium/ui/aura/window_tree_host_platform.h b/chromium/ui/aura/window_tree_host_platform.h
index ff4050c1ed4..27d7d78a34a 100644
--- a/chromium/ui/aura/window_tree_host_platform.h
+++ b/chromium/ui/aura/window_tree_host_platform.h
@@ -49,6 +49,11 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost,
const gfx::Point& location_in_pixels) override;
void OnCursorVisibilityChangedNative(bool show) override;
+ ui::PlatformWindow* platform_window() { return platform_window_.get(); }
+ const ui::PlatformWindow* platform_window() const {
+ return platform_window_.get();
+ }
+
protected:
// NOTE: this does not call CreateCompositor(); subclasses must call
// CreateCompositor() at the appropriate time.
@@ -59,10 +64,6 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost,
void CreateAndSetPlatformWindow(ui::PlatformWindowInitProperties properties);
void SetPlatformWindow(std::unique_ptr<ui::PlatformWindow> window);
- ui::PlatformWindow* platform_window() { return platform_window_.get(); }
- const ui::PlatformWindow* platform_window() const {
- return platform_window_.get();
- }
// ui::PlatformWindowDelegate:
void OnBoundsChanged(const gfx::Rect& new_bounds) override;
@@ -73,6 +74,7 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost,
void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
void OnLostCapture() override;
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
+ void OnWillDestroyAcceleratedWidget() override;
void OnAcceleratedWidgetDestroyed() override;
void OnActivationChanged(bool active) override;
void OnMouseEnter() override;
diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn
index 1179753aeff..88883f2c90d 100644
--- a/chromium/ui/base/BUILD.gn
+++ b/chromium/ui/base/BUILD.gn
@@ -3,10 +3,8 @@
# found in the LICENSE file.
import("//build/buildflag_header.gni")
-import("//build/config/chromeos/ui_mode.gni")
import("//build/config/compiler/compiler.gni")
import("//build/config/dcheck_always_on.gni")
-import("//build/config/jumbo.gni")
import("//build/config/linux/gtk/gtk.gni")
import("//build/config/linux/pangocairo/pangocairo.gni")
import("//build/config/sanitizers/sanitizers.gni")
@@ -67,7 +65,6 @@ buildflag_header("buildflags") {
flags = [
"ENABLE_HIDPI=$enable_hidpi",
"ENABLE_MESSAGE_CENTER=$enable_message_center",
- "LACROS=$chromeos_is_browser_only",
"USE_ATK=$use_atk",
"USE_XKBCOMMON=$use_xkbcommon",
"HAS_NATIVE_ACCESSIBILITY=$has_native_accessibility",
@@ -89,7 +86,7 @@ source_set("hit_test") {
}
}
-jumbo_component("base") {
+component("base") {
output_name = "ui_base"
sources = [
@@ -121,6 +118,11 @@ jumbo_component("base") {
"models/combobox_model.cc",
"models/combobox_model.h",
"models/combobox_model_observer.h",
+ "models/dialog_model.cc",
+ "models/dialog_model.h",
+ "models/dialog_model_field.cc",
+ "models/dialog_model_field.h",
+ "models/dialog_model_host.h",
"models/image_model.cc",
"models/image_model.h",
"models/list_model.h",
@@ -354,7 +356,7 @@ jumbo_component("base") {
"user_activity/user_activity_observer.h",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"accelerators/menu_label_accelerator_util_linux.cc",
"accelerators/menu_label_accelerator_util_linux.h",
@@ -404,7 +406,7 @@ jumbo_component("base") {
sources += [ "pointer/pointer_device_android.cc" ]
} else if (is_ios) {
sources += [ "pointer/pointer_device_ios.cc" ]
- } else if (is_linux) {
+ } else if (is_linux || is_chromeos) {
sources += [ "pointer/pointer_device_linux.cc" ]
} else {
# Empty implementation for all other cases.
@@ -430,6 +432,7 @@ jumbo_component("base") {
":ui_data_pack",
"//base",
"//skia",
+ "//third_party/abseil-cpp:absl",
"//ui/gfx",
"//ui/gfx/geometry",
]
@@ -438,6 +441,7 @@ jumbo_component("base") {
"//base:base_static",
"//base:i18n",
"//base/third_party/dynamic_annotations",
+ "//base/util/type_safety:type_safety",
"//net",
"//third_party/brotli:dec",
"//third_party/icu",
@@ -515,11 +519,11 @@ jumbo_component("base") {
}
}
- if (use_aura && is_linux) {
+ if (use_aura && (is_linux || is_chromeos)) {
sources += [ "resource/resource_bundle_auralinux.cc" ]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//third_party/fontconfig" ]
}
@@ -532,7 +536,7 @@ jumbo_component("base") {
]
}
- if (is_chromeos || (use_aura && is_linux && !use_x11)) {
+ if (is_chromeos || (use_aura && is_linux)) {
sources += [
"dragdrop/os_exchange_data_provider_non_backed.cc",
"dragdrop/os_exchange_data_provider_non_backed.h",
@@ -553,7 +557,7 @@ jumbo_component("base") {
if (is_mac) {
deps += [ "//third_party/mozilla" ]
- libs += [
+ frameworks = [
"Accelerate.framework",
"AppKit.framework",
"QuartzCore.framework",
@@ -636,7 +640,7 @@ component("features") {
# TODO(crbug.com/1091985): Support CrOS.
if (is_win || is_mac || (is_linux && !is_chromeos)) {
- jumbo_static_library("pixel_diff_test_support") {
+ static_library("pixel_diff_test_support") {
testonly = true
sources = [
"test/skia_gold_matching_algorithm.cc",
@@ -674,7 +678,7 @@ if (!is_ios) {
}
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"resource/mock_resource_bundle_delegate.cc",
@@ -869,6 +873,18 @@ test("ui_base_unittests") {
"models/simple_menu_model_unittest.cc",
"models/tree_node_iterator_unittest.cc",
"pointer/touch_ui_controller_unittest.cc",
+ "prediction/empty_filter_unittests.cc",
+ "prediction/input_filter_unittest_helpers.cc",
+ "prediction/input_filter_unittest_helpers.h",
+ "prediction/input_predictor_unittest_helpers.cc",
+ "prediction/input_predictor_unittest_helpers.h",
+ "prediction/kalman_predictor_unittest.cc",
+ "prediction/least_squares_predictor_unittest.cc",
+ "prediction/linear_predictor_unittest.cc",
+ "prediction/linear_resampling_unittest.cc",
+ "prediction/one_euro_filter_unittests.cc",
+ "prediction/prediction_metrics_handler_unittest.cc",
+ "prediction/prediction_unittest_helpers.h",
"resource/data_pack_literal.cc",
"resource/data_pack_literal.h",
"resource/data_pack_unittest.cc",
@@ -917,6 +933,7 @@ test("ui_base_unittests") {
"//ui/base/clipboard:clipboard_test",
"//ui/base/clipboard:clipboard_types",
"//ui/base/cursor:unittests",
+ "//ui/base/prediction:prediction",
"//ui/display",
"//ui/events:events_base",
"//ui/events:test_support",
@@ -955,7 +972,7 @@ test("ui_base_unittests") {
]
deps += [ "//components/system_media_controls:test_support" ]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources +=
[ "accelerators/menu_label_accelerator_util_linux_unittest.cc" ]
}
@@ -1114,7 +1131,7 @@ test("ui_base_unittests") {
deps += [ "//ui/events:dom_keycode_converter" ]
}
- if (is_android || is_linux || is_mac || is_win || is_fuchsia) {
+ if (is_android || is_linux || is_chromeos || is_mac || is_win || is_fuchsia) {
# TODO(brettw): We should be able to depend on //ui/resources:ui_test_pak
# instead of depending directly on the non-test .pak files, but depending
# on ui_test_pak seems to have no effect.
@@ -1127,7 +1144,7 @@ test("ui_base_unittests") {
"//ui/resources:ui_test_pak", # TODO(brettw): this does nothing.
]
}
- if (is_linux || is_win || is_fuchsia) {
+ if (is_linux || is_chromeos || is_win || is_fuchsia) {
data += [
# TODO(brettw): Remove these two lines.
"$root_out_dir/ui/en-US.pak",
@@ -1163,7 +1180,7 @@ if (is_win) {
}
}
-if (is_linux) {
+if (is_linux || is_chromeos) {
# This source set defines linux wm role names and must not have any external
# dependencies and be kept as minimal as possible.
source_set("wm_role_names") {
diff --git a/chromium/ui/base/accelerators/accelerator.cc b/chromium/ui/base/accelerators/accelerator.cc
index 7f26ef86fe9..627e077480f 100644
--- a/chromium/ui/base/accelerators/accelerator.cc
+++ b/chromium/ui/base/accelerators/accelerator.cc
@@ -23,7 +23,7 @@
#include <windows.h>
#endif
-#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_MACOSX))
+#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_APPLE))
#include "ui/events/keycodes/keyboard_code_conversion.h"
#endif
@@ -198,7 +198,7 @@ bool Accelerator::IsRepeat() const {
base::string16 Accelerator::GetShortcutText() const {
base::string16 shortcut;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
shortcut = KeyCodeToMacSymbol();
#else
shortcut = KeyCodeToName();
@@ -221,7 +221,7 @@ base::string16 Accelerator::GetShortcutText() const {
// VKEY_UNKNOWN), |::MapVirtualKeyW| returns 0.
if (key != 0)
shortcut += key;
-#elif defined(USE_AURA) || defined(OS_MACOSX) || defined(OS_ANDROID)
+#elif defined(USE_AURA) || defined(OS_APPLE) || defined(OS_ANDROID)
const uint16_t c = DomCodeToUsLayoutCharacter(
UsLayoutKeyboardCodeToDomCode(key_code_), false);
if (c != 0)
@@ -230,7 +230,7 @@ base::string16 Accelerator::GetShortcutText() const {
#endif
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
shortcut = ApplyShortFormModifiers(shortcut);
#else
// Checking whether the character used for the accelerator is alphanumeric.
@@ -276,12 +276,12 @@ base::string16 Accelerator::GetShortcutText() const {
shortcut_rtl.append(shortcut, 0, shortcut.length() - key_length - 1);
shortcut.swap(shortcut_rtl);
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
return shortcut;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
base::string16 Accelerator::KeyCodeToMacSymbol() const {
switch (key_code_) {
case VKEY_CAPITAL:
@@ -317,7 +317,7 @@ base::string16 Accelerator::KeyCodeToMacSymbol() const {
return KeyCodeToName();
}
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
base::string16 Accelerator::KeyCodeToName() const {
int string_id = 0;
@@ -373,7 +373,7 @@ base::string16 Accelerator::KeyCodeToName() const {
case VKEY_F11:
string_id = IDS_APP_F11_KEY;
break;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// On Mac, commas and periods are used literally in accelerator text.
case VKEY_OEM_COMMA:
string_id = IDS_APP_COMMA_KEY;
@@ -414,7 +414,7 @@ base::string16 Accelerator::ApplyLongFormModifiers(
shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_ALT_KEY);
if (IsCmdDown()) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_COMMAND_KEY);
#elif defined(OS_CHROMEOS)
shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_SEARCH_KEY);
diff --git a/chromium/ui/base/accelerators/accelerator.h b/chromium/ui/base/accelerators/accelerator.h
index 6111a3bf550..35a9bb01aef 100644
--- a/chromium/ui/base/accelerators/accelerator.h
+++ b/chromium/ui/base/accelerators/accelerator.h
@@ -87,7 +87,7 @@ class COMPONENT_EXPORT(UI_BASE) Accelerator {
// Returns a string with the localized shortcut if any.
base::string16 GetShortcutText() const;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
base::string16 KeyCodeToMacSymbol() const;
#endif
base::string16 KeyCodeToName() const;
diff --git a/chromium/ui/base/accelerators/accelerator_unittest.cc b/chromium/ui/base/accelerators/accelerator_unittest.cc
index 3a73e7954a6..5a760ea6224 100644
--- a/chromium/ui/base/accelerators/accelerator_unittest.cc
+++ b/chromium/ui/base/accelerators/accelerator_unittest.cc
@@ -52,7 +52,7 @@ TEST(AcceleratorTest, MAYBE_GetShortcutText) {
{VKEY_A, EF_ALT_DOWN | EF_SHIFT_DOWN, "Alt+Shift+A", "\u2325\u21e7A"},
// Regression test for https://crbug.com/867732:
{VKEY_OEM_COMMA, EF_CONTROL_DOWN, "Ctrl+Comma", "\u2303,"},
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
{VKEY_T, EF_COMMAND_DOWN | EF_CONTROL_DOWN, nullptr, "\u2303\u2318T"},
#endif
};
@@ -60,7 +60,7 @@ TEST(AcceleratorTest, MAYBE_GetShortcutText) {
for (const auto& key : keys) {
base::string16 text =
Accelerator(key.code, key.modifiers).GetShortcutText();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(text, base::UTF8ToUTF16(key.expected_short));
#else
EXPECT_EQ(text, base::UTF8ToUTF16(key.expected_long));
diff --git a/chromium/ui/base/android/OWNERS b/chromium/ui/base/android/OWNERS
deleted file mode 100644
index d3930d9f589..00000000000
--- a/chromium/ui/base/android/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-tedchoc@chromium.org
diff --git a/chromium/ui/base/clipboard/BUILD.gn b/chromium/ui/base/clipboard/BUILD.gn
index b36512c4c36..32a6ff50524 100644
--- a/chromium/ui/base/clipboard/BUILD.gn
+++ b/chromium/ui/base/clipboard/BUILD.gn
@@ -3,7 +3,7 @@
# found in the LICENSE file.
import("///ui/ozone/ozone.gni")
-import("//build/config/jumbo.gni")
+import("//build/config/chromeos/ui_mode.gni")
import("//build/config/ui.gni")
# Reset sources_assignment_filter for the BUILD.gn file to prevent
@@ -12,7 +12,7 @@ import("//build/config/ui.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("clipboard_types") {
+component("clipboard_types") {
output_name = "ui_base_clipboard_types"
sources = [
"clipboard_buffer.h",
@@ -32,7 +32,7 @@ jumbo_component("clipboard_types") {
sources += [ "clipboard_format_type_mac.mm" ]
}
- if (is_mac || is_ios) {
+ if (is_apple) {
sources += [ "clipboard_constants_mac.mm" ]
}
@@ -50,21 +50,24 @@ jumbo_component("clipboard_types") {
deps = [ "//base" ]
- libs = []
- if (is_mac || is_ios) {
- libs += [ "Foundation.framework" ]
+ frameworks = []
+ if (is_apple) {
+ frameworks += [ "Foundation.framework" ]
}
if (is_mac) {
- libs += [ "AppKit.framework" ]
+ frameworks += [ "AppKit.framework" ]
}
}
-jumbo_component("clipboard") {
+component("clipboard") {
output_name = "ui_base_clipboard"
sources = [
"clipboard.cc",
"clipboard.h",
+ "clipboard_data_endpoint.cc",
+ "clipboard_data_endpoint.h",
+ "clipboard_dlp_controller.h",
"clipboard_metrics.cc",
"clipboard_metrics.h",
"clipboard_monitor.cc",
@@ -143,6 +146,8 @@ jumbo_component("clipboard") {
# linux-chromeos uses non-backed clipboard by default, but supports ozone
# x11 with flag --use-system-clipbboard.
sources += [
+ "clipboard_data.cc",
+ "clipboard_data.h",
"clipboard_non_backed.cc",
"clipboard_non_backed.h",
"clipboard_ozone.cc",
@@ -152,6 +157,8 @@ jumbo_component("clipboard") {
} else if (!is_win) {
# This file is used for all builds not backed by an underlying platform.
sources += [
+ "clipboard_data.cc",
+ "clipboard_data.h",
"clipboard_non_backed.cc",
"clipboard_non_backed.h",
]
@@ -164,7 +171,7 @@ jumbo_component("clipboard") {
if (is_mac) {
deps += [ "//third_party/mozilla" ]
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreFoundation.framework",
]
@@ -175,7 +182,7 @@ jumbo_component("clipboard") {
}
}
-jumbo_source_set("clipboard_test_support") {
+source_set("clipboard_test_support") {
testonly = true
if (!is_ios) {
@@ -224,6 +231,13 @@ source_set("clipboard_test") {
]
}
+ if (use_aura && !chromeos_is_browser_only && !use_x11 && !is_win) {
+ sources += [
+ "clipboard_data_unittest.cc",
+ "clipboard_non_backed_unittest.cc",
+ ]
+ }
+
deps = [
":clipboard_test_support",
"//base/test:test_support",
diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h
index 1a69e8420d8..62004e65921 100644
--- a/chromium/ui/base/clipboard/clipboard.h
+++ b/chromium/ui/base/clipboard/clipboard.h
@@ -27,6 +27,7 @@
#include "build/build_config.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/base/clipboard/clipboard_dlp_controller.h"
#include "ui/base/clipboard/clipboard_format_type.h"
class SkBitmap;
@@ -34,6 +35,7 @@ class SkBitmap;
namespace ui {
class TestClipboard;
class ScopedClipboardWriter;
+class ClipboardDataEndpoint;
// Clipboard:
// - reads from and writes to the system clipboard.
@@ -51,7 +53,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
case ClipboardBuffer::kCopyPaste:
return true;
case ClipboardBuffer::kSelection:
-#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
+#if !defined(OS_WIN) && !defined(OS_APPLE) && !defined(OS_CHROMEOS)
return true;
#else
return false;
@@ -106,9 +108,18 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
// whether it has changed.
virtual uint64_t GetSequenceNumber(ClipboardBuffer buffer) const = 0;
- // Tests whether the clipboard contains a certain format
- virtual bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const = 0;
+ // Sets the data leak prevention controller for the clipboard. This function
+ // will be used only on Chrome OS.
+ virtual void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) = 0;
+
+ // Tests whether the clipboard contains a certain format.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
+ virtual bool IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const = 0;
// Returns whether the clipboard has data that is marked by its originator as
// confidential. This is available for opt-in checking by the user of this API
@@ -128,27 +139,42 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
// TODO(huangdarwin): Rename to ReadAvailablePortableFormatNames().
// Includes all sanitized types.
// Also, includes pickled types by splitting them out of the pickled format.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originated uses.
virtual void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const = 0;
// Includes all types, including unsanitized types.
// Omits formats held within pickles, as they're different from what a native
// application would see.
+ // TODO(crbug.com/1103614): Update |dst| in all references to its appropriate
+ // ClipboardDataEndpoint for web-originates uses.
virtual std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const = 0;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const = 0;
// Reads Unicode text from the clipboard, if available.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const = 0;
// Reads ASCII text from the clipboard, if available.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const = 0;
// Reads HTML from the clipboard, if available. If the HTML fragment requires
// context to parse, |fragment_start| and |fragment_end| are indexes into
// markup indicating the beginning and end of the actual fragment. Otherwise,
// they will contain 0 and markup->size().
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -156,25 +182,42 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
// Reads RTF from the clipboard, if available. Stores the result as a byte
// vector.
- virtual void ReadRTF(ClipboardBuffer buffer, std::string* result) const = 0;
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
+ virtual void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const = 0;
using ReadImageCallback = base::OnceCallback<void(const SkBitmap&)>;
// Reads an image from the clipboard, if available.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const = 0;
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const = 0;
// Reads a bookmark from the clipboard, if available.
// |title| or |url| may be null.
- virtual void ReadBookmark(base::string16* title, std::string* url) const = 0;
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
+ virtual void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const = 0;
// Reads raw data from the clipboard with the given format type. Stores result
// as a byte vector.
+ // TODO(crbug.com/1103614): Update |data_dst| in all references to its
+ // appropriate ClipboardDataEndpoint for web-originates uses.
virtual void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const = 0;
// Returns an estimate of the time the clipboard was last updated. If the
@@ -184,6 +227,12 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
// Resets the clipboard last modified time to Time::Time().
virtual void ClearLastModifiedTime();
+#if defined(USE_OZONE)
+ // Returns whether the selection buffer is available. This is true for some
+ // Linux platforms.
+ virtual bool IsSelectionBufferAvailable() const = 0;
+#endif // defined(USE_OZONE)
+
protected:
// PortableFormat designates the type of data to be stored in the clipboard.
// This designation is shared across all OSes. The system-specific designation
@@ -250,15 +299,23 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
virtual ~Clipboard();
// Write a bunch of objects to the system clipboard. Copies are made of the
- // contents of |objects|.
- virtual void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) = 0;
+ // contents of |objects|. Also, adds the source of the data to the clipboard,
+ // which can be used when we need to restrict the clipboard data between a set
+ // of confidential documents. The data source maybe passed as nullptr.
+ virtual void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) = 0;
+
// Write |platform_representations|, in the order of their appearance in
- // |platform_representations|.
+ // |platform_representations|. Also, adds the source of the data to the
+ // clipboard, which can be used when we need to restrict the clipboard data
+ // between a set of confidential documents. The data source maybe passed as
+ // nullptr.
virtual void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation>
- platform_representations) = 0;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) = 0;
void DispatchPortableRepresentation(PortableFormat format,
const ObjectMapParams& params);
diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc
index 917d0d9d135..f8bcfc65d7c 100644
--- a/chromium/ui/base/clipboard/clipboard_android.cc
+++ b/chromium/ui/base/clipboard/clipboard_android.cc
@@ -26,6 +26,7 @@
#include "base/time/time.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/ui_base_jni_headers/Clipboard_jni.h"
#include "ui/gfx/android/java_bitmap.h"
@@ -365,12 +366,21 @@ uint64_t ClipboardAndroid::GetSequenceNumber(
return g_map.Get().GetSequenceNumber();
}
-bool ClipboardAndroid::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+void ClipboardAndroid::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+bool ClipboardAndroid::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
- if (ClipboardFormatType::GetBitmapType().Equals(format)) {
+ if (format == ClipboardFormatType::GetBitmapType()) {
return g_map.Get().HasFormat(kMimeTypeImageURI);
}
return g_map.Get().HasFormat(format.GetName());
@@ -382,8 +392,11 @@ void ClipboardAndroid::Clear(ClipboardBuffer buffer) {
g_map.Get().Clear();
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -393,22 +406,26 @@ void ClipboardAndroid::ReadAvailableTypes(
// would be nice to ask the ClipboardMap to enumerate the types it supports,
// rather than hardcode the list here.
- if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
+ data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeText));
- if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
// these formats aren't supported by the ClipboardMap currently, but might
// be one day?
- if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
- if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
std::vector<base::string16>
ClipboardAndroid::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
std::vector<std::string> formats = g_map.Get().GetFormats();
@@ -420,16 +437,22 @@ ClipboardAndroid::ReadAvailablePlatformSpecificFormatNames(
return types;
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
std::string utf8;
- ReadAsciiText(buffer, &utf8);
+ ReadAsciiText(buffer, data_dst, &utf8);
*result = base::UTF8ToUTF16(utf8);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -437,8 +460,11 @@ void ClipboardAndroid::ReadAsciiText(ClipboardBuffer buffer,
*result = g_map.Get().Get(ClipboardFormatType::GetPlainTextType().GetName());
}
-// Note: |src_url| isn't really used. It is only implemented in Windows
+// Note: |src_url| isn't really used. It is only implemented in Windows.
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -457,13 +483,19 @@ void ClipboardAndroid::ReadHTML(ClipboardBuffer buffer,
*fragment_end = static_cast<uint32_t>(markup->length());
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
NOTIMPLEMENTED();
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -471,20 +503,29 @@ void ClipboardAndroid::ReadImage(ClipboardBuffer buffer,
g_map.Get().GetImage(std::move(callback));
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
NOTIMPLEMENTED();
}
-void ClipboardAndroid::ReadBookmark(base::string16* title,
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardAndroid::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
std::string* url) const {
DCHECK(CalledOnValidThread());
NOTIMPLEMENTED();
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kData);
@@ -502,8 +543,12 @@ void ClipboardAndroid::ClearLastModifiedTime() {
}
// Main entry point used to write several values in the clipboard.
-void ClipboardAndroid::WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) {
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardAndroid::WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
g_map.Get().Clear();
@@ -514,9 +559,12 @@ void ClipboardAndroid::WritePortableRepresentations(ClipboardBuffer buffer,
g_map.Get().CommitToAndroidClipboard();
}
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardAndroid::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
g_map.Get().Clear();
diff --git a/chromium/ui/base/clipboard/clipboard_android.h b/chromium/ui/base/clipboard/clipboard_android.h
index c86bd91e81a..09258ce4de7 100644
--- a/chromium/ui/base/clipboard/clipboard_android.h
+++ b/chromium/ui/base/clipboard/clipboard_android.h
@@ -59,38 +59,57 @@ class ClipboardAndroid : public Clipboard {
// Clipboard overrides:
void OnPreShutdown() override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
base::Time GetLastModifiedTime() const override;
void ClearLastModifiedTime() override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/clipboard_android_test_support.cc b/chromium/ui/base/clipboard/clipboard_android_test_support.cc
index c14b76b3144..fa69f2ba17f 100644
--- a/chromium/ui/base/clipboard/clipboard_android_test_support.cc
+++ b/chromium/ui/base/clipboard/clipboard_android_test_support.cc
@@ -34,9 +34,11 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeWriteHtml(
}
auto* clipboard = Clipboard::GetForCurrentThread();
return clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(),
- ClipboardBuffer::kCopyPaste) &&
+ ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr) &&
clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(),
- ClipboardBuffer::kCopyPaste);
+ ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr);
}
jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains(
@@ -47,13 +49,15 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains(
// well as the Android side.
auto* clipboard = Clipboard::GetForCurrentThread();
if (clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(),
- ClipboardBuffer::kCopyPaste)) {
+ ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr)) {
LOG(ERROR) << "HTML still in clipboard.";
return false;
}
if (!clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(),
- ClipboardBuffer::kCopyPaste)) {
+ ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr)) {
LOG(ERROR) << "Plain text not in clipboard.";
return false;
}
@@ -62,7 +66,8 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains(
base::android::ConvertJavaStringToUTF8(env, j_text, &expected_text);
std::string contents;
- clipboard->ReadAsciiText(ClipboardBuffer::kCopyPaste, &contents);
+ clipboard->ReadAsciiText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &contents);
if (expected_text != contents) {
LOG(ERROR) << "Clipboard contents do not match. Expected: " << expected_text
<< " Actual: " << contents;
diff --git a/chromium/ui/base/clipboard/clipboard_constants.cc b/chromium/ui/base/clipboard/clipboard_constants.cc
index f5a65648e5e..dc9197cc789 100644
--- a/chromium/ui/base/clipboard/clipboard_constants.cc
+++ b/chromium/ui/base/clipboard/clipboard_constants.cc
@@ -10,19 +10,22 @@ const char kMimeTypeText[] = "text/plain";
const char kMimeTypeTextUtf8[] = "text/plain;charset=utf-8";
const char kMimeTypeURIList[] = "text/uri-list";
const char kMimeTypeMozillaURL[] = "text/x-moz-url";
-// Unstandardized format for downloading files after drop events. Now only
-// works in Windows, but used to also work in Linux and MacOS.
-// See https://crbug.com/860557 and https://crbug.com/425170.
const char kMimeTypeDownloadURL[] = "downloadurl";
const char kMimeTypeHTML[] = "text/html";
const char kMimeTypeRTF[] = "text/rtf";
const char kMimeTypePNG[] = "image/png";
-#if !defined(OS_MACOSX)
-// TODO(dcheng): This name is temporary. See crbug.com/106449.
+
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+const char kMimeTypeLinuxUtf8String[] = "UTF8_STRING";
+const char kMimeTypeLinuxString[] = "STRING";
+const char kMimeTypeLinuxText[] = "TEXT";
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+
+#if !defined(OS_APPLE)
const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data";
const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
#if defined(OS_ANDROID)
const char kMimeTypeImageURI[] = "image-uri";
diff --git a/chromium/ui/base/clipboard/clipboard_constants.h b/chromium/ui/base/clipboard/clipboard_constants.h
index f57524d56b6..8808a9917fd 100644
--- a/chromium/ui/base/clipboard/clipboard_constants.h
+++ b/chromium/ui/base/clipboard/clipboard_constants.h
@@ -10,13 +10,13 @@
#include "base/component_export.h"
#include "build/build_config.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#ifdef __OBJC__
@class NSString;
#else
class NSString;
#endif
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
namespace ui {
@@ -24,6 +24,9 @@ namespace ui {
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeText[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeTextUtf8[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeURIList[];
+// Unstandardized format for downloading files after drop events. Now only
+// works in Windows, but used to also work in Linux and MacOS.
+// See https://crbug.com/860557 and https://crbug.com/425170.
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
extern const char kMimeTypeDownloadURL[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
@@ -31,7 +34,19 @@ extern const char kMimeTypeMozillaURL[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeHTML[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeRTF[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypePNG[];
-#if !defined(OS_MACOSX)
+
+// Linux-specific MIME type constants (also used in Fuchsia).
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
+extern const char kMimeTypeLinuxUtf8String[];
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
+extern const char kMimeTypeLinuxString[];
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
+extern const char kMimeTypeLinuxText[];
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+
+#if !defined(OS_APPLE)
+// TODO(dcheng): This name is temporary. See crbug.com/106449.
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
extern const char kMimeTypeWebCustomData[];
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
@@ -58,10 +73,11 @@ extern NSString* const kPepperCustomDataPboardType;
COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
extern NSString* const kUTTypeConfidentialData;
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
#if defined(OS_ANDROID)
-COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) extern const char kMimeTypeImageURI[];
+COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES)
+extern const char kMimeTypeImageURI[];
#endif // defined(OS_ANDROID)
} // namespace ui
diff --git a/chromium/ui/base/clipboard/clipboard_data.cc b/chromium/ui/base/clipboard/clipboard_data.cc
new file mode 100644
index 00000000000..d887c1ed725
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_data.cc
@@ -0,0 +1,77 @@
+// 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 "ui/base/clipboard/clipboard_data.h"
+
+#include <memory>
+#include <ostream>
+
+#include "base/notreached.h"
+#include "skia/ext/skia_utils_base.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+#include "ui/gfx/skia_util.h"
+
+namespace ui {
+
+ClipboardData::ClipboardData() : web_smart_paste_(false), format_(0) {}
+
+ClipboardData::ClipboardData(const ClipboardData& other) {
+ format_ = other.format_;
+ text_ = other.text_;
+ markup_data_ = other.markup_data_;
+ url_ = other.url_;
+ rtf_data_ = other.rtf_data_;
+ bitmap_ = other.bitmap();
+ bookmark_title_ = other.bookmark_title_;
+ bookmark_url_ = other.bookmark_url_;
+ custom_data_format_ = other.custom_data_format_;
+ custom_data_data_ = other.custom_data_data_;
+ web_smart_paste_ = other.web_smart_paste_;
+ src_ = other.src_ ? std::make_unique<ClipboardDataEndpoint>(*other.src_.get())
+ : nullptr;
+}
+
+ClipboardData::~ClipboardData() = default;
+
+ClipboardData::ClipboardData(ClipboardData&&) = default;
+
+bool ClipboardData::operator==(const ClipboardData& that) const {
+ return format_ == that.format() && text_ == that.text() &&
+ markup_data_ == that.markup_data() && url_ == that.url() &&
+ rtf_data_ == that.rtf_data() &&
+ bookmark_title_ == that.bookmark_title() &&
+ bookmark_url_ == that.bookmark_url() &&
+ custom_data_format_ == that.custom_data_format() &&
+ custom_data_data_ == that.custom_data_data() &&
+ web_smart_paste_ == that.web_smart_paste() &&
+ gfx::BitmapsAreEqual(bitmap_, that.bitmap()) &&
+ (src_.get() ? (that.source() && *src_.get() == *that.source())
+ : !that.source());
+}
+
+bool ClipboardData::operator!=(const ClipboardData& that) const {
+ return !(*this == that);
+}
+
+void ClipboardData::SetBitmapData(const SkBitmap& bitmap) {
+ if (!skia::SkBitmapToN32OpaqueOrPremul(bitmap, &bitmap_)) {
+ NOTREACHED() << "Unable to convert bitmap for clipboard";
+ return;
+ }
+ format_ |= static_cast<int>(ClipboardInternalFormat::kBitmap);
+}
+
+void ClipboardData::SetCustomData(const std::string& data_format,
+ const std::string& data_data) {
+ if (data_data.size() == 0) {
+ custom_data_data_.clear();
+ custom_data_format_.clear();
+ return;
+ }
+ custom_data_data_ = data_data;
+ custom_data_format_ = data_format;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kCustom);
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/clipboard/clipboard_data.h b/chromium/ui/base/clipboard/clipboard_data.h
new file mode 100644
index 00000000000..edbd558f6d6
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_data.h
@@ -0,0 +1,135 @@
+// 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 UI_BASE_CLIPBOARD_CLIPBOARD_DATA_H_
+#define UI_BASE_CLIPBOARD_CLIPBOARD_DATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+
+class SkBitmap;
+
+namespace ui {
+
+// Clipboard data format used by ClipboardInternal.
+enum class ClipboardInternalFormat {
+ kText = 1 << 0,
+ kHtml = 1 << 1,
+ kRtf = 1 << 2,
+ kBookmark = 1 << 3,
+ kBitmap = 1 << 4,
+ kCustom = 1 << 5,
+ kWeb = 1 << 6,
+};
+
+// ClipboardData contains data copied to the Clipboard for a variety of formats.
+// It mostly just provides APIs to cleanly access and manipulate this data.
+class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardData {
+ public:
+ ClipboardData();
+ explicit ClipboardData(const ClipboardData&);
+ ClipboardData(ClipboardData&&);
+ ClipboardData& operator=(const ClipboardData&) = delete;
+ ~ClipboardData();
+
+ bool operator==(const ClipboardData& that) const;
+ bool operator!=(const ClipboardData& that) const;
+
+ // Bitmask of ClipboardInternalFormat types.
+ int format() const { return format_; }
+
+ const std::string& text() const { return text_; }
+ void set_text(const std::string& text) {
+ text_ = text;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kText);
+ }
+
+ const std::string& markup_data() const { return markup_data_; }
+ void set_markup_data(const std::string& markup_data) {
+ markup_data_ = markup_data;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kHtml);
+ }
+
+ const std::string& rtf_data() const { return rtf_data_; }
+ void SetRTFData(const std::string& rtf_data) {
+ rtf_data_ = rtf_data;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kRtf);
+ }
+
+ const std::string& url() const { return url_; }
+ void set_url(const std::string& url) {
+ url_ = url;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kHtml);
+ }
+
+ const std::string& bookmark_title() const { return bookmark_title_; }
+ void set_bookmark_title(const std::string& bookmark_title) {
+ bookmark_title_ = bookmark_title;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kBookmark);
+ }
+
+ const std::string& bookmark_url() const { return bookmark_url_; }
+ void set_bookmark_url(const std::string& bookmark_url) {
+ bookmark_url_ = bookmark_url;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kBookmark);
+ }
+
+ const SkBitmap& bitmap() const { return bitmap_; }
+ void SetBitmapData(const SkBitmap& bitmap);
+
+ const std::string& custom_data_format() const { return custom_data_format_; }
+ const std::string& custom_data_data() const { return custom_data_data_; }
+ void SetCustomData(const std::string& data_format,
+ const std::string& data_data);
+
+ bool web_smart_paste() const { return web_smart_paste_; }
+ void set_web_smart_paste(bool web_smart_paste) {
+ web_smart_paste_ = web_smart_paste;
+ format_ |= static_cast<int>(ClipboardInternalFormat::kWeb);
+ }
+
+ ClipboardDataEndpoint* source() const { return src_.get(); }
+
+ void set_source(std::unique_ptr<ClipboardDataEndpoint> src) {
+ src_ = std::move(src);
+ }
+
+ private:
+ // Plain text in UTF8 format.
+ std::string text_;
+
+ // HTML markup data in UTF8 format.
+ std::string markup_data_;
+ std::string url_;
+
+ // RTF data.
+ std::string rtf_data_;
+
+ // Bookmark title in UTF8 format.
+ std::string bookmark_title_;
+ std::string bookmark_url_;
+
+ // Bitmap images.
+ SkBitmap bitmap_;
+
+ // Data with custom format.
+ std::string custom_data_format_;
+ std::string custom_data_data_;
+
+ // WebKit smart paste data.
+ bool web_smart_paste_;
+
+ int format_;
+
+ // The source of the data.
+ std::unique_ptr<ClipboardDataEndpoint> src_ = nullptr;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_CLIPBOARD_CLIPBOARD_DATA_H_
diff --git a/chromium/ui/base/clipboard/clipboard_data_endpoint.cc b/chromium/ui/base/clipboard/clipboard_data_endpoint.cc
new file mode 100644
index 00000000000..e7160b0e0a4
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_data_endpoint.cc
@@ -0,0 +1,34 @@
+// 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 "ui/base/clipboard/clipboard_data_endpoint.h"
+
+#include "base/check_op.h"
+#include "base/optional.h"
+#include "url/origin.h"
+
+namespace ui {
+
+ClipboardDataEndpoint::ClipboardDataEndpoint(const GURL& url)
+ : type_(EndpointType::kUrl), url_(url) {}
+
+ClipboardDataEndpoint::ClipboardDataEndpoint(EndpointType type)
+ : type_(type), url_(base::nullopt) {
+ DCHECK_NE(type, EndpointType::kUrl);
+}
+
+ClipboardDataEndpoint::ClipboardDataEndpoint(
+ const ClipboardDataEndpoint& other) = default;
+
+ClipboardDataEndpoint::ClipboardDataEndpoint(ClipboardDataEndpoint&& other) =
+ default;
+
+bool ClipboardDataEndpoint::operator==(
+ const ClipboardDataEndpoint& other) const {
+ return url_ == other.url_ && type_ == other.type_;
+}
+
+ClipboardDataEndpoint::~ClipboardDataEndpoint() = default;
+
+} // namespace ui
diff --git a/chromium/ui/base/clipboard/clipboard_data_endpoint.h b/chromium/ui/base/clipboard/clipboard_data_endpoint.h
new file mode 100644
index 00000000000..787c9a0b47a
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_data_endpoint.h
@@ -0,0 +1,58 @@
+// 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 UI_BASE_CLIPBOARD_CLIPBOARD_DATA_ENDPOINT_H_
+#define UI_BASE_CLIPBOARD_CLIPBOARD_DATA_ENDPOINT_H_
+
+#include "base/optional.h"
+#include "base/stl_util.h"
+#include "url/gurl.h"
+
+namespace ui {
+
+// EndpointType can represent either the source of the clipboard data or the
+// destination trying to read the clipboard data.
+// Whenever a new format is supported, a new enum should be added.
+enum class EndpointType {
+ kUrl = 0, // Website URL e.g. www.example.com
+ kVm = 1, // Virtual machine: ARC++, PluginVM, Crostini.
+ kMaxValue = kVm
+};
+
+// ClipboardDataEndpoint can represent:
+// - The source of the data in the clipboard.
+// - The destination trying to access the clipboard data.
+class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardDataEndpoint {
+ public:
+ explicit ClipboardDataEndpoint(const GURL& url);
+ // This constructor shouldn't be used if |type| == EndpointType::kUrl.
+ explicit ClipboardDataEndpoint(EndpointType type);
+
+ ClipboardDataEndpoint(const ClipboardDataEndpoint& other);
+ ClipboardDataEndpoint(ClipboardDataEndpoint&& other);
+
+ ClipboardDataEndpoint& operator=(const ClipboardDataEndpoint& other) = delete;
+ ClipboardDataEndpoint& operator=(ClipboardDataEndpoint&& other) = delete;
+
+ bool operator==(const ClipboardDataEndpoint& other) const;
+
+ ~ClipboardDataEndpoint();
+
+ bool IsUrlType() const { return type_ == EndpointType::kUrl; }
+
+ const GURL* url() const { return base::OptionalOrNullptr(url_); }
+
+ EndpointType type() const { return type_; }
+
+ private:
+ // This variable should always have a value representing the object type.
+ const EndpointType type_;
+ // The url of the data endpoint. It always has a value if |type_| ==
+ // EndpointType::URL, otherwise it's empty.
+ const base::Optional<GURL> url_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_CLIPBOARD_CLIPBOARD_DATA_ENDPOINT_H_
diff --git a/chromium/ui/base/clipboard/clipboard_data_unittest.cc b/chromium/ui/base/clipboard/clipboard_data_unittest.cc
new file mode 100644
index 00000000000..3d85c0cc2c3
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_data_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 "ui/base/clipboard/clipboard_data.h"
+
+#include <memory>
+
+#include "base/strings/string_piece_forward.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+
+namespace ui {
+
+// Tests that two ClipboardData objects won't be equal if they don't have the
+// same bitmap.
+TEST(ClipboardDataTest, BitMapTest) {
+ ClipboardData data1;
+ SkBitmap test_bitmap;
+ test_bitmap.allocN32Pixels(3, 2);
+ test_bitmap.eraseARGB(255, 0, 255, 0);
+ data1.SetBitmapData(test_bitmap);
+
+ ClipboardData data2;
+ EXPECT_NE(data1, data2);
+
+ data2.SetBitmapData(test_bitmap);
+ EXPECT_EQ(data1, data2);
+}
+
+// Tests that two ClipboardData objects won't be equal if they don't have the
+// same data source.
+TEST(ClipboardDataTest, DataSrcTest) {
+ GURL url("www.example.com");
+ ClipboardData data1;
+ data1.set_source(std::make_unique<ClipboardDataEndpoint>(url));
+
+ ClipboardData data2;
+ EXPECT_NE(data1, data2);
+
+ data2.set_source(std::make_unique<ClipboardDataEndpoint>(url));
+ EXPECT_EQ(data1, data2);
+}
+
+} // namespace ui \ No newline at end of file
diff --git a/chromium/ui/base/clipboard/clipboard_dlp_controller.h b/chromium/ui/base/clipboard/clipboard_dlp_controller.h
new file mode 100644
index 00000000000..bf4447bf664
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_dlp_controller.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_CLIPBOARD_CLIPBOARD_DLP_CONTROLLER_H_
+#define UI_BASE_CLIPBOARD_CLIPBOARD_DLP_CONTROLLER_H_
+
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+
+namespace ui {
+
+// The Clipboard Data Leak Prevention controller is used to control clipboard
+// read operations. It should allow/disallow clipboard data read given the
+// source of the data and the destination trying to access the data and a set of
+// rules controlling these source and destination.
+class ClipboardDlpController {
+ public:
+ ClipboardDlpController() = default;
+ virtual ~ClipboardDlpController() = default;
+
+ virtual bool IsDataReadAllowed(
+ const ClipboardDataEndpoint* const data_src,
+ const ClipboardDataEndpoint* const data_dst) const = 0;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_CLIPBOARD_CLIPBOARD_DLP_CONTROLLER_H_
diff --git a/chromium/ui/base/clipboard/clipboard_format_type.h b/chromium/ui/base/clipboard/clipboard_format_type.h
index 66d4703adba..17480a7d030 100644
--- a/chromium/ui/base/clipboard/clipboard_format_type.h
+++ b/chromium/ui/base/clipboard/clipboard_format_type.h
@@ -17,13 +17,13 @@
#include <objidl.h>
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#ifdef __OBJC__
@class NSString;
#else
class NSString;
#endif
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
namespace ui {
@@ -94,14 +94,14 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType {
std::string GetName() const;
#if defined(OS_WIN)
const FORMATETC& ToFormatEtc() const { return data_; }
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
NSString* ToNSString() const { return data_; }
// Custom copy and assignment constructor to handle NSString.
ClipboardFormatType(const ClipboardFormatType& other);
ClipboardFormatType& operator=(const ClipboardFormatType& other);
#endif
- bool Equals(const ClipboardFormatType& other) const;
+ bool operator==(const ClipboardFormatType& other) const;
private:
friend class base::NoDestructor<ClipboardFormatType>;
@@ -133,7 +133,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType {
#elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
explicit ClipboardFormatType(const std::string& native_format);
std::string data_;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
explicit ClipboardFormatType(NSString* native_format);
NSString* data_;
#else
diff --git a/chromium/ui/base/clipboard/clipboard_format_type_android.cc b/chromium/ui/base/clipboard/clipboard_format_type_android.cc
index 5e48d0d7eca..07564e07957 100644
--- a/chromium/ui/base/clipboard/clipboard_format_type_android.cc
+++ b/chromium/ui/base/clipboard/clipboard_format_type_android.cc
@@ -34,7 +34,7 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const {
return data_ < other.data_;
}
-bool ClipboardFormatType::Equals(const ClipboardFormatType& other) const {
+bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
return data_ == other.data_;
}
diff --git a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc
index b824f50bcf4..0ea472a7847 100644
--- a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc
+++ b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc
@@ -41,7 +41,7 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const {
return data_ < other.data_;
}
-bool ClipboardFormatType::Equals(const ClipboardFormatType& other) const {
+bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
return data_ == other.data_;
}
diff --git a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm
index b86101f64b8..dce0ff7dfa5 100644
--- a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm
+++ b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm
@@ -29,7 +29,7 @@ ClipboardFormatType& ClipboardFormatType::operator=(
return *this;
}
-bool ClipboardFormatType::Equals(const ClipboardFormatType& other) const {
+bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
return [data_ isEqualToString:other.data_];
}
diff --git a/chromium/ui/base/clipboard/clipboard_format_type_win.cc b/chromium/ui/base/clipboard/clipboard_format_type_win.cc
index 36c0c8b182d..ef2e7f4c73d 100644
--- a/chromium/ui/base/clipboard/clipboard_format_type_win.cc
+++ b/chromium/ui/base/clipboard/clipboard_format_type_win.cc
@@ -141,7 +141,7 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const {
return data_.cfFormat < other.data_.cfFormat;
}
-bool ClipboardFormatType::Equals(const ClipboardFormatType& other) const {
+bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
return data_.cfFormat == other.data_.cfFormat;
}
diff --git a/chromium/ui/base/clipboard/clipboard_mac.h b/chromium/ui/base/clipboard/clipboard_mac.h
index 507a7f8c786..1189e05278c 100644
--- a/chromium/ui/base/clipboard/clipboard_mac.h
+++ b/chromium/ui/base/clipboard/clipboard_mac.h
@@ -31,38 +31,56 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard {
// Clipboard overrides:
void OnPreShutdown() override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
bool IsMarkedByOriginatorAsConfidential() const override;
void MarkAsConfidential() override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/clipboard_mac.mm b/chromium/ui/base/clipboard/clipboard_mac.mm
index c396f9effea..3faea41c500 100644
--- a/chromium/ui/base/clipboard/clipboard_mac.mm
+++ b/chromium/ui/base/clipboard/clipboard_mac.mm
@@ -24,6 +24,7 @@
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_util_mac.h"
#include "ui/base/clipboard/custom_data_helper.h"
@@ -69,8 +70,17 @@ uint64_t ClipboardMac::GetSequenceNumber(ClipboardBuffer buffer) const {
return [pb changeCount];
}
-bool ClipboardMac::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+void ClipboardMac::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+bool ClipboardMac::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -115,18 +125,22 @@ void ClipboardMac::Clear(ClipboardBuffer buffer) {
[pb declareTypes:@[] owner:nil];
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
types->clear();
- if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
+ data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeText));
- if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
- if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
NSPasteboard* pb = GetPasteboard();
@@ -140,9 +154,12 @@ void ClipboardMac::ReadAvailableTypes(
}
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
std::vector<base::string16>
ClipboardMac::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -156,7 +173,10 @@ ClipboardMac::ReadAvailablePlatformSpecificFormatNames(
return type_names;
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -167,7 +187,10 @@ void ClipboardMac::ReadText(ClipboardBuffer buffer,
*result = base::SysNSStringToUTF16(contents);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -181,7 +204,10 @@ void ClipboardMac::ReadAsciiText(ClipboardBuffer buffer,
result->assign([contents UTF8String]);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -213,22 +239,32 @@ void ClipboardMac::ReadHTML(ClipboardBuffer buffer,
*fragment_end = static_cast<uint32_t>(markup->length());
}
-void ClipboardMac::ReadRTF(ClipboardBuffer buffer, std::string* result) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardMac::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
RecordRead(ClipboardFormatMetric::kRtf);
- return ReadData(ClipboardFormatType::GetRtfType(), result);
+ return ReadData(ClipboardFormatType::GetRtfType(), data_dst, result);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(ReadImageInternal(buffer, GetPasteboard()));
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -242,7 +278,11 @@ void ClipboardMac::ReadCustomData(ClipboardBuffer buffer,
}
}
-void ClipboardMac::ReadBookmark(base::string16* title, std::string* url) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardMac::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kBookmark);
NSPasteboard* pb = GetPasteboard();
@@ -261,7 +301,10 @@ void ClipboardMac::ReadBookmark(base::string16* title, std::string* url) const {
}
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kData);
@@ -271,8 +314,12 @@ void ClipboardMac::ReadData(const ClipboardFormatType& format,
result->assign(static_cast<const char*>([data bytes]), [data length]);
}
-void ClipboardMac::WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) {
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardMac::WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
@@ -283,9 +330,12 @@ void ClipboardMac::WritePortableRepresentations(ClipboardBuffer buffer,
DispatchPortableRepresentation(object.first, object.second);
}
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardMac::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.cc b/chromium/ui/base/clipboard/clipboard_non_backed.cc
index 2b827891f6d..31cf5d946b1 100644
--- a/chromium/ui/base/clipboard/clipboard_non_backed.cc
+++ b/chromium/ui/base/clipboard/clipboard_non_backed.cc
@@ -9,6 +9,7 @@
#include <limits>
#include <list>
#include <memory>
+#include <set>
#include <utility>
#include "base/check_op.h"
@@ -18,9 +19,13 @@
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
#include "skia/ext/skia_utils_base.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+#include "ui/base/clipboard/clipboard_dlp_controller.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_monitor.h"
@@ -32,174 +37,87 @@ namespace ui {
namespace {
-const size_t kMaxClipboardSize = 1;
-
-// Clipboard data format used by ClipboardInternal.
-enum class ClipboardInternalFormat {
- kText = 1 << 0,
- kHtml = 1 << 1,
- kRtf = 1 << 2,
- kBookmark = 1 << 3,
- kBitmap = 1 << 4,
- kCustom = 1 << 5,
- kWeb = 1 << 6,
-};
-
-// ClipboardData contains data copied to the Clipboard for a variety of formats.
-// It mostly just provides APIs to cleanly access and manipulate this data.
-class ClipboardData {
- public:
- ClipboardData() : web_smart_paste_(false), format_(0) {}
-
- virtual ~ClipboardData() = default;
-
- // Bitmask of ClipboardInternalFormat types.
- int format() const { return format_; }
-
- const std::string& text() const { return text_; }
- void set_text(const std::string& text) {
- text_ = text;
- format_ |= static_cast<int>(ClipboardInternalFormat::kText);
- }
-
- const std::string& markup_data() const { return markup_data_; }
- void set_markup_data(const std::string& markup_data) {
- markup_data_ = markup_data;
- format_ |= static_cast<int>(ClipboardInternalFormat::kHtml);
- }
-
- const std::string& rtf_data() const { return rtf_data_; }
- void SetRTFData(const std::string& rtf_data) {
- rtf_data_ = rtf_data;
- format_ |= static_cast<int>(ClipboardInternalFormat::kRtf);
- }
-
- const std::string& url() const { return url_; }
- void set_url(const std::string& url) {
- url_ = url;
- format_ |= static_cast<int>(ClipboardInternalFormat::kHtml);
- }
-
- const std::string& bookmark_title() const { return bookmark_title_; }
- void set_bookmark_title(const std::string& bookmark_title) {
- bookmark_title_ = bookmark_title;
- format_ |= static_cast<int>(ClipboardInternalFormat::kBookmark);
- }
-
- const std::string& bookmark_url() const { return bookmark_url_; }
- void set_bookmark_url(const std::string& bookmark_url) {
- bookmark_url_ = bookmark_url;
- format_ |= static_cast<int>(ClipboardInternalFormat::kBookmark);
- }
-
- const SkBitmap& bitmap() const { return bitmap_; }
- void SetBitmapData(const SkBitmap& bitmap) {
- if (!skia::SkBitmapToN32OpaqueOrPremul(bitmap, &bitmap_)) {
- NOTREACHED() << "Unable to convert bitmap for clipboard";
- return;
- }
- format_ |= static_cast<int>(ClipboardInternalFormat::kBitmap);
- }
-
- const std::string& custom_data_format() const { return custom_data_format_; }
- const std::string& custom_data_data() const { return custom_data_data_; }
- void SetCustomData(const std::string& data_format,
- const std::string& data_data) {
- if (data_data.size() == 0) {
- custom_data_data_.clear();
- custom_data_format_.clear();
- return;
- }
- custom_data_data_ = data_data;
- custom_data_format_ = data_format;
- format_ |= static_cast<int>(ClipboardInternalFormat::kCustom);
- }
-
- bool web_smart_paste() const { return web_smart_paste_; }
- void set_web_smart_paste(bool web_smart_paste) {
- web_smart_paste_ = web_smart_paste;
- format_ |= static_cast<int>(ClipboardInternalFormat::kWeb);
- }
-
- private:
- // Plain text in UTF8 format.
- std::string text_;
-
- // HTML markup data in UTF8 format.
- std::string markup_data_;
- std::string url_;
-
- // RTF data.
- std::string rtf_data_;
-
- // Bookmark title in UTF8 format.
- std::string bookmark_title_;
- std::string bookmark_url_;
-
- // Filenames.
- std::vector<std::string> files_;
-
- // Bitmap images.
- SkBitmap bitmap_;
+// Returns the registry which tracks all instances of ClipboardNonBacked in
+// existence. This allows us to determine if any arbitrary Clipboard pointer in
+// fact points to a ClipboardNonBacked instance. Only if a pointer exists in
+// this registry is it safe to cast to ClipboardNonBacked*.
+std::set<const ClipboardNonBacked*>* GetInstanceRegistry() {
+ static base::NoDestructor<std::set<const ClipboardNonBacked*>> registry;
+ return registry.get();
+}
- // Data with custom format.
- std::string custom_data_format_;
- std::string custom_data_data_;
+// The ClipboardNonBacked instance registry can be accessed by multiple threads.
+// Any inspection/modification of the instance registry must only be performed
+// while this lock is held.
+base::Lock& GetInstanceRegistryLock() {
+ static base::NoDestructor<base::Lock> registry_lock;
+ return *registry_lock;
+}
- // WebKit smart paste data.
- bool web_smart_paste_;
+// Registers the specified |clipboard| as an instance of ClipboardNonBacked.
+// Registration should occur during |clipboard| construction and registration
+// should be maintained until |clipboard| is destroyed. This allows us to check
+// if any arbitrary Clipboard* is safe to cast.
+void RegisterInstance(const ClipboardNonBacked* clipboard) {
+ base::AutoLock lock(GetInstanceRegistryLock());
+ GetInstanceRegistry()->insert(clipboard);
+}
- int format_;
+// Unregisters the specified |clipboard| as a instance of ClipboardNonBacked.
+// This should only be done when destroying |clipboard|.
+void UnregisterInstance(const ClipboardNonBacked* clipboard) {
+ base::AutoLock lock(GetInstanceRegistryLock());
+ GetInstanceRegistry()->erase(clipboard);
+}
- DISALLOW_COPY_AND_ASSIGN(ClipboardData);
-};
+// Checks if |clipboard| is registered as an instance of ClipboardNonBacked.
+// Only if this method returns true is it safe to cast |clipboard| to
+// ClipboardNonBacked*.
+bool IsRegisteredInstance(const Clipboard* clipboard) {
+ base::AutoLock lock(GetInstanceRegistryLock());
+ return base::Contains(*GetInstanceRegistry(), clipboard);
+}
} // namespace
// Simple, internal implementation of a clipboard, handling things like format
-// conversion, versioning, etc.
+// conversion, sequence numbers, etc.
class ClipboardInternal {
public:
- ClipboardInternal() : sequence_number_(0) {}
+ ClipboardInternal() = default;
~ClipboardInternal() = default;
void Clear() {
- sequence_number_++;
- data_list_.clear();
+ ++sequence_number_;
+ data_.reset();
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
uint64_t sequence_number() const { return sequence_number_; }
- // Returns the data currently on the top of the clipboard stack, nullptr if
- // the clipboard stack is empty.
- const ClipboardData* GetData() const {
- if (data_list_.empty())
- return nullptr;
- return data_list_.front().get();
- }
+ // Returns the current clipboard data, which may be nullptr if nothing has
+ // been written since the last Clear().
+ const ClipboardData* GetData() const { return data_.get(); }
// Returns true if the data on top of the clipboard stack has format |format|
// or another format that can be converted to |format|.
bool IsFormatAvailable(ClipboardInternalFormat format) const {
- switch (format) {
- case ClipboardInternalFormat::kText:
- return HasFormat(ClipboardInternalFormat::kText) ||
- HasFormat(ClipboardInternalFormat::kBookmark);
- default:
- return HasFormat(format);
+ if (format == ClipboardInternalFormat::kText) {
+ return HasFormat(ClipboardInternalFormat::kText) ||
+ HasFormat(ClipboardInternalFormat::kBookmark);
}
+ return HasFormat(format);
}
- // Reads text from the data at the top of clipboard stack.
+ // Reads text from the ClipboardData.
void ReadText(base::string16* result) const {
std::string utf8_result;
ReadAsciiText(&utf8_result);
*result = base::UTF8ToUTF16(utf8_result);
}
- // Reads ASCII text from the data at the top of clipboard stack.
+ // Reads ASCII text from the ClipboardData.
void ReadAsciiText(std::string* result) const {
result->clear();
const ClipboardData* data = GetData();
@@ -213,7 +131,7 @@ class ClipboardInternal {
*result = data->bookmark_url();
}
- // Reads HTML from the data at the top of clipboard stack.
+ // Reads HTML from the ClipboardData.
void ReadHTML(base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -236,7 +154,7 @@ class ClipboardInternal {
*fragment_end = static_cast<uint32_t>(markup->length());
}
- // Reads RTF from the data at the top of clipboard stack.
+ // Reads RTF from the ClipboardData.
void ReadRTF(std::string* result) const {
result->clear();
const ClipboardData* data = GetData();
@@ -246,7 +164,7 @@ class ClipboardInternal {
*result = data->rtf_data();
}
- // Reads image from the data at the top of clipboard stack.
+ // Reads image from the ClipboardData.
SkBitmap ReadImage() const {
SkBitmap img;
if (!HasFormat(ClipboardInternalFormat::kBitmap))
@@ -261,7 +179,7 @@ class ClipboardInternal {
return img;
}
- // Reads data of type |type| from the data at the top of clipboard stack.
+ // Reads data of type |type| from the ClipboardData.
void ReadCustomData(const base::string16& type,
base::string16* result) const {
result->clear();
@@ -273,7 +191,7 @@ class ClipboardInternal {
data->custom_data_data().size(), type, result);
}
- // Reads bookmark from the data at the top of clipboard stack.
+ // Reads bookmark from the ClipboardData.
void ReadBookmark(base::string16* title, std::string* url) const {
if (title)
title->clear();
@@ -299,37 +217,43 @@ class ClipboardInternal {
*result = data->custom_data_data();
}
- // Writes |data| to the top of the clipboard stack.
- void WriteData(std::unique_ptr<ClipboardData> data) {
+ // Writes |data| to the ClipboardData and returns the previous data.
+ std::unique_ptr<ClipboardData> WriteData(
+ std::unique_ptr<ClipboardData> data) {
DCHECK(data);
- AddToListEnsuringSize(std::move(data));
+ std::unique_ptr<ClipboardData> previous_data = std::move(data_);
+ data_ = std::move(data);
+ ++sequence_number_;
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
+ return previous_data;
+ }
+
+ void SetDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ dlp_controller_ = std::move(dlp_controller);
+ }
+
+ bool IsReadAllowed(const ClipboardDataEndpoint* data_dst) const {
+ if (!dlp_controller_)
+ return true;
+ return dlp_controller_->IsDataReadAllowed(GetData()->source(), data_dst);
}
private:
- // True if the data on top of the clipboard stack has format |format|.
+ // True if the ClipboardData has format |format|.
bool HasFormat(ClipboardInternalFormat format) const {
const ClipboardData* data = GetData();
return data ? data->format() & static_cast<int>(format) : false;
}
- void AddToListEnsuringSize(std::unique_ptr<ClipboardData> data) {
- DCHECK(data);
- sequence_number_++;
- data_list_.push_front(std::move(data));
-
- // If the size of list becomes more than the maximum allowed, we delete the
- // last element.
- if (data_list_.size() > kMaxClipboardSize) {
- data_list_.pop_back();
- }
- }
-
- // Stack containing various versions of ClipboardData.
- std::list<std::unique_ptr<ClipboardData>> data_list_;
+ // Current ClipboardData.
+ std::unique_ptr<ClipboardData> data_;
// Sequence number uniquely identifying clipboard state.
- uint64_t sequence_number_;
+ uint64_t sequence_number_ = 0;
+
+ // Data-leak prevention controller controlling clipboard read operations.
+ std::unique_ptr<ClipboardDlpController> dlp_controller_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ClipboardInternal);
};
@@ -337,7 +261,13 @@ class ClipboardInternal {
// Helper class to build a ClipboardData object and write it to clipboard.
class ClipboardDataBuilder {
public:
- static void CommitToClipboard(ClipboardInternal* clipboard) {
+ // If |data_src| is nullptr, this means that the data source isn't
+ // confidential and the data can be pasted in any document.
+ static void CommitToClipboard(
+ ClipboardInternal* clipboard,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
+ ClipboardData* data = GetCurrentData();
+ data->set_source(std::move(data_src));
clipboard->WriteData(TakeCurrentData());
}
@@ -414,41 +344,72 @@ Clipboard* Clipboard::Create() {
}
#endif
+// static
+ClipboardNonBacked* ClipboardNonBacked::GetForCurrentThread() {
+ auto* clipboard = Clipboard::GetForCurrentThread();
+ CHECK(IsRegisteredInstance(clipboard)); // Ensure type safety.
+ return static_cast<ClipboardNonBacked*>(clipboard);
+}
+
// ClipboardNonBacked implementation.
ClipboardNonBacked::ClipboardNonBacked()
: clipboard_internal_(std::make_unique<ClipboardInternal>()) {
DCHECK(CalledOnValidThread());
+ RegisterInstance(this);
}
ClipboardNonBacked::~ClipboardNonBacked() {
DCHECK(CalledOnValidThread());
+ UnregisterInstance(this);
+}
+
+const ClipboardData* ClipboardNonBacked::GetClipboardData() const {
+ DCHECK(CalledOnValidThread());
+ return clipboard_internal_->GetData();
+}
+
+std::unique_ptr<ClipboardData> ClipboardNonBacked::WriteClipboardData(
+ std::unique_ptr<ClipboardData> data) {
+ DCHECK(CalledOnValidThread());
+ return clipboard_internal_->WriteData(std::move(data));
}
void ClipboardNonBacked::OnPreShutdown() {}
+void ClipboardNonBacked::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ clipboard_internal_->SetDlpController(std::move(dlp_controller));
+}
+
uint64_t ClipboardNonBacked::GetSequenceNumber(ClipboardBuffer buffer) const {
DCHECK(CalledOnValidThread());
return clipboard_internal_->sequence_number();
}
-bool ClipboardNonBacked::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+bool ClipboardNonBacked::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
- if (ClipboardFormatType::GetPlainTextType().Equals(format) ||
- ClipboardFormatType::GetUrlType().Equals(format))
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return false;
+
+ if (format == ClipboardFormatType::GetPlainTextType() ||
+ format == ClipboardFormatType::GetUrlType())
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kText);
- if (ClipboardFormatType::GetHtmlType().Equals(format))
+ if (format == ClipboardFormatType::GetHtmlType())
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kHtml);
- if (ClipboardFormatType::GetRtfType().Equals(format))
+ if (format == ClipboardFormatType::GetRtfType())
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kRtf);
- if (ClipboardFormatType::GetBitmapType().Equals(format))
+ if (format == ClipboardFormatType::GetBitmapType())
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kBitmap);
- if (ClipboardFormatType::GetWebKitSmartPasteType().Equals(format))
+ if (format == ClipboardFormatType::GetWebKitSmartPasteType())
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kWeb);
const ClipboardData* data = clipboard_internal_->GetData();
@@ -463,21 +424,26 @@ void ClipboardNonBacked::Clear(ClipboardBuffer buffer) {
void ClipboardNonBacked::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
types->clear();
- if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
+ data_dst))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName()));
- if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName()));
- if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName()));
- if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer))
+ if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst))
types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
if (clipboard_internal_->IsFormatAvailable(
@@ -491,25 +457,31 @@ void ClipboardNonBacked::ReadAvailableTypes(
std::vector<base::string16>
ClipboardNonBacked::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
std::vector<base::string16> types;
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return types;
+
// Includes all non-pickled AvailableTypes.
- if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer)) {
+ if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
+ data_dst)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName()));
}
- if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer)) {
+ if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName()));
}
- if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer)) {
+ if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName()));
}
- if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer)) {
+ if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer,
+ data_dst)) {
types.push_back(base::UTF8ToUTF16(kMimeTypePNG));
}
@@ -517,84 +489,132 @@ ClipboardNonBacked::ReadAvailablePlatformSpecificFormatNames(
}
void ClipboardNonBacked::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kText);
clipboard_internal_->ReadText(result);
}
void ClipboardNonBacked::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kText);
clipboard_internal_->ReadAsciiText(result);
}
void ClipboardNonBacked::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kHtml);
clipboard_internal_->ReadHTML(markup, src_url, fragment_start, fragment_end);
}
void ClipboardNonBacked::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kRtf);
clipboard_internal_->ReadRTF(result);
}
void ClipboardNonBacked::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(clipboard_internal_->ReadImage());
}
void ClipboardNonBacked::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kCustomData);
clipboard_internal_->ReadCustomData(type, result);
}
-void ClipboardNonBacked::ReadBookmark(base::string16* title,
+void ClipboardNonBacked::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
std::string* url) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kBookmark);
clipboard_internal_->ReadBookmark(title, url);
}
void ClipboardNonBacked::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
+
+ if (!clipboard_internal_->IsReadAllowed(data_dst))
+ return;
+
RecordRead(ClipboardFormatMetric::kData);
clipboard_internal_->ReadData(format.GetName(), result);
}
+bool ClipboardNonBacked::IsSelectionBufferAvailable() const {
+ return false;
+}
+
void ClipboardNonBacked::WritePortableRepresentations(
ClipboardBuffer buffer,
- const ObjectMap& objects) {
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
for (const auto& object : objects)
DispatchPortableRepresentation(object.first, object.second);
- ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get());
+ ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(),
+ std::move(data_src));
}
void ClipboardNonBacked::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
DispatchPlatformRepresentations(std::move(platform_representations));
- ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get());
+ ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(),
+ std::move(data_src));
}
void ClipboardNonBacked::WriteText(const char* text_data, size_t text_len) {
diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.h b/chromium/ui/base/clipboard/clipboard_non_backed.h
index 8f57aca1e25..c66c814964f 100644
--- a/chromium/ui/base/clipboard/clipboard_non_backed.h
+++ b/chromium/ui/base/clipboard/clipboard_non_backed.h
@@ -8,11 +8,13 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/component_export.h"
#include "base/macros.h"
#include "ui/base/clipboard/clipboard.h"
namespace ui {
+class ClipboardData;
class ClipboardInternal;
// In-memory clipboard implementation not backed by an underlying platform.
@@ -21,45 +23,79 @@ class ClipboardInternal;
// ClipboardWin on Windows or ClipboardMac on MacOS. As this isn't backed by an
// underlying platform, the clipboard data isn't persisted after an instance
// goes away.
-class ClipboardNonBacked : public Clipboard {
+class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked
+ : public Clipboard {
+ public:
+ // Returns the in-memory clipboard for the current thread. Note that this
+ // method must *only* be used when the caller is sure that the clipboard for
+ // the current thread is in fact an instance of ClipboardNonBacked.
+ static ClipboardNonBacked* GetForCurrentThread();
+
+ // Returns the current ClipboardData.
+ const ClipboardData* GetClipboardData() const;
+
+ // Writes the current ClipboardData and returns the previous data.
+ std::unique_ptr<ClipboardData> WriteClipboardData(
+ std::unique_ptr<ClipboardData> data);
+
private:
friend class Clipboard;
+ friend class ClipboardNonBackedTest;
ClipboardNonBacked();
~ClipboardNonBacked() override;
// Clipboard overrides:
void OnPreShutdown() override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+ bool IsSelectionBufferAvailable() const override;
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc
new file mode 100644
index 00000000000..30a411f56fd
--- /dev/null
+++ b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 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 "ui/base/clipboard/clipboard_non_backed.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard_data.h"
+
+namespace ui {
+
+class ClipboardNonBackedTest : public testing::Test {
+ public:
+ ClipboardNonBackedTest() = default;
+ ClipboardNonBackedTest(const ClipboardNonBackedTest&) = delete;
+ ClipboardNonBackedTest& operator=(const ClipboardNonBackedTest&) = delete;
+ ~ClipboardNonBackedTest() override = default;
+
+ ClipboardNonBacked* clipboard() { return &clipboard_; }
+
+ private:
+ ClipboardNonBacked clipboard_;
+};
+
+// Verifies that GetClipboardData() returns the same instance of ClipboardData
+// as was written via WriteClipboardData().
+TEST_F(ClipboardNonBackedTest, WriteAndGetClipboardData) {
+ auto clipboard_data = std::make_unique<ClipboardData>();
+
+ auto* expected_clipboard_data_ptr = clipboard_data.get();
+ clipboard()->WriteClipboardData(std::move(clipboard_data));
+ auto* actual_clipboard_data_ptr = clipboard()->GetClipboardData();
+
+ EXPECT_EQ(expected_clipboard_data_ptr, actual_clipboard_data_ptr);
+}
+
+// Verifies that WriteClipboardData() writes a ClipboardData instance to the
+// clipboard and returns the previous instance.
+TEST_F(ClipboardNonBackedTest, WriteClipboardData) {
+ auto first_data = std::make_unique<ClipboardData>();
+ auto second_data = std::make_unique<ClipboardData>();
+
+ auto* first_data_ptr = first_data.get();
+ auto* second_data_ptr = second_data.get();
+
+ auto previous_data = clipboard()->WriteClipboardData(std::move(first_data));
+ EXPECT_EQ(previous_data.get(), nullptr);
+
+ previous_data = clipboard()->WriteClipboardData(std::move(second_data));
+
+ EXPECT_EQ(first_data_ptr, previous_data.get());
+ EXPECT_EQ(second_data_ptr, clipboard()->GetClipboardData());
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/clipboard/clipboard_ozone.cc b/chromium/ui/base/clipboard/clipboard_ozone.cc
index a41bc2f5798..5f46a6215cb 100644
--- a/chromium/ui/base/clipboard/clipboard_ozone.cc
+++ b/chromium/ui/base/clipboard/clipboard_ozone.cc
@@ -19,6 +19,7 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/custom_data_helper.h"
@@ -70,6 +71,7 @@ class StubPlatformClipboard : public PlatformClipboard {
bool IsSelectionOwner(ClipboardBuffer buffer) override { return false; }
void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override {}
+ bool IsSelectionBufferAvailable() const override { return false; }
};
} // namespace
@@ -94,6 +96,10 @@ class ClipboardOzone::AsyncClipboardOzone {
~AsyncClipboardOzone() = default;
+ bool IsSelectionBufferAvailable() const {
+ return platform_clipboard_->IsSelectionBufferAvailable();
+ }
+
base::span<uint8_t> ReadClipboardDataAndWait(ClipboardBuffer buffer,
const std::string& mime_type) {
// We can use a fastpath if we are the owner of the selection.
@@ -101,7 +107,7 @@ class ClipboardOzone::AsyncClipboardOzone {
auto it = offered_data_[buffer].find(mime_type);
if (it == offered_data_[buffer].end())
return {};
- return base::make_span(it->second.data(), it->second.size());
+ return base::make_span(it->second->front(), it->second->size());
}
Request request(RequestType::kRead);
@@ -112,7 +118,7 @@ class ClipboardOzone::AsyncClipboardOzone {
auto it = offered_data_[buffer].find(mime_type);
if (it == offered_data_[buffer].end())
return {};
- return base::make_span(it->second.data(), it->second.size());
+ return base::make_span(it->second->front(), it->second->size());
}
std::vector<std::string> RequestMimeTypes(ClipboardBuffer buffer) {
@@ -144,9 +150,14 @@ class ClipboardOzone::AsyncClipboardOzone {
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
- void InsertData(std::vector<uint8_t> data, const std::string& mime_type) {
- DCHECK_EQ(data_to_offer_.count(mime_type), 0U);
- data_to_offer_[mime_type] = std::move(data);
+ void InsertData(std::vector<uint8_t> data,
+ const std::set<std::string>& mime_types) {
+ auto wrapped_data = scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&data));
+ for (const auto& mime_type : mime_types) {
+ DCHECK_EQ(data_to_offer_.count(mime_type), 0U);
+ data_to_offer_[mime_type] = wrapped_data;
+ }
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
@@ -247,7 +258,7 @@ class ClipboardOzone::AsyncClipboardOzone {
platform_clipboard_->GetAvailableMimeTypes(buffer, std::move(callback));
}
- void OnTextRead(const base::Optional<std::vector<uint8_t>>& data) {
+ void OnTextRead(const base::Optional<PlatformClipboard::Data>& data) {
// |data| is already set in request's data_map, so just finish request
// processing.
CompleteRequest();
@@ -287,7 +298,7 @@ class ClipboardOzone::AsyncClipboardOzone {
base::RepeatingTimer abort_timer_;
// Provides communication to a system clipboard under ozone level.
- PlatformClipboard* platform_clipboard_ = nullptr;
+ PlatformClipboard* const platform_clipboard_ = nullptr;
base::flat_map<ClipboardBuffer, uint64_t> clipboard_sequence_number_;
@@ -336,8 +347,17 @@ uint64_t ClipboardOzone::GetSequenceNumber(ClipboardBuffer buffer) const {
return async_clipboard_ozone_->GetSequenceNumber(buffer);
}
-bool ClipboardOzone::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+// TODO(crbug.com/1103194): Setting |dlp_controller| should be supported.
+void ClipboardOzone::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ NOTIMPLEMENTED();
+}
+
+// TODO(crbug.com/1103194): |data_dst| should be supported.
+bool ClipboardOzone::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
auto available_types = async_clipboard_ozone_->RequestMimeTypes(buffer);
@@ -348,8 +368,10 @@ void ClipboardOzone::Clear(ClipboardBuffer buffer) {
async_clipboard_ozone_->Clear(buffer);
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
@@ -371,9 +393,11 @@ void ClipboardOzone::ReadAvailableTypes(
}
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
std::vector<base::string16>
ClipboardOzone::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
std::vector<std::string> mime_types =
@@ -385,7 +409,9 @@ ClipboardOzone::ReadAvailablePlatformSpecificFormatNames(
return types;
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
@@ -396,7 +422,9 @@ void ClipboardOzone::ReadText(ClipboardBuffer buffer,
reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
@@ -406,7 +434,9 @@ void ClipboardOzone::ReadAsciiText(ClipboardBuffer buffer,
result->assign(clipboard_data.begin(), clipboard_data.end());
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -428,7 +458,9 @@ void ClipboardOzone::ReadHTML(ClipboardBuffer buffer,
*fragment_end = static_cast<uint32_t>(markup->length());
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kRtf);
@@ -438,14 +470,18 @@ void ClipboardOzone::ReadRTF(ClipboardBuffer buffer,
result->assign(clipboard_data.begin(), clipboard_data.end());
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(ReadImageInternal(buffer));
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kCustomData);
@@ -455,14 +491,18 @@ void ClipboardOzone::ReadCustomData(ClipboardBuffer buffer,
ReadCustomDataForType(custom_data.data(), custom_data.size(), type, result);
}
-void ClipboardOzone::ReadBookmark(base::string16* title,
+// TODO(crbug.com/1103194): |data_dst| should be supported.
+void ClipboardOzone::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
std::string* url) const {
DCHECK(CalledOnValidThread());
// TODO(msisov): This was left NOTIMPLEMENTED() in all the Linux platforms.
NOTIMPLEMENTED();
}
+// TODO(crbug.com/1103194): |data_dst| should be supported.
void ClipboardOzone::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kData);
@@ -472,8 +512,15 @@ void ClipboardOzone::ReadData(const ClipboardFormatType& format,
result->assign(clipboard_data.begin(), clipboard_data.end());
}
-void ClipboardOzone::WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) {
+bool ClipboardOzone::IsSelectionBufferAvailable() const {
+ return async_clipboard_ozone_->IsSelectionBufferAvailable();
+}
+
+// TODO(crbug.com/1103194): |data_src| should be supported
+void ClipboardOzone::WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
for (const auto& object : objects)
@@ -487,24 +534,21 @@ void ClipboardOzone::WritePortableRepresentations(ClipboardBuffer buffer,
auto text_iter = objects.find(PortableFormat::kText);
if (text_iter != objects.end()) {
const ObjectMapParams& params_vector = text_iter->second;
- if (params_vector.size()) {
+ if (!params_vector.empty()) {
const ObjectMapParam& char_vector = params_vector[0];
- const uint8_t* uint8_data =
- reinterpret_cast<const uint8_t*>(char_vector.data());
- if (char_vector.size()) {
- std::vector<uint8_t> data(uint8_data,
- uint8_data + char_vector.size());
- async_clipboard_ozone_->InsertData(std::move(data), kMimeTypeText);
- }
+ if (!char_vector.empty())
+ WriteText(&char_vector.front(), char_vector.size());
}
async_clipboard_ozone_->OfferData(ClipboardBuffer::kSelection);
}
}
}
+// TODO(crbug.com/1103194): |data_src| should be supported
void ClipboardOzone::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DispatchPlatformRepresentations(std::move(platform_representations));
@@ -513,7 +557,9 @@ void ClipboardOzone::WritePlatformRepresentations(
void ClipboardOzone::WriteText(const char* text_data, size_t text_len) {
std::vector<uint8_t> data(text_data, text_data + text_len);
- async_clipboard_ozone_->InsertData(std::move(data), kMimeTypeText);
+ async_clipboard_ozone_->InsertData(
+ std::move(data), {kMimeTypeText, kMimeTypeLinuxText, kMimeTypeLinuxString,
+ kMimeTypeTextUtf8, kMimeTypeLinuxUtf8String});
}
void ClipboardOzone::WriteHTML(const char* markup_data,
@@ -521,12 +567,12 @@ void ClipboardOzone::WriteHTML(const char* markup_data,
const char* url_data,
size_t url_len) {
std::vector<uint8_t> data(markup_data, markup_data + markup_len);
- async_clipboard_ozone_->InsertData(std::move(data), kMimeTypeHTML);
+ async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeHTML});
}
void ClipboardOzone::WriteRTF(const char* rtf_data, size_t data_len) {
std::vector<uint8_t> data(rtf_data, rtf_data + data_len);
- async_clipboard_ozone_->InsertData(std::move(data), kMimeTypeRTF);
+ async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeRTF});
}
void ClipboardOzone::WriteBookmark(const char* title_data,
@@ -542,25 +588,25 @@ void ClipboardOzone::WriteBookmark(const char* title_data,
std::vector<uint8_t> data(
reinterpret_cast<const uint8_t*>(bookmark.data()),
reinterpret_cast<const uint8_t*>(bookmark.data() + bookmark.size()));
- async_clipboard_ozone_->InsertData(std::move(data), kMimeTypeMozillaURL);
+ async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeMozillaURL});
}
void ClipboardOzone::WriteWebSmartPaste() {
async_clipboard_ozone_->InsertData(std::vector<uint8_t>(),
- kMimeTypeWebkitSmartPaste);
+ {kMimeTypeWebkitSmartPaste});
}
void ClipboardOzone::WriteBitmap(const SkBitmap& bitmap) {
std::vector<unsigned char> output;
if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output))
- async_clipboard_ozone_->InsertData(std::move(output), kMimeTypePNG);
+ async_clipboard_ozone_->InsertData(std::move(output), {kMimeTypePNG});
}
void ClipboardOzone::WriteData(const ClipboardFormatType& format,
const char* data_data,
size_t data_len) {
std::vector<uint8_t> data(data_data, data_data + data_len);
- async_clipboard_ozone_->InsertData(std::move(data), format.GetName());
+ async_clipboard_ozone_->InsertData(std::move(data), {format.GetName()});
}
SkBitmap ClipboardOzone::ReadImageInternal(ClipboardBuffer buffer) const {
diff --git a/chromium/ui/base/clipboard/clipboard_ozone.h b/chromium/ui/base/clipboard/clipboard_ozone.h
index 207ba7ba715..c254b022989 100644
--- a/chromium/ui/base/clipboard/clipboard_ozone.h
+++ b/chromium/ui/base/clipboard/clipboard_ozone.h
@@ -26,36 +26,55 @@ class ClipboardOzone : public Clipboard {
// Clipboard overrides:
void OnPreShutdown() override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+ bool IsSelectionBufferAvailable() const override;
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/clipboard_test_template.h b/chromium/ui/base/clipboard/clipboard_test_template.h
index 279252ce350..c165e64fa6d 100644
--- a/chromium/ui/base/clipboard/clipboard_test_template.h
+++ b/chromium/ui/base/clipboard/clipboard_test_template.h
@@ -23,10 +23,14 @@
#include "base/pickle.h"
#include "base/run_loop.h"
+#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "build/chromecast_buildflags.h"
+#include "build/lacros_buildflags.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -35,6 +39,8 @@
#include "third_party/skia/include/core/SkUnPreMultiply.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+#include "ui/base/clipboard/clipboard_dlp_controller.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/clipboard/test/clipboard_test_util.h"
#include "ui/base/clipboard/test/test_clipboard.h"
@@ -58,6 +64,8 @@ using testing::Contains;
namespace ui {
+class MockClipboardDlpController;
+
template <typename ClipboardTraits>
class ClipboardTest : public PlatformTest {
public:
@@ -84,18 +92,44 @@ class ClipboardTest : public PlatformTest {
std::vector<base::string16> GetAvailableTypes(ClipboardBuffer buffer) {
std::vector<base::string16> types;
- clipboard().ReadAvailableTypes(buffer, &types);
+ clipboard().ReadAvailableTypes(buffer, /* data_dst = */ nullptr, &types);
return types;
}
+ void AddDlpController() {
+ auto dlp_controller = std::make_unique<MockClipboardDlpController>();
+ dlp_controller_ = dlp_controller.get();
+ ClipboardTest::clipboard().SetClipboardDlpController(
+ std::move(dlp_controller));
+ }
+
+ MockClipboardDlpController* dlp_controller() const { return dlp_controller_; }
+
private:
#if defined(USE_X11)
std::unique_ptr<PlatformEventSource> event_source_;
#endif
// Clipboard has a protected destructor, so scoped_ptr doesn't work here.
Clipboard* clipboard_ = nullptr;
+
+ // MockClipboardDlpController object is owned by ClipboardTest.
+ MockClipboardDlpController* dlp_controller_ = nullptr;
};
+// A mock delegate for testing.
+class MockClipboardDlpController : public ClipboardDlpController {
+ public:
+ MockClipboardDlpController();
+ ~MockClipboardDlpController();
+ MOCK_CONST_METHOD2(IsDataReadAllowed,
+ bool(const ClipboardDataEndpoint* const data_src,
+ const ClipboardDataEndpoint* const data_dst));
+};
+
+MockClipboardDlpController::MockClipboardDlpController() = default;
+
+MockClipboardDlpController::~MockClipboardDlpController() = default;
+
// Hack for tests that need to call static methods of ClipboardTest.
struct NullClipboardTraits {
static Clipboard* Create() { return nullptr; }
@@ -115,10 +149,12 @@ TYPED_TEST(ClipboardTest, ClearTest) {
EXPECT_TRUE(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste).empty());
EXPECT_FALSE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#if defined(OS_WIN)
EXPECT_FALSE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#endif
}
@@ -133,17 +169,26 @@ TYPED_TEST(ClipboardTest, TextTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeText)));
+#if defined(USE_OZONE) && !defined(OS_CHROMEOS) && !defined(OS_FUCHSIA) && \
+ !BUILDFLAG(IS_CHROMECAST) && !BUILDFLAG(IS_LACROS)
+ EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
+ Contains(ASCIIToUTF16(kMimeTypeTextUtf8)));
+#endif
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#if defined(OS_WIN)
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#endif
- this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, &text_result);
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result);
EXPECT_EQ(text, text_result);
- this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste, &ascii_text);
+ this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
@@ -161,10 +206,12 @@ TYPED_TEST(ClipboardTest, HTMLTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeHTML)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste, &markup_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &markup_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_LE(markup.size(), fragment_end - fragment_start);
EXPECT_EQ(markup,
@@ -193,17 +240,22 @@ TYPED_TEST(ClipboardTest, RTFTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeRTF)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetRtfType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetRtfType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
std::string result;
- this->clipboard().ReadRTF(ClipboardBuffer::kCopyPaste, &result);
+ this->clipboard().ReadRTF(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &result);
EXPECT_EQ(rtf, result);
}
#endif // !defined(OS_ANDROID)
-// TODO(msisov, tonikitoo): Enable test once ClipboardOzone implements
-// selection support. https://crbug.com/911992
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(USE_OZONE)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
TYPED_TEST(ClipboardTest, MultipleBufferTest) {
+#if defined(USE_OZONE)
+ if (!this->clipboard().IsSelectionBufferAvailable())
+ return;
+#endif
+
base::string16 text(ASCIIToUTF16("Standard")), text_result;
base::string16 markup(ASCIIToUTF16("<string>Selection</string>"));
std::string url("http://www.example.com/"), url_result;
@@ -224,22 +276,28 @@ TYPED_TEST(ClipboardTest, MultipleBufferTest) {
Contains(ASCIIToUTF16(kMimeTypeHTML)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
EXPECT_FALSE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kSelection));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kSelection,
+ /* data_dst = */ nullptr));
EXPECT_FALSE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kSelection));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kSelection,
+ /* data_dst = */ nullptr));
- this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, &text_result);
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result);
EXPECT_EQ(text, text_result);
base::string16 markup_result;
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kSelection, &markup_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kSelection,
+ /* data_dst = */ nullptr, &markup_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_LE(markup.size(), fragment_end - fragment_start);
EXPECT_EQ(markup,
@@ -262,10 +320,12 @@ TYPED_TEST(ClipboardTest, TrickyHTMLTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeHTML)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste, &markup_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &markup_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_LE(markup.size(), fragment_end - fragment_start);
EXPECT_EQ(markup,
@@ -296,10 +356,12 @@ TYPED_TEST(ClipboardTest, UnicodeHTMLTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeHTML)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste, &markup_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &markup_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_LE(markup.size(), fragment_end - fragment_start);
EXPECT_EQ(markup,
@@ -310,7 +372,7 @@ TYPED_TEST(ClipboardTest, UnicodeHTMLTest) {
}
// TODO(estade): Port the following test (decide what target we use for urls)
-#if !defined(OS_POSIX) || defined(OS_MACOSX)
+#if !defined(OS_POSIX) || defined(OS_APPLE)
TYPED_TEST(ClipboardTest, BookmarkTest) {
base::string16 title(ASCIIToUTF16("The Example Company")), title_result;
std::string url("http://www.example.com/"), url_result;
@@ -321,12 +383,14 @@ TYPED_TEST(ClipboardTest, BookmarkTest) {
}
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetUrlType(), ClipboardBuffer::kCopyPaste));
- this->clipboard().ReadBookmark(&title_result, &url_result);
+ ClipboardFormatType::GetUrlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
+ this->clipboard().ReadBookmark(/* data_dst = */ nullptr, &title_result,
+ &url_result);
EXPECT_EQ(title, title_result);
EXPECT_EQ(url, url_result);
}
-#endif // !defined(OS_POSIX) || defined(OS_MACOSX)
+#endif // !defined(OS_POSIX) || defined(OS_APPLE)
TYPED_TEST(ClipboardTest, MultiFormatTest) {
base::string16 text(ASCIIToUTF16("Hi!")), text_result;
@@ -345,16 +409,20 @@ TYPED_TEST(ClipboardTest, MultiFormatTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeText)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#if defined(OS_WIN)
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#endif
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste, &markup_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &markup_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_LE(markup.size(), fragment_end - fragment_start);
EXPECT_EQ(markup,
@@ -364,9 +432,11 @@ TYPED_TEST(ClipboardTest, MultiFormatTest) {
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
- this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, &text_result);
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result);
EXPECT_EQ(text, text_result);
- this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste, &ascii_text);
+ this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
@@ -381,26 +451,31 @@ TYPED_TEST(ClipboardTest, URLTest) {
EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste),
Contains(ASCIIToUTF16(kMimeTypeText)));
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#if defined(OS_WIN)
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#endif
base::string16 text_result;
- this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, &text_result);
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result);
EXPECT_EQ(text_result, url);
std::string ascii_text;
- this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste, &ascii_text);
+ this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &ascii_text);
EXPECT_EQ(UTF16ToUTF8(url), ascii_text);
// TODO(tonikitoo, msisov): enable back for ClipboardOzone implements
// selection support. https://crbug.com/911992
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
+#if defined(OS_POSIX) && !defined(OS_APPLE) && !defined(OS_ANDROID) && \
!defined(OS_CHROMEOS) && !defined(USE_OZONE)
ascii_text.clear();
- this->clipboard().ReadAsciiText(ClipboardBuffer::kSelection, &ascii_text);
+ this->clipboard().ReadAsciiText(ClipboardBuffer::kSelection,
+ /* data_dst = */ nullptr, &ascii_text);
EXPECT_EQ(UTF16ToUTF8(url), ascii_text);
#endif
}
@@ -425,7 +500,8 @@ static void TestBitmapWrite(Clipboard* clipboard,
}
EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::GetBitmapType(),
- ClipboardBuffer::kCopyPaste));
+ ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
const SkBitmap& image = clipboard_test_util::ReadImage(clipboard);
ASSERT_EQ(image.info().colorType(), kN32_SkColorType);
ASSERT_NE(image.info().alphaType(), kUnpremul_SkAlphaType);
@@ -559,10 +635,10 @@ TYPED_TEST(ClipboardTest, PickleTest) {
clipboard_writer.WritePickledData(write_pickle, kFormat);
}
- ASSERT_TRUE(this->clipboard().IsFormatAvailable(kFormat,
- ClipboardBuffer::kCopyPaste));
+ ASSERT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
std::string output;
- this->clipboard().ReadData(kFormat, &output);
+ this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output);
ASSERT_FALSE(output.empty());
base::Pickle read_pickle(output.data(), static_cast<int>(output.size()));
@@ -593,13 +669,13 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) {
}
ASSERT_FALSE(this->clipboard().IsFormatAvailable(
- kFormat1, ClipboardBuffer::kCopyPaste));
- ASSERT_TRUE(this->clipboard().IsFormatAvailable(kFormat2,
- ClipboardBuffer::kCopyPaste));
+ kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
+ ASSERT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
// Check string 2.
std::string output2;
- this->clipboard().ReadData(kFormat2, &output2);
+ this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2);
ASSERT_FALSE(output2.empty());
base::Pickle read_pickle2(output2.data(), static_cast<int>(output2.size()));
@@ -615,14 +691,14 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) {
clipboard_writer.WritePickledData(write_pickle1, kFormat1);
}
- ASSERT_TRUE(this->clipboard().IsFormatAvailable(kFormat1,
- ClipboardBuffer::kCopyPaste));
+ ASSERT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
ASSERT_FALSE(this->clipboard().IsFormatAvailable(
- kFormat2, ClipboardBuffer::kCopyPaste));
+ kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
// Check string 1.
std::string output1;
- this->clipboard().ReadData(kFormat1, &output1);
+ this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1);
ASSERT_FALSE(output1.empty());
base::Pickle read_pickle1(output1.data(), static_cast<int>(output1.size()));
@@ -646,10 +722,10 @@ TYPED_TEST(ClipboardTest, DataTest) {
mojo_base::BigBuffer(payload_span));
}
- ASSERT_TRUE(this->clipboard().IsFormatAvailable(kFormat,
- ClipboardBuffer::kCopyPaste));
+ ASSERT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
std::string output;
- this->clipboard().ReadData(kFormat, &output);
+ this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output);
EXPECT_EQ(payload, output);
}
@@ -686,22 +762,22 @@ TYPED_TEST(ClipboardTest, MultipleDataTest) {
// Check format 1.
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer::kCopyPaste),
+ ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
Contains(ASCIIToUTF16(kFormatString1)));
- EXPECT_TRUE(this->clipboard().IsFormatAvailable(kFormat1,
- ClipboardBuffer::kCopyPaste));
+ EXPECT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
std::string output1;
- this->clipboard().ReadData(kFormat1, &output1);
+ this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1);
EXPECT_EQ(payload1, output1);
// Check format 2.
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer::kCopyPaste),
+ ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
Contains(ASCIIToUTF16(kFormatString2)));
- EXPECT_TRUE(this->clipboard().IsFormatAvailable(kFormat2,
- ClipboardBuffer::kCopyPaste));
+ EXPECT_TRUE(this->clipboard().IsFormatAvailable(
+ kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
std::string output2;
- this->clipboard().ReadData(kFormat2, &output2);
+ this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2);
EXPECT_EQ(payload2, output2);
}
#endif
@@ -716,8 +792,8 @@ TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) {
const std::vector<base::string16> raw_types =
this->clipboard().ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer::kCopyPaste);
-#if defined(OS_MACOSX)
+ ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
+#if defined(OS_APPLE)
EXPECT_THAT(raw_types, Contains(ASCIIToUTF16("public.utf8-plain-text")));
EXPECT_THAT(raw_types, Contains(ASCIIToUTF16("NSStringPboardType")));
EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(2));
@@ -778,37 +854,43 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) {
const std::vector<base::string16> raw_types =
this->clipboard().ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer::kCopyPaste);
+ ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kFormatString)));
#if defined(OS_WIN)
// Only Windows ClipboardFormatType recognizes ANSI formats.
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
#endif // defined(OS_WIN)
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
std::string text_result;
- this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste, &text_result);
+ this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result);
EXPECT_EQ(text_result, text);
// Note: Windows will automatically convert CF_TEXT to its UNICODE version.
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
base::string16 text_result16;
- this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, &text_result16);
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &text_result16);
EXPECT_EQ(text_result16, base::ASCIIToUTF16(text));
std::string platform_specific_result;
this->clipboard().ReadData(ClipboardFormatType::GetType(kFormatString),
+ /* data_dst = */ nullptr,
&platform_specific_result);
EXPECT_EQ(platform_specific_result, kPlatformSpecificText);
}
#endif // defined(OS_WIN) || defined(USE_X11)
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if !defined(OS_APPLE) && !defined(OS_ANDROID)
TYPED_TEST(ClipboardTest, HyperlinkTest) {
const std::string kTitle("The <Example> Company's \"home page\"");
const std::string kUrl("http://www.example.com?x=3&lt=3#\"'<>");
@@ -824,10 +906,12 @@ TYPED_TEST(ClipboardTest, HyperlinkTest) {
}
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
- ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste));
+ ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr));
uint32_t fragment_start;
uint32_t fragment_end;
- this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste, &html_result,
+ this->clipboard().ReadHTML(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &html_result,
&url_result, &fragment_start, &fragment_end);
EXPECT_EQ(kExpectedHtml,
html_result.substr(fragment_end - kExpectedHtml.size(),
@@ -843,7 +927,7 @@ TYPED_TEST(ClipboardTest, WebSmartPasteTest) {
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
ClipboardFormatType::GetWebKitSmartPasteType(),
- ClipboardBuffer::kCopyPaste));
+ ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
}
#if defined(OS_WIN) // Windows only tests.
@@ -972,6 +1056,52 @@ TYPED_TEST(ClipboardTest, WriteImageEmptyParams) {
scw.WriteImage(SkBitmap());
}
+// DLP is only intended to be used in Chrome OS, so the following DLP related
+// tests are only run on Chrome OS.
+#if defined(OS_CHROMEOS)
+// Test that copy/paste would work normally if the dlp controller didn't
+// restrict the clipboard data.
+TYPED_TEST(ClipboardTest, DlpAllowDataRead) {
+ this->AddDlpController();
+ const base::string16 kTestText(base::UTF8ToUTF16("World"));
+ {
+ ScopedClipboardWriter writer(
+ ClipboardBuffer::kCopyPaste,
+ std::make_unique<ClipboardDataEndpoint>(GURL()));
+ writer.WriteText(kTestText);
+ }
+ auto* dlp_controller = this->dlp_controller();
+ EXPECT_CALL(*dlp_controller, IsDataReadAllowed)
+ .WillRepeatedly(testing::Return(true));
+ base::string16 read_result;
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &read_result);
+ ::testing::Mock::VerifyAndClearExpectations(dlp_controller);
+ EXPECT_EQ(kTestText, read_result);
+}
+
+// Test that pasting clipboard data would not work if the dlp controller
+// restricted it.
+TYPED_TEST(ClipboardTest, DlpDisallowDataRead) {
+ this->AddDlpController();
+ const base::string16 kTestText(base::UTF8ToUTF16("World"));
+ {
+ ScopedClipboardWriter writer(
+ ClipboardBuffer::kCopyPaste,
+ std::make_unique<ClipboardDataEndpoint>(GURL()));
+ writer.WriteText(kTestText);
+ }
+ auto* dlp_controller = this->dlp_controller();
+ EXPECT_CALL(*dlp_controller, IsDataReadAllowed)
+ .WillRepeatedly(testing::Return(false));
+ base::string16 read_result;
+ this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+ /* data_dst = */ nullptr, &read_result);
+ ::testing::Mock::VerifyAndClearExpectations(dlp_controller);
+ EXPECT_EQ(base::string16(), read_result);
+}
+#endif // defined(OS_CHROMEOS)
+
} // namespace ui
#endif // UI_BASE_CLIPBOARD_CLIPBOARD_TEST_TEMPLATE_H_
diff --git a/chromium/ui/base/clipboard/clipboard_util_win.cc b/chromium/ui/base/clipboard/clipboard_util_win.cc
index 0162d6e5d7b..708b5693bd5 100644
--- a/chromium/ui/base/clipboard/clipboard_util_win.cc
+++ b/chromium/ui/base/clipboard/clipboard_util_win.cc
@@ -211,7 +211,7 @@ base::FilePath WriteFileContentsToTempFile(const base::FilePath& suggested_name,
// Don't write to the temp file for empty content--leave it at 0-bytes.
if (!(data.Size() == 1 && data.get()[0] == '\0')) {
if (base::WriteFile(temp_path, data.get(), data.Size()) < 0) {
- base::DeleteFile(temp_path, false);
+ base::DeleteFile(temp_path);
return base::FilePath();
}
}
diff --git a/chromium/ui/base/clipboard/clipboard_win.cc b/chromium/ui/base/clipboard/clipboard_win.cc
index 421f22eaaf6..b422119ff36 100644
--- a/chromium/ui/base/clipboard/clipboard_win.cc
+++ b/chromium/ui/base/clipboard/clipboard_win.cc
@@ -15,7 +15,6 @@
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
@@ -23,6 +22,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "base/win/message_window.h"
#include "base/win/scoped_gdi_object.h"
#include "base/win/scoped_hdc.h"
@@ -30,6 +30,7 @@
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_util_win.h"
#include "ui/base/clipboard/custom_data_helper.h"
@@ -231,7 +232,7 @@ Clipboard* Clipboard::Create() {
// ClipboardWin implementation.
ClipboardWin::ClipboardWin() {
- if (base::MessageLoopCurrentForUI::IsSet())
+ if (base::CurrentUIThread::IsSet())
clipboard_owner_ = std::make_unique<base::win::MessageWindow>();
}
@@ -245,8 +246,17 @@ uint64_t ClipboardWin::GetSequenceNumber(ClipboardBuffer buffer) const {
return ::GetClipboardSequenceNumber();
}
-bool ClipboardWin::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+void ClipboardWin::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+bool ClipboardWin::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE;
}
@@ -260,8 +270,11 @@ void ClipboardWin::Clear(ClipboardBuffer buffer) {
::EmptyClipboard();
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(types);
@@ -292,9 +305,12 @@ void ClipboardWin::ReadAvailableTypes(
::GlobalUnlock(hdata);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
std::vector<base::string16>
ClipboardWin::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
int count = ::CountClipboardFormats();
if (!count)
return {};
@@ -317,7 +333,10 @@ ClipboardWin::ReadAvailablePlatformSpecificFormatNames(
return types;
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
RecordRead(ClipboardFormatMetric::kText);
@@ -343,7 +362,10 @@ void ClipboardWin::ReadText(ClipboardBuffer buffer,
TrimAfterNull(result);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
RecordRead(ClipboardFormatMetric::kText);
@@ -369,7 +391,10 @@ void ClipboardWin::ReadAsciiText(ClipboardBuffer buffer,
TrimAfterNull(result);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -427,22 +452,32 @@ void ClipboardWin::ReadHTML(ClipboardBuffer buffer,
*fragment_end = base::checked_cast<uint32_t>(end);
}
-void ClipboardWin::ReadRTF(ClipboardBuffer buffer, std::string* result) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardWin::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
RecordRead(ClipboardFormatMetric::kRtf);
- ReadData(ClipboardFormatType::GetRtfType(), result);
+ ReadData(ClipboardFormatType::GetRtfType(), data_dst, result);
TrimAfterNull(result);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(ReadImageInternal(buffer));
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
RecordRead(ClipboardFormatMetric::kCustomData);
@@ -461,7 +496,11 @@ void ClipboardWin::ReadCustomData(ClipboardBuffer buffer,
::GlobalUnlock(hdata);
}
-void ClipboardWin::ReadBookmark(base::string16* title, std::string* url) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardWin::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const {
RecordRead(ClipboardFormatMetric::kBookmark);
if (title)
title->clear();
@@ -487,7 +526,10 @@ void ClipboardWin::ReadBookmark(base::string16* title, std::string* url) const {
ParseBookmarkClipboardFormat(bookmark, title, url);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
RecordRead(ClipboardFormatMetric::kData);
if (!result) {
@@ -508,8 +550,12 @@ void ClipboardWin::ReadData(const ClipboardFormatType& format,
::GlobalUnlock(data);
}
-void ClipboardWin::WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) {
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardWin::WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
ScopedClipboard clipboard;
@@ -522,9 +568,12 @@ void ClipboardWin::WritePortableRepresentations(ClipboardBuffer buffer,
DispatchPortableRepresentation(object.first, object.second);
}
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardWin::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste);
ScopedClipboard clipboard;
diff --git a/chromium/ui/base/clipboard/clipboard_win.h b/chromium/ui/base/clipboard/clipboard_win.h
index f919abd308f..32e85b07d16 100644
--- a/chromium/ui/base/clipboard/clipboard_win.h
+++ b/chromium/ui/base/clipboard/clipboard_win.h
@@ -36,36 +36,54 @@ class ClipboardWin : public Clipboard {
// Clipboard overrides:
void OnPreShutdown() override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/clipboard_x11.cc b/chromium/ui/base/clipboard/clipboard_x11.cc
index c2f3620584b..71b5489dafd 100644
--- a/chromium/ui/base/clipboard/clipboard_x11.cc
+++ b/chromium/ui/base/clipboard/clipboard_x11.cc
@@ -22,6 +22,7 @@
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/custom_data_helper.h"
@@ -495,14 +496,23 @@ uint64_t ClipboardX11::GetSequenceNumber(ClipboardBuffer buffer) const {
return SelectionChangeObserver::GetInstance()->primary_sequence_number();
}
-bool ClipboardX11::IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const {
+void ClipboardX11::SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) {
+ NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+bool ClipboardX11::IsFormatAvailable(
+ const ClipboardFormatType& format,
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
TargetList target_list = x11_details_->WaitAndGetTargetsList(buffer);
- if (format.Equals(ClipboardFormatType::GetPlainTextType()) ||
- format.Equals(ClipboardFormatType::GetUrlType())) {
+ if (format == ClipboardFormatType::GetPlainTextType() ||
+ format == ClipboardFormatType::GetUrlType()) {
return target_list.ContainsText();
}
return target_list.ContainsFormat(format);
@@ -514,8 +524,11 @@ void ClipboardX11::Clear(ClipboardBuffer buffer) {
x11_details_->Clear(buffer);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadAvailableTypes(
ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
@@ -540,9 +553,12 @@ void ClipboardX11::ReadAvailableTypes(
ReadCustomDataTypes(data.GetData(), data.GetSize(), types);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
std::vector<base::string16>
ClipboardX11::ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const {
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
// Copy target_list(), so that XGetAtomNames can get a non-const Atom*.
@@ -566,7 +582,10 @@ ClipboardX11::ReadAvailablePlatformSpecificFormatNames(
return types;
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
@@ -579,7 +598,10 @@ void ClipboardX11::ReadText(ClipboardBuffer buffer,
}
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
@@ -592,7 +614,10 @@ void ClipboardX11::ReadAsciiText(ClipboardBuffer buffer,
// TODO(estade): handle different charsets.
// TODO(port): set *src_url.
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
@@ -617,7 +642,11 @@ void ClipboardX11::ReadHTML(ClipboardBuffer buffer,
}
}
-void ClipboardX11::ReadRTF(ClipboardBuffer buffer, std::string* result) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardX11::ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kRtf);
@@ -628,15 +657,21 @@ void ClipboardX11::ReadRTF(ClipboardBuffer buffer, std::string* result) const {
data.AssignTo(result);
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const {
DCHECK(IsSupportedClipboardBuffer(buffer));
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(ReadImageInternal(buffer));
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kCustomData);
@@ -648,13 +683,20 @@ void ClipboardX11::ReadCustomData(ClipboardBuffer buffer,
ReadCustomDataForType(data.GetData(), data.GetSize(), type, result);
}
-void ClipboardX11::ReadBookmark(base::string16* title, std::string* url) const {
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardX11::ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const {
DCHECK(CalledOnValidThread());
// TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too.
NOTIMPLEMENTED();
}
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kData);
@@ -665,8 +707,18 @@ void ClipboardX11::ReadData(const ClipboardFormatType& format,
data.AssignTo(result);
}
-void ClipboardX11::WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) {
+#if defined(USE_OZONE)
+bool ClipboardX11::IsSelectionBufferAvailable() const {
+ return true;
+}
+#endif // defined(USE_OZONE)
+
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
+void ClipboardX11::WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
@@ -690,9 +742,12 @@ void ClipboardX11::WritePortableRepresentations(ClipboardBuffer buffer,
}
}
+// |data_src| is not used. It's only passed to be consistent with other
+// platforms.
void ClipboardX11::WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations) {
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
@@ -707,9 +762,9 @@ void ClipboardX11::WriteText(const char* text_data, size_t text_len) {
base::RefCountedString::TakeString(&str));
x11_details_->InsertMapping(kMimeTypeText, mem);
- x11_details_->InsertMapping(kText, mem);
- x11_details_->InsertMapping(kString, mem);
- x11_details_->InsertMapping(kUtf8String, mem);
+ x11_details_->InsertMapping(kMimeTypeLinuxText, mem);
+ x11_details_->InsertMapping(kMimeTypeLinuxString, mem);
+ x11_details_->InsertMapping(kMimeTypeLinuxUtf8String, mem);
}
void ClipboardX11::WriteHTML(const char* markup_data,
diff --git a/chromium/ui/base/clipboard/clipboard_x11.h b/chromium/ui/base/clipboard/clipboard_x11.h
index 68e87132d28..9143b11d3f9 100644
--- a/chromium/ui/base/clipboard/clipboard_x11.h
+++ b/chromium/ui/base/clipboard/clipboard_x11.h
@@ -25,36 +25,57 @@ class ClipboardX11 : public Clipboard {
// Clipboard overrides:
void OnPreShutdown() override;
uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override;
+ void SetClipboardDlpController(
+ std::unique_ptr<ClipboardDlpController> dlp_controller) override;
bool IsFormatAvailable(const ClipboardFormatType& format,
- ClipboardBuffer buffer) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
void Clear(ClipboardBuffer buffer) override;
void ReadAvailableTypes(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::vector<base::string16>* types) const override;
std::vector<base::string16> ReadAvailablePlatformSpecificFormatNames(
- ClipboardBuffer buffer) const override;
- void ReadText(ClipboardBuffer buffer, base::string16* result) const override;
+ ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst) const override;
+ void ReadText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ base::string16* result) const override;
void ReadAsciiText(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
void ReadHTML(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const override;
- void ReadRTF(ClipboardBuffer buffer, std::string* result) const override;
+ void ReadRTF(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
+ std::string* result) const override;
void ReadImage(ClipboardBuffer buffer,
+ const ClipboardDataEndpoint* data_dst,
ReadImageCallback callback) const override;
void ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
+ const ClipboardDataEndpoint* data_dst,
base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
+ void ReadBookmark(const ClipboardDataEndpoint* data_dst,
+ base::string16* title,
+ std::string* url) const override;
void ReadData(const ClipboardFormatType& format,
+ const ClipboardDataEndpoint* data_dst,
std::string* result) const override;
- void WritePortableRepresentations(ClipboardBuffer buffer,
- const ObjectMap& objects) override;
+#if defined(USE_OZONE)
+ bool IsSelectionBufferAvailable() const override;
+#endif // defined(USE_OZONE)
+ void WritePortableRepresentations(
+ ClipboardBuffer buffer,
+ const ObjectMap& objects,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WritePlatformRepresentations(
ClipboardBuffer buffer,
- std::vector<Clipboard::PlatformRepresentation> platform_representations)
- override;
+ std::vector<Clipboard::PlatformRepresentation> platform_representations,
+ std::unique_ptr<ClipboardDataEndpoint> data_src) override;
void WriteText(const char* text_data, size_t text_len) override;
void WriteHTML(const char* markup_data,
size_t markup_len,
diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc
index c723b0d2e33..2a806d3010f 100644
--- a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc
+++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc
@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include <memory>
+#include <utility>
#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
@@ -15,8 +17,10 @@
// be found in clipboard.h.
namespace ui {
-ScopedClipboardWriter::ScopedClipboardWriter(ClipboardBuffer buffer)
- : buffer_(buffer) {}
+ScopedClipboardWriter::ScopedClipboardWriter(
+ ClipboardBuffer buffer,
+ std::unique_ptr<ClipboardDataEndpoint> data_src)
+ : buffer_(buffer), data_src_(std::move(data_src)) {}
ScopedClipboardWriter::~ScopedClipboardWriter() {
static constexpr size_t kMaxRepresentations = 1 << 12;
@@ -25,13 +29,13 @@ ScopedClipboardWriter::~ScopedClipboardWriter() {
"same write.";
DCHECK(platform_representations_.size() < kMaxRepresentations);
if (!objects_.empty()) {
- Clipboard::GetForCurrentThread()->WritePortableRepresentations(buffer_,
- objects_);
- }
- if (!platform_representations_.empty()) {
+ Clipboard::GetForCurrentThread()->WritePortableRepresentations(
+ buffer_, objects_, std::move(data_src_));
+ } else if (!platform_representations_.empty()) {
Clipboard::GetForCurrentThread()->WritePlatformRepresentations(
- buffer_, std::move(platform_representations_));
+ buffer_, std::move(platform_representations_), std::move(data_src_));
}
+
if (confidential_)
Clipboard::GetForCurrentThread()->MarkAsConfidential();
}
diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.h b/chromium/ui/base/clipboard/scoped_clipboard_writer.h
index d2ed3736ce2..82bf6526076 100644
--- a/chromium/ui/base/clipboard/scoped_clipboard_writer.h
+++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.h
@@ -12,6 +12,7 @@
#include "base/strings/string16.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
namespace base {
class Pickle;
@@ -30,8 +31,12 @@ namespace ui {
class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter {
public:
// Create an instance that is a simple wrapper around the clipboard of the
- // given buffer.
- explicit ScopedClipboardWriter(ClipboardBuffer buffer);
+ // given buffer with an optional parameter indicating the source of the data.
+ // TODO(crbug.com/1103193): change its references to use
+ // ClipboardDataEndpoint, if possible.
+ explicit ScopedClipboardWriter(
+ ClipboardBuffer buffer,
+ std::unique_ptr<ClipboardDataEndpoint> src = nullptr);
~ScopedClipboardWriter();
@@ -88,6 +93,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter {
bool confidential_ = false;
+ // The source of the data written in ScopedClipboardWriter, nullptr means it's
+ // not set, or the source of the data can't be represented by
+ // ClipboardDataEndpoint.
+ std::unique_ptr<ClipboardDataEndpoint> data_src_ = nullptr;
+
DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriter);
};
diff --git a/chromium/ui/base/cocoa/menu_controller.h b/chromium/ui/base/cocoa/menu_controller.h
index 8dc3cdb50a5..566dc965045 100644
--- a/chromium/ui/base/cocoa/menu_controller.h
+++ b/chromium/ui/base/cocoa/menu_controller.h
@@ -15,6 +15,13 @@ namespace ui {
class MenuModel;
}
+COMPONENT_EXPORT(UI_BASE)
+@protocol MenuControllerCocoaDelegate
+- (void)controllerWillAddItem:(NSMenuItem*)menuItem
+ fromModel:(ui::MenuModel*)model
+ atIndex:(NSInteger)index;
+@end
+
// A controller for the cross-platform menu model. The menu that's created
// has the tag and represented object set for each menu item. The object is a
// NSValue holding a pointer to the model for that level of the menu (to
@@ -40,6 +47,7 @@ COMPONENT_EXPORT(UI_BASE)
// slightly different form (0th item is empty). Note this attribute of the menu
// cannot be changed after it has been created.
- (instancetype)initWithModel:(ui::MenuModel*)model
+ delegate:(id<MenuControllerCocoaDelegate>)delegate
useWithPopUpButtonCell:(BOOL)useWithCell;
// Programmatically close the constructed menu.
diff --git a/chromium/ui/base/cocoa/menu_controller.mm b/chromium/ui/base/cocoa/menu_controller.mm
index df978482bd4..be5edb5ae2a 100644
--- a/chromium/ui/base/cocoa/menu_controller.mm
+++ b/chromium/ui/base/cocoa/menu_controller.mm
@@ -107,14 +107,12 @@ bool MenuHasVisibleItems(const ui::MenuModel* model) {
- (void)itemSelected:(id)sender;
@end
-@interface ResponsiveNSMenuItem : NSMenuItem
-@end
-
@implementation MenuControllerCocoa {
base::WeakPtr<ui::MenuModel> _model;
base::scoped_nsobject<NSMenu> _menu;
BOOL _useWithPopUpButtonCell; // If YES, 0th item is blank
BOOL _isMenuOpen;
+ id<MenuControllerCocoaDelegate> _delegate;
}
@synthesize useWithPopUpButtonCell = _useWithPopUpButtonCell;
@@ -133,9 +131,11 @@ bool MenuHasVisibleItems(const ui::MenuModel* model) {
}
- (instancetype)initWithModel:(ui::MenuModel*)model
+ delegate:(id<MenuControllerCocoaDelegate>)delegate
useWithPopUpButtonCell:(BOOL)useWithCell {
if ((self = [super init])) {
_model = model->AsWeakPtr();
+ _delegate = delegate;
_useWithPopUpButtonCell = useWithCell;
[self menu];
}
@@ -153,6 +153,10 @@ bool MenuHasVisibleItems(const ui::MenuModel* model) {
[super dealloc];
}
+- (void)setDelegate:(id<MenuControllerCocoaDelegate>)delegate {
+ _delegate = delegate;
+}
+
- (void)cancel {
if (_isMenuOpen) {
[_menu cancelTracking];
@@ -239,6 +243,10 @@ bool MenuHasVisibleItems(const ui::MenuModel* model) {
}
}
}
+
+ if (_delegate)
+ [_delegate controllerWillAddItem:item fromModel:model atIndex:index];
+
[menu insertItem:item atIndex:index];
}
diff --git a/chromium/ui/base/cocoa/menu_controller_unittest.mm b/chromium/ui/base/cocoa/menu_controller_unittest.mm
index 19a7cecd832..4d317fc2767 100644
--- a/chromium/ui/base/cocoa/menu_controller_unittest.mm
+++ b/chromium/ui/base/cocoa/menu_controller_unittest.mm
@@ -170,6 +170,7 @@ class OwningDelegate : public Delegate {
model_.AddItem(1, ASCIIToUTF16("foo"));
controller_.reset([[WatchedLifetimeMenuController alloc]
initWithModel:&model_
+ delegate:nil
useWithPopUpButtonCell:NO]);
[controller_ setDeallocCalled:did_dealloc];
}
@@ -225,6 +226,7 @@ TEST_F(MenuControllerTest, EmptyMenu) {
SimpleMenuModel model(&delegate);
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(0, [[menu menu] numberOfItems]);
}
@@ -241,6 +243,7 @@ TEST_F(MenuControllerTest, BasicCreation) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(6, [[menu menu] numberOfItems]);
@@ -267,6 +270,7 @@ TEST_F(MenuControllerTest, Submenus) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(3, [[menu menu] numberOfItems]);
@@ -301,6 +305,7 @@ TEST_F(MenuControllerTest, EmptySubmenu) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(2, [[menu menu] numberOfItems]);
@@ -328,6 +333,7 @@ TEST_F(MenuControllerTest, EmptySubmenuWhenAllChildItemsAreHidden) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(2, [[menu menu] numberOfItems]);
@@ -361,6 +367,7 @@ TEST_F(MenuControllerTest, HiddenSubmenu) {
// Create the controller.
base::scoped_nsobject<MenuControllerCocoa> menu_controller(
[[MenuControllerCocoa alloc] initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(2, [[menu_controller menu] numberOfItems]);
delegate.menu_to_close_ = [menu_controller menu];
@@ -410,6 +417,7 @@ TEST_F(MenuControllerTest, DisabledSubmenu) {
// Create the controller.
base::scoped_nsobject<MenuControllerCocoa> menu_controller(
[[MenuControllerCocoa alloc] initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
delegate.menu_to_close_ = [menu_controller menu];
@@ -450,6 +458,7 @@ TEST_F(MenuControllerTest, PopUpButton) {
// title.
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:YES]);
EXPECT_EQ(4, [[menu menu] numberOfItems]);
EXPECT_EQ(base::string16(),
@@ -466,6 +475,7 @@ TEST_F(MenuControllerTest, Execute) {
model.AddItem(1, ASCIIToUTF16("one"));
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(1, [[menu menu] numberOfItems]);
@@ -496,6 +506,7 @@ TEST_F(MenuControllerTest, Validate) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(3, [[menu menu] numberOfItems]);
@@ -514,6 +525,7 @@ TEST_F(MenuControllerTest, LabelFontList) {
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(2, [[menu menu] numberOfItems]);
@@ -556,6 +568,7 @@ TEST_F(MenuControllerTest, Dynamic) {
model.AddItem(1, ASCIIToUTF16("foo"));
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
EXPECT_EQ(1, [[menu menu] numberOfItems]);
// Validate() simulates opening the menu - the item label/icon should be
@@ -598,6 +611,7 @@ TEST_F(MenuControllerTest, OpenClose) {
// Create the controller.
base::scoped_nsobject<MenuControllerCocoa> menu([[MenuControllerCocoa alloc]
initWithModel:&model
+ delegate:nil
useWithPopUpButtonCell:NO]);
delegate.menu_to_close_ = [menu menu];
diff --git a/chromium/ui/base/cursor/BUILD.gn b/chromium/ui/base/cursor/BUILD.gn
index f16386ecf4a..30e25d57168 100644
--- a/chromium/ui/base/cursor/BUILD.gn
+++ b/chromium/ui/base/cursor/BUILD.gn
@@ -104,23 +104,34 @@ if (use_aura) {
source_set("unittests") {
testonly = true
sources = []
- deps = [
- "//skia",
- "//testing/gtest",
- "//ui/gfx/geometry",
- ]
+ deps = [ "//testing/gtest" ]
if (!is_ios) {
sources += [ "cursor_unittest.cc" ]
deps += [
":cursor_base",
+ "//skia",
"//ui/base/cursor/mojom:cursor_type",
"//ui/gfx:geometry_skia",
+ "//ui/gfx/geometry",
]
}
if (use_aura) {
sources += [ "cursor_util_unittest.cc" ]
- deps += [ ":cursor" ]
+ deps += [
+ ":cursor",
+ "//skia",
+ "//ui/gfx/geometry",
+ ]
+ }
+
+ if (use_ozone) {
+ sources += [ "ozone/bitmap_cursor_factory_ozone_unittest.cc" ]
+ deps += [
+ ":cursor",
+ "//base",
+ "//ui/base/cursor/mojom:cursor_type",
+ ]
}
}
diff --git a/chromium/ui/base/cursor/cursor_theme_manager.h b/chromium/ui/base/cursor/cursor_theme_manager.h
index a6d09f04945..9ef31c0f75b 100644
--- a/chromium/ui/base/cursor/cursor_theme_manager.h
+++ b/chromium/ui/base/cursor/cursor_theme_manager.h
@@ -26,6 +26,9 @@ class CursorThemeManager {
void RemoveObserver(CursorThemeManagerObserver* observer);
+ virtual std::string GetCursorThemeName() = 0;
+ virtual int GetCursorThemeSize() = 0;
+
protected:
CursorThemeManager();
@@ -34,9 +37,6 @@ class CursorThemeManager {
return cursor_theme_observers_;
}
- virtual std::string GetCursorThemeName() = 0;
- virtual int GetCursorThemeSize() = 0;
-
private:
base::ObserverList<CursorThemeManagerObserver> cursor_theme_observers_;
};
diff --git a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone_unittest.cc b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone_unittest.cc
new file mode 100644
index 00000000000..0d8dbbfca41
--- /dev/null
+++ b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone_unittest.cc
@@ -0,0 +1,24 @@
+// 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 "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+
+#include "base/optional.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+
+namespace ui {
+
+TEST(BitmapCursorFactoryOzoneTest, InvisibleCursor) {
+ BitmapCursorFactoryOzone cursor_factory;
+
+ base::Optional<PlatformCursor> cursor =
+ cursor_factory.GetDefaultCursor(mojom::CursorType::kNone);
+ // The invisible cursor should be nullptr, not base::nullopt.
+ ASSERT_TRUE(cursor.has_value());
+ EXPECT_EQ(cursor, nullptr);
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/default_style.h b/chromium/ui/base/default_style.h
index cd031f9c551..632ea134c5a 100644
--- a/chromium/ui/base/default_style.h
+++ b/chromium/ui/base/default_style.h
@@ -20,7 +20,7 @@ namespace ui {
const int kMessageFontSizeDelta = 0;
// Default font size delta for dialog buttons, textfields, and labels.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Aim for 12pt for Cocoa labels ([NSFont systemFontSize] is typically 13pt).
const int kLabelFontSizeDelta = -1;
#else
@@ -28,7 +28,7 @@ const int kLabelFontSizeDelta = 0;
#endif
// Font size delta for dialog titles.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
const int kTitleFontSizeDelta = 1;
#else
const int kTitleFontSizeDelta = 3;
diff --git a/chromium/ui/base/dragdrop/drag_drop_types.h b/chromium/ui/base/dragdrop/drag_drop_types.h
index af2fad11112..a6f049535dc 100644
--- a/chromium/ui/base/dragdrop/drag_drop_types.h
+++ b/chromium/ui/base/dragdrop/drag_drop_types.h
@@ -21,19 +21,12 @@ class COMPONENT_EXPORT(UI_BASE) DragDropTypes {
DRAG_LINK = 1 << 2
};
- enum DragEventSource {
- DRAG_EVENT_SOURCE_MOUSE = 0,
- DRAG_EVENT_SOURCE_TOUCH,
- DRAG_EVENT_SOURCE_LAST = DRAG_EVENT_SOURCE_TOUCH,
- DRAG_EVENT_SOURCE_COUNT
- };
-
#if defined(OS_WIN)
static uint32_t DragOperationToDropEffect(int drag_operation);
static int DropEffectToDragOperation(uint32_t effect);
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
static uint64_t DragOperationToNSDragOperation(int drag_operation);
static int NSDragOperationToDragOperation(uint64_t ns_drag_operation);
#endif
diff --git a/chromium/ui/base/dragdrop/drop_target_win.h b/chromium/ui/base/dragdrop/drop_target_win.h
index 18409b41d36..4671749b564 100644
--- a/chromium/ui/base/dragdrop/drop_target_win.h
+++ b/chromium/ui/base/dragdrop/drop_target_win.h
@@ -107,7 +107,7 @@ class COMPONENT_EXPORT(UI_BASE) DropTargetWin : public IDropTarget {
// mouse events that are sent to the renderer notifying various drag states.
HWND hwnd_;
- LONG ref_count_;
+ ULONG ref_count_;
DISALLOW_COPY_AND_ASSIGN(DropTargetWin);
};
diff --git a/chromium/ui/base/dragdrop/mojom/BUILD.gn b/chromium/ui/base/dragdrop/mojom/BUILD.gn
new file mode 100644
index 00000000000..501cb0e5b98
--- /dev/null
+++ b/chromium/ui/base/dragdrop/mojom/BUILD.gn
@@ -0,0 +1,9 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+ sources = [ "drag_drop_types.mojom" ]
+}
diff --git a/chromium/ui/base/dragdrop/mojom/OWNERS b/chromium/ui/base/dragdrop/mojom/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/ui/base/dragdrop/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/ui/base/dragdrop/mojom/drag_drop_types.mojom b/chromium/ui/base/dragdrop/mojom/drag_drop_types.mojom
new file mode 100644
index 00000000000..bd378c3dd9a
--- /dev/null
+++ b/chromium/ui/base/dragdrop/mojom/drag_drop_types.mojom
@@ -0,0 +1,10 @@
+// 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.
+
+module ui.mojom;
+
+enum DragEventSource {
+ kMouse,
+ kTouch
+};
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider.h b/chromium/ui/base/dragdrop/os_exchange_data_provider.h
index b945ec9180f..1a7186b2c6d 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider.h
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider.h
@@ -24,7 +24,7 @@
#include "ui/base/dragdrop/file_info/file_info.h"
#include "url/gurl.h"
-#if defined(USE_AURA) || defined(OS_MACOSX)
+#if defined(USE_AURA) || defined(OS_APPLE)
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia.h"
#endif
@@ -99,7 +99,7 @@ class COMPONENT_EXPORT(UI_BASE_DATA_EXCHANGE) OSExchangeDataProvider {
virtual bool HasHtml() const = 0;
#endif
-#if defined(USE_AURA) || defined(OS_MACOSX)
+#if defined(USE_AURA) || defined(OS_APPLE)
virtual void SetDragImage(const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) = 0;
virtual gfx::ImageSkia GetDragImage() const = 0;
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc
index fcf63aac986..4f5c946a1a8 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc
@@ -7,14 +7,16 @@
#include "base/notreached.h"
#include "build/build_config.h"
-#if defined(USE_X11)
-#include "ui/base/dragdrop/os_exchange_data_provider_x11.h"
-#elif defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h"
+#include "ui/base/ui_base_features.h"
#if defined(USE_OZONE)
#include "ui/base/dragdrop/os_exchange_data_provider_factory_ozone.h"
-#endif
-#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h"
-#elif defined(OS_MACOSX)
+#endif // defined(USE_OZONE)
+#if defined(USE_X11)
+#include "ui/base/dragdrop/os_exchange_data_provider_x11.h"
+#endif // defined(USE_X11)
+#elif defined(OS_APPLE)
#include "ui/base/dragdrop/os_exchange_data_provider_builder_mac.h"
#elif defined(OS_WIN)
#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
@@ -22,12 +24,10 @@
namespace ui {
-//static
-std::unique_ptr<OSExchangeDataProvider>
-OSExchangeDataProviderFactory::CreateProvider() {
-#if defined(USE_X11)
- return std::make_unique<OSExchangeDataProviderX11>();
-#elif defined(OS_LINUX)
+namespace {
+
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+std::unique_ptr<OSExchangeDataProvider> CreateProviderForLinux() {
#if defined(USE_OZONE)
// The instance can be nullptr in tests that do not instantiate the platform,
// or on platforms that do not implement specific drag'n'drop. For them,
@@ -38,9 +38,27 @@ OSExchangeDataProviderFactory::CreateProvider() {
if (provider)
return provider;
}
-#endif
+#endif // defined(USE_OZONE)
+ // non-Ozone X11 is never expected to reach this.
+ DCHECK(features::IsUsingOzonePlatform());
return std::make_unique<OSExchangeDataProviderNonBacked>();
-#elif defined(OS_MACOSX)
+}
+#endif // defined(USE_LINUX)
+
+} // namespace
+
+// static
+std::unique_ptr<OSExchangeDataProvider>
+OSExchangeDataProviderFactory::CreateProvider() {
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+ if (features::IsUsingOzonePlatform())
+ return CreateProviderForLinux();
+#if defined(USE_X11)
+ return std::make_unique<OSExchangeDataProviderX11>();
+#endif // defined(USE_X11)
+ NOTREACHED();
+ return nullptr;
+#elif defined(OS_APPLE)
return BuildOSExchangeDataProviderMac();
#elif defined(OS_WIN)
return std::make_unique<OSExchangeDataProviderWin>();
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc
index a4964922b00..a3722644622 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc
@@ -160,6 +160,14 @@ bool OSExchangeDataProviderNonBacked::HasCustomFormat(
return base::Contains(pickle_data_, format);
}
+#if defined(USE_X11)
+void OSExchangeDataProviderNonBacked::SetFileContents(
+ const base::FilePath& filename,
+ const std::string& file_contents) {
+ NOTREACHED();
+}
+#endif
+
void OSExchangeDataProviderNonBacked::SetHtml(const base::string16& html,
const GURL& base_url) {
formats_ |= OSExchangeData::HTML;
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.h
index 6a15a8df69d..0ae453e067a 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.h
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.h
@@ -58,6 +58,10 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderNonBacked
bool HasURL(FilenameToURLPolicy policy) const override;
bool HasFile() const override;
bool HasCustomFormat(const ClipboardFormatType& format) const override;
+#if defined(USE_X11)
+ void SetFileContents(const base::FilePath& filename,
+ const std::string& file_contents) override;
+#endif
void SetHtml(const base::string16& html, const GURL& base_url) override;
bool GetHtml(base::string16* html, GURL* base_url) const override;
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc
index b51a0161fdf..f5ec26aa193 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -120,7 +120,7 @@ class FormatEtcEnumerator final : public IEnumFORMATETC {
// The cursor of the active enumeration - an index into |contents_|.
size_t cursor_;
- LONG ref_count_;
+ ULONG ref_count_;
DISALLOW_COPY_AND_ASSIGN(FormatEtcEnumerator);
};
@@ -273,13 +273,6 @@ IDataObject* OSExchangeDataProviderWin::GetIDataObject(
data_object();
}
-// static
-IDataObjectAsyncCapability* OSExchangeDataProviderWin::GetIAsyncOperation(
- const OSExchangeData& data) {
- return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
- async_operation();
-}
-
OSExchangeDataProviderWin::OSExchangeDataProviderWin(IDataObject* source)
: data_(new DataObjectImpl()),
source_object_(source) {
@@ -842,13 +835,12 @@ static void DuplicateMedium(CLIPFORMAT source_clipformat,
DataObjectImpl::StoredDataInfo::StoredDataInfo(const FORMATETC& format_etc,
STGMEDIUM* medium)
- : format_etc(format_etc), medium(medium), owns_medium(true) {}
+ : format_etc(format_etc), medium(medium) {}
DataObjectImpl::StoredDataInfo::~StoredDataInfo() {
- if (owns_medium) {
- ReleaseStgMedium(medium);
- delete medium;
- }
+ ReleaseStgMedium(medium);
+ delete medium;
+
if (downloader.get())
downloader->Stop();
}
@@ -980,15 +972,18 @@ HRESULT DataObjectImpl::SetData(
STGMEDIUM* local_medium = new STGMEDIUM;
if (should_release) {
+ // Ownership of the original data in `medium` is transferred to `this`.
*local_medium = *medium;
} else {
+ // Ownership of `medium` remains with the caller. To prevent lifetime
+ // issues, perform a deep copy of `medium`.
DuplicateMedium(format_etc->cfFormat, medium, local_medium);
}
auto info = std::make_unique<DataObjectImpl::StoredDataInfo>(*format_etc,
local_medium);
info->medium->tymed = format_etc->tymed;
- info->owns_medium = !!should_release;
+
// Make newly added data appear first.
// TODO(dcheng): Make various setters agree whether elements should be
// prioritized from front to back or back to front.
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h
index f160602b2da..d4f8c93d72f 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h
@@ -15,14 +15,6 @@
#include <string>
#include <vector>
-// Win8 SDK compatibility, see http://goo.gl/fufvl for more information.
-// "Note: This interface has been renamed IDataObjectAsyncCapability."
-// If we're building on pre-8 we define it to its old name. It's documented as
-// being binary compatible.
-#ifndef __IDataObjectAsyncCapability_FWD_DEFINED__
-#define IDataObjectAsyncCapability IAsyncOperation
-#endif
-
#include "base/component_export.h"
#include "base/macros.h"
#include "ui/base/dragdrop/os_exchange_data.h"
@@ -97,7 +89,6 @@ class DataObjectImpl : public DownloadFileObserver,
struct StoredDataInfo {
FORMATETC format_etc;
STGMEDIUM* medium;
- bool owns_medium;
std::unique_ptr<DownloadFileProvider> downloader;
StoredDataInfo(const FORMATETC& format_etc, STGMEDIUM* medium);
@@ -127,8 +118,6 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderWin
static DataObjectImpl* GetDataObjectImpl(const OSExchangeData& data);
static IDataObject* GetIDataObject(const OSExchangeData& data);
- static IDataObjectAsyncCapability* GetIAsyncOperation(
- const OSExchangeData& data);
explicit OSExchangeDataProviderWin(IDataObject* source);
OSExchangeDataProviderWin();
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.cc
index fdb28d24631..d4555f18894 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.cc
@@ -37,39 +37,6 @@ std::unique_ptr<OSExchangeDataProvider> OSExchangeDataProviderX11::Clone()
return std::move(ret);
}
-void OSExchangeDataProviderX11::SetFileContents(
- const base::FilePath& filename,
- const std::string& file_contents) {
- DCHECK(!filename.empty());
- DCHECK(!base::Contains(format_map(), gfx::GetAtom(kMimeTypeMozillaURL)));
-
- set_file_contents_name(filename);
-
- // Direct save handling is a complicated juggling affair between this class,
- // SelectionFormat, and DesktopDragDropClientAuraX11. The general idea behind
- // the protocol is this:
- // - The source window sets its XdndDirectSave0 window property to the
- // proposed filename.
- // - When a target window receives the drop, it updates the XdndDirectSave0
- // property on the source window to the filename it would like the contents
- // to be saved to and then requests the XdndDirectSave0 type from the
- // source.
- // - The source is supposed to copy the file here and return success (S),
- // failure (F), or error (E).
- // - In this case, failure means the destination should try to populate the
- // file itself by copying the data from application/octet-stream. To make
- // things simpler for Chrome, we always 'fail' and let the destination do
- // the work.
- std::string failure("F");
- InsertData(gfx::GetAtom("XdndDirectSave0"),
- scoped_refptr<base::RefCountedMemory>(
- base::RefCountedString::TakeString(&failure)));
- std::string file_contents_copy = file_contents;
- InsertData(gfx::GetAtom("application/octet-stream"),
- scoped_refptr<base::RefCountedMemory>(
- base::RefCountedString::TakeString(&file_contents_copy)));
-}
-
bool OSExchangeDataProviderX11::DispatchXEvent(x11::Event* xev) {
auto* selection = xev->As<x11::SelectionRequestEvent>();
if (selection && selection->owner == x_window()) {
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.h
index 024152eb474..a85e864624e 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.h
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11.h
@@ -34,8 +34,6 @@ class COMPONENT_EXPORT(UI_BASE) OSExchangeDataProviderX11
// OSExchangeDataProvider:
std::unique_ptr<OSExchangeDataProvider> Clone() const override;
- void SetFileContents(const base::FilePath& filename,
- const std::string& file_contents) override;
// XEventDispatcher:
bool DispatchXEvent(x11::Event* xev) override;
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11_unittest.cc
index 11a7fd89a44..e22500a79b9 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_provider_x11_unittest.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_x11_unittest.cc
@@ -11,6 +11,7 @@
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/file_info/file_info.h"
#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "url/gurl.h"
@@ -25,7 +26,7 @@ class OSExchangeDataProviderX11Test : public testing::Test {
public:
OSExchangeDataProviderX11Test()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
- event_source(gfx::GetXDisplay()) {}
+ event_source(x11::Connection::Get()) {}
void AddURLList(const std::string& list_contents) {
std::string contents_copy = list_contents;
diff --git a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc
index 1bc955e3806..07d8fd6d8e4 100644
--- a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc
+++ b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc
@@ -4,12 +4,14 @@
#include "ui/base/dragdrop/os_exchange_data.h"
+#include <objbase.h>
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/win/scoped_hglobal.h"
@@ -24,12 +26,95 @@
namespace ui {
namespace {
+
const std::vector<DWORD> kStorageMediaTypesForVirtualFiles = {
TYMED_ISTORAGE,
TYMED_ISTREAM,
TYMED_HGLOBAL,
};
+class RefCountMockStream : public IStream {
+ public:
+ RefCountMockStream() = default;
+ ~RefCountMockStream() = default;
+
+ ULONG GetRefCount() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return ref_count_;
+ }
+
+ // Overridden from IUnknown:
+ IFACEMETHODIMP QueryInterface(REFIID iid, void** object) override {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (iid == IID_IUnknown || iid == IID_ISequentialStream ||
+ iid == IID_IStream) {
+ *object = static_cast<IStream*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ *object = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ IFACEMETHODIMP_(ULONG) AddRef(void) override {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return ++ref_count_;
+ }
+
+ IFACEMETHODIMP_(ULONG) Release(void) override {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ EXPECT_GT(ref_count_, 0u);
+ return --ref_count_;
+ }
+ // Overridden from ISequentialStream:
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ Read,
+ HRESULT(void* pv, ULONG cb, ULONG* pcbRead));
+
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ Write,
+ HRESULT(void const* pv, ULONG cb, ULONG* pcbW));
+
+ // Overridden from IStream:
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ SetSize,
+ HRESULT(ULARGE_INTEGER));
+
+ MOCK_METHOD4_WITH_CALLTYPE(
+ STDMETHODCALLTYPE,
+ CopyTo,
+ HRESULT(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*));
+
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Commit, HRESULT(DWORD));
+
+ MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Revert, HRESULT());
+
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ LockRegion,
+ HRESULT(ULARGE_INTEGER, ULARGE_INTEGER, DWORD));
+
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ UnlockRegion,
+ HRESULT(ULARGE_INTEGER, ULARGE_INTEGER, DWORD));
+
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Clone, HRESULT(IStream**));
+
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ Seek,
+ HRESULT(LARGE_INTEGER liDistanceToMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER* lpNewFilePointer));
+
+ MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE,
+ Stat,
+ HRESULT(STATSTG* pStatstg, DWORD grfStatFlag));
+
+ private:
+ ULONG ref_count_ = 0u;
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
} // namespace
class OSExchangeDataWinTest : public ::testing::Test {
@@ -223,8 +308,7 @@ TEST_F(OSExchangeDataWinTest, EnumerationViaCOM) {
Microsoft::WRL::ComPtr<IDataObject> com_data(
OSExchangeDataProviderWin::GetIDataObject(data));
Microsoft::WRL::ComPtr<IEnumFORMATETC> enumerator;
- EXPECT_EQ(S_OK, com_data.Get()->EnumFormatEtc(DATADIR_GET,
- enumerator.GetAddressOf()));
+ EXPECT_EQ(S_OK, com_data.Get()->EnumFormatEtc(DATADIR_GET, &enumerator));
// Test that we can get one item.
{
@@ -277,7 +361,7 @@ TEST_F(OSExchangeDataWinTest, EnumerationViaCOM) {
EXPECT_EQ(S_OK, enumerator->Reset());
EXPECT_EQ(S_OK, enumerator->Skip(1));
Microsoft::WRL::ComPtr<IEnumFORMATETC> cloned_enumerator;
- EXPECT_EQ(S_OK, enumerator.Get()->Clone(cloned_enumerator.GetAddressOf()));
+ EXPECT_EQ(S_OK, enumerator.Get()->Clone(&cloned_enumerator));
EXPECT_EQ(S_OK, enumerator.Get()->Reset());
{
@@ -900,4 +984,62 @@ TEST_F(OSExchangeDataWinTest, OnDownloadCompleted) {
EXPECT_TRUE(weak_ptr);
}
+// Verifies the data set by DataObjectImpl::SetData with |fRelease| is released
+// correctly after the DataObjectImpl instance is destroyed.
+TEST_F(OSExchangeDataWinTest, SetDataRelease) {
+ RefCountMockStream stream;
+
+ ASSERT_EQ(stream.AddRef(), 1u);
+ {
+ OSExchangeDataProviderWin data_provider;
+ IDataObject* data_object = data_provider.data_object();
+
+ ClipboardFormatType format(
+ /* cfFormat= */ CF_TEXT, /* lindex= */ -1, /* tymed= */ TYMED_ISTREAM);
+ FORMATETC format_etc = format.ToFormatEtc();
+
+ STGMEDIUM medium;
+ medium.tymed = TYMED_ISTREAM;
+ medium.pstm = &stream;
+ medium.pUnkForRelease = nullptr;
+
+ // |stream| should be released when |data_object| is destroyed since it
+ // takes responsibility to release |stream| after used.
+ EXPECT_EQ(S_OK,
+ data_object->SetData(&format_etc, &medium, /* fRelease= */ TRUE));
+ ASSERT_EQ(stream.GetRefCount(), 1u);
+ }
+
+ EXPECT_EQ(stream.GetRefCount(), 0u);
+}
+
+// Verifies the data duplicated by DataObjectImpl::SetData without |fRelease|
+// is released correctly after the DataObjectImpl instance destroyed.
+TEST_F(OSExchangeDataWinTest, SetDataNoRelease) {
+ RefCountMockStream stream;
+
+ ASSERT_EQ(stream.GetRefCount(), 0u);
+ {
+ OSExchangeDataProviderWin data_provider;
+ IDataObject* data_object = data_provider.data_object();
+
+ ClipboardFormatType format(
+ /* cfFormat= */ CF_TEXT, /* lindex= */ -1, /* tymed= */ TYMED_ISTREAM);
+ FORMATETC format_etc = format.ToFormatEtc();
+
+ STGMEDIUM medium;
+ medium.tymed = TYMED_ISTREAM;
+ medium.pstm = &stream;
+ medium.pUnkForRelease = nullptr;
+
+ EXPECT_EQ(S_OK, data_object->SetData(&format_etc, &medium,
+ /* fRelease= */ FALSE));
+ ASSERT_EQ(stream.GetRefCount(), 1u);
+ }
+
+ // Reference count should be the same as before if |data_object| is
+ // destroyed.
+ EXPECT_EQ(stream.GetRefCount(), 0u);
+}
+
} // namespace ui
diff --git a/chromium/ui/base/fullscreen_win.cc b/chromium/ui/base/fullscreen_win.cc
index b82f96f3ea9..6cb256656b0 100644
--- a/chromium/ui/base/fullscreen_win.cc
+++ b/chromium/ui/base/fullscreen_win.cc
@@ -47,11 +47,11 @@ bool IsFullScreenWindowMode() {
if (!::EqualRect(&wnd_rect, &monitor_info.rcMonitor))
return false;
- // At last, the window style should not have WS_DLGFRAME and WS_THICKFRAME and
- // its extended style should not have WS_EX_WINDOWEDGE and WS_EX_TOOLWINDOW.
+ // At last, the window style should not have WS_DLGFRAME and its extended
+ // style should not have WS_EX_WINDOWEDGE and WS_EX_TOOLWINDOW.
LONG style = ::GetWindowLong(wnd, GWL_STYLE);
LONG ext_style = ::GetWindowLong(wnd, GWL_EXSTYLE);
- return !((style & (WS_DLGFRAME | WS_THICKFRAME)) ||
+ return !((style & WS_DLGFRAME) ||
(ext_style & (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW)));
}
diff --git a/chromium/ui/base/idle/BUILD.gn b/chromium/ui/base/idle/BUILD.gn
index 8e76844e5b6..6a921122e38 100644
--- a/chromium/ui/base/idle/BUILD.gn
+++ b/chromium/ui/base/idle/BUILD.gn
@@ -77,7 +77,7 @@ component("idle") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"Carbon.framework",
"Foundation.framework",
diff --git a/chromium/ui/base/idle/idle.h b/chromium/ui/base/idle/idle.h
index 83420c7bd36..f0a7843beb7 100644
--- a/chromium/ui/base/idle/idle.h
+++ b/chromium/ui/base/idle/idle.h
@@ -19,7 +19,7 @@ enum IdleState {
};
// For MacOSX, InitIdleMonitor needs to be called first to setup the monitor.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
COMPONENT_EXPORT(UI_BASE_IDLE) void InitIdleMonitor();
#endif
diff --git a/chromium/ui/base/idle/idle_linux.cc b/chromium/ui/base/idle/idle_linux.cc
index ec0ed5c89e2..3f13e93c146 100644
--- a/chromium/ui/base/idle/idle_linux.cc
+++ b/chromium/ui/base/idle/idle_linux.cc
@@ -9,29 +9,34 @@
#if defined(USE_X11)
#include "ui/base/idle/idle_query_x11.h"
#include "ui/base/idle/screensaver_window_finder_x11.h"
+#include "ui/base/ui_base_features.h"
#endif
namespace ui {
int CalculateIdleTime() {
+// TODO(https://crbug.com/1098201): calculate idle time for Ozone/Linux.
#if defined(USE_X11)
- IdleQueryX11 idle_query;
- return idle_query.IdleTime();
-#else
- return 0;
+ if (!features::IsUsingOzonePlatform()) {
+ IdleQueryX11 idle_query;
+ return idle_query.IdleTime();
+ }
#endif
+ return 0;
}
bool CheckIdleStateIsLocked() {
if (IdleStateForTesting().has_value())
return IdleStateForTesting().value() == IDLE_STATE_LOCKED;
+// TODO(https://crbug.com/1098202): fix screensaver.
#if defined(USE_X11)
- // Usually the screensaver is used to lock the screen.
- return ScreensaverWindowFinder::ScreensaverWindowExists();
-#else
- return false;
+ if (!features::IsUsingOzonePlatform()) {
+ // Usually the screensaver is used to lock the screen.
+ return ScreensaverWindowFinder::ScreensaverWindowExists();
+ }
#endif
+ return false;
}
} // namespace ui
diff --git a/chromium/ui/base/idle/screensaver_window_finder_x11.cc b/chromium/ui/base/idle/screensaver_window_finder_x11.cc
index 45342ff5383..1813d9ab560 100644
--- a/chromium/ui/base/idle/screensaver_window_finder_x11.cc
+++ b/chromium/ui/base/idle/screensaver_window_finder_x11.cc
@@ -4,7 +4,9 @@
#include "ui/base/idle/screensaver_window_finder_x11.h"
+#include "base/command_line.h"
#include "ui/base/x/x11_util.h"
+#include "ui/gfx/switches.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/screensaver.h"
#include "ui/gfx/x/x11.h"
@@ -16,6 +18,10 @@ namespace ui {
ScreensaverWindowFinder::ScreensaverWindowFinder() : exists_(false) {}
bool ScreensaverWindowFinder::ScreensaverWindowExists() {
+ // Avoid calling into potentially missing X11 APIs in headless mode.
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless))
+ return false;
+
auto* connection = x11::Connection::Get();
// Let the server know the client version before making any requests.
diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn
index 5ab39cdea54..b441ea7da4c 100644
--- a/chromium/ui/base/ime/BUILD.gn
+++ b/chromium/ui/base/ime/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
source_set("text_input_types") {
@@ -14,7 +13,7 @@ source_set("text_input_types") {
]
}
-jumbo_component("ime_types") {
+component("ime_types") {
output_name = "ui_base_ime_types"
sources = [
"candidate_window.cc",
@@ -52,15 +51,13 @@ jumbo_component("ime_types") {
}
}
-jumbo_component("ime") {
+component("ime") {
output_name = "ui_base_ime"
sources = [
"constants.cc",
"constants.h",
"ime_assistive_window_handler_interface.h",
"ime_candidate_window_handler_interface.h",
- "ime_engine_handler_interface.h",
- "ime_input_context_handler_interface.h",
"input_method.h",
"input_method_base.cc",
"input_method_base.h",
@@ -72,8 +69,6 @@ jumbo_component("ime") {
"input_method_minimal.cc",
"input_method_minimal.h",
"input_method_observer.h",
- "mock_ime_input_context_handler.cc",
- "mock_ime_input_context_handler.h",
"mock_input_method.cc",
"mock_input_method.h",
"text_edit_commands.h",
diff --git a/chromium/ui/base/ime/chromeos/BUILD.gn b/chromium/ui/base/ime/chromeos/BUILD.gn
index 03f91b5ea59..bb036a98249 100644
--- a/chromium/ui/base/ime/chromeos/BUILD.gn
+++ b/chromium/ui/base/ime/chromeos/BUILD.gn
@@ -2,15 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
assert(is_chromeos)
source_set("ime_types") {
sources = [ "ime_keyset.h" ]
}
-jumbo_component("chromeos") {
+component("chromeos") {
output_name = "ui_base_ime_chromeos"
sources = [
@@ -24,6 +22,8 @@ jumbo_component("chromeos") {
"fake_input_method_delegate.h",
"ime_bridge.cc",
"ime_bridge.h",
+ "ime_engine_handler_interface.h",
+ "ime_input_context_handler_interface.h",
"ime_keyboard.cc",
"ime_keyboard_impl.cc",
"ime_keyboard_impl.h",
@@ -46,6 +46,8 @@ jumbo_component("chromeos") {
"mock_ime_candidate_window_handler.h",
"mock_ime_engine_handler.cc",
"mock_ime_engine_handler.h",
+ "mock_ime_input_context_handler.cc",
+ "mock_ime_input_context_handler.h",
]
defines = [ "IS_UI_BASE_IME_CHROMEOS_IMPL" ]
diff --git a/chromium/ui/base/ime/dummy_text_input_client.cc b/chromium/ui/base/ime/dummy_text_input_client.cc
index a286b663259..14dc45935f4 100644
--- a/chromium/ui/base/ime/dummy_text_input_client.cc
+++ b/chromium/ui/base/ime/dummy_text_input_client.cc
@@ -35,7 +35,9 @@ void DummyTextInputClient::SetCompositionText(
composition_history_.push_back(composition);
}
-void DummyTextInputClient::ConfirmCompositionText(bool keep_selection) {}
+uint32_t DummyTextInputClient::ConfirmCompositionText(bool keep_selection) {
+ return UINT32_MAX;
+}
void DummyTextInputClient::ClearCompositionText() {
SetCompositionText(CompositionText());
@@ -153,11 +155,28 @@ bool DummyTextInputClient::SetCompositionFromExistingText(
#endif
#if defined(OS_CHROMEOS)
+gfx::Range DummyTextInputClient::GetAutocorrectRange() const {
+ return autocorrect_range_;
+}
+gfx::Rect DummyTextInputClient::GetAutocorrectCharacterBounds() const {
+ return gfx::Rect();
+}
+
bool DummyTextInputClient::SetAutocorrectRange(
const base::string16& autocorrect_text,
const gfx::Range& range) {
- return false;
+ // Clears autocorrect range if text is empty.
+ // autocorrect_text content is ignored.
+ if (autocorrect_text.empty()) {
+ autocorrect_range_ = gfx::Range();
+ } else {
+ autocorrect_range_ = range;
+ }
+ return true;
}
+
+void DummyTextInputClient::ClearAutocorrectRange() {}
+
#endif
#if defined(OS_WIN)
diff --git a/chromium/ui/base/ime/dummy_text_input_client.h b/chromium/ui/base/ime/dummy_text_input_client.h
index af8f0ad48fc..ac615643367 100644
--- a/chromium/ui/base/ime/dummy_text_input_client.h
+++ b/chromium/ui/base/ime/dummy_text_input_client.h
@@ -25,7 +25,7 @@ class DummyTextInputClient : public TextInputClient {
// Overriden from TextInputClient.
void SetCompositionText(const CompositionText& composition) override;
- void ConfirmCompositionText(bool) override;
+ uint32_t ConfirmCompositionText(bool keep_selection) override;
void ClearCompositionText() override;
void InsertText(const base::string16& text) override;
void InsertChar(const KeyEvent& event) override;
@@ -63,9 +63,11 @@ class DummyTextInputClient : public TextInputClient {
#endif
#if defined(OS_CHROMEOS)
- // Set the autocorrect range
+ gfx::Range GetAutocorrectRange() const override;
+ gfx::Rect GetAutocorrectCharacterBounds() const override;
bool SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) override;
+ void ClearAutocorrectRange() override;
#endif
#if defined(OS_WIN)
@@ -101,6 +103,7 @@ class DummyTextInputClient : public TextInputClient {
std::vector<base::string16> insert_text_history_;
std::vector<CompositionText> composition_history_;
std::vector<gfx::Range> selection_history_;
+ gfx::Range autocorrect_range_;
};
} // namespace ui
diff --git a/chromium/ui/base/ime/fuchsia/BUILD.gn b/chromium/ui/base/ime/fuchsia/BUILD.gn
index fb665fead3b..2fb3d45ba28 100644
--- a/chromium/ui/base/ime/fuchsia/BUILD.gn
+++ b/chromium/ui/base/ime/fuchsia/BUILD.gn
@@ -2,11 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
assert(is_fuchsia)
-jumbo_component("fuchsia") {
+component("fuchsia") {
output_name = "ui_base_ime_fuchsia"
sources = [
diff --git a/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc
index 6a6271b471e..a99e7298aeb 100644
--- a/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc
+++ b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc
@@ -8,7 +8,6 @@
#include <utility>
#include "base/check.h"
-#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "base/notreached.h"
diff --git a/chromium/ui/base/ime/ime_assistive_window_handler_interface.h b/chromium/ui/base/ime/ime_assistive_window_handler_interface.h
index d400f291dff..6acb99dafcc 100644
--- a/chromium/ui/base/ime/ime_assistive_window_handler_interface.h
+++ b/chromium/ui/base/ime/ime_assistive_window_handler_interface.h
@@ -9,10 +9,7 @@
#include "base/component_export.h"
#include "base/strings/string16.h"
-
-namespace gfx {
-class Rect;
-} // namespace gfx
+#include "ui/gfx/geometry/rect.h"
namespace ui {
namespace ime {
@@ -25,6 +22,14 @@ namespace chromeos {
struct AssistiveWindowProperties;
+// Contains bounds for windows controlled by handler.
+struct Bounds {
+ // Position of the cursor.
+ gfx::Rect caret;
+ // Position of the autocorrect span, empty if not present.
+ gfx::Rect autocorrect;
+};
+
// A interface to handle the assistive windows related method call.
class COMPONENT_EXPORT(UI_BASE_IME) IMEAssistiveWindowHandlerInterface {
public:
@@ -56,7 +61,7 @@ class COMPONENT_EXPORT(UI_BASE_IME) IMEAssistiveWindowHandlerInterface {
virtual size_t GetConfirmedLength() const = 0;
// Called when the application changes its caret bounds.
- virtual void SetBounds(const gfx::Rect& cursor_bounds) = 0;
+ virtual void SetBounds(const Bounds& bounds) = 0;
// Called when the text field's focus state is changed.
virtual void FocusStateChanged() {}
diff --git a/chromium/ui/base/ime/ime_engine_handler_interface.h b/chromium/ui/base/ime/ime_engine_handler_interface.h
deleted file mode 100644
index 4895587e81c..00000000000
--- a/chromium/ui/base/ime/ime_engine_handler_interface.h
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_IME_IME_ENGINE_HANDLER_INTERFACE_H_
-#define UI_BASE_IME_IME_ENGINE_HANDLER_INTERFACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/component_export.h"
-#include "build/build_config.h"
-#include "ui/base/ime/text_input_client.h"
-#include "ui/base/ime/text_input_mode.h"
-#include "ui/base/ime/text_input_type.h"
-
-namespace gfx {
-class Rect;
-} // namespace gfx
-
-namespace ui {
-
-class InputMethodKeyboardController;
-class KeyEvent;
-
-#if defined(OS_CHROMEOS)
-namespace ime {
-struct AssistiveWindowButton;
-} // namespace ime
-#endif // defined(OS_CHROMEOS)
-
-// A interface to handle the engine handler method call.
-class COMPONENT_EXPORT(UI_BASE_IME) IMEEngineHandlerInterface {
- public:
- typedef base::OnceCallback<void(bool consumed)> KeyEventDoneCallback;
-
- // A information about a focused text input field.
- // A type of each member is based on the html spec, but InputContext can be
- // used to specify about a non html text field like Omnibox.
- struct InputContext {
- InputContext() {}
- InputContext(TextInputType type_,
- TextInputMode mode_,
- int flags_,
- TextInputClient::FocusReason focus_reason_,
- bool should_do_learning_)
- : type(type_),
- mode(mode_),
- flags(flags_),
- focus_reason(focus_reason_),
- should_do_learning(should_do_learning_) {}
- InputContext(int id_,
- TextInputType type_,
- TextInputMode mode_,
- int flags_,
- TextInputClient::FocusReason focus_reason_,
- bool should_do_learning_)
- : id(id_),
- type(type_),
- mode(mode_),
- flags(flags_),
- focus_reason(focus_reason_),
- should_do_learning(should_do_learning_) {}
- // An attribute of the context id which used for ChromeOS only.
- int id;
- // An attribute of the field defined at
- // http://www.w3.org/TR/html401/interact/forms.html#input-control-types.
- TextInputType type;
- // An attribute of the field defined at
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/
- // association-of-controls-and-forms.html#input-modalities
- // :-the-inputmode-attribute.
- TextInputMode mode;
- // An antribute to indicate the flags for web input fields. Please refer to
- // WebTextInputType.
- int flags;
- // An attribute to indicate how this input field was focused.
- TextInputClient::FocusReason focus_reason =
- TextInputClient::FOCUS_REASON_NONE;
- // An attribute to indicate whether text entered in this field should be
- // used to improve typing suggestions for the user.
- bool should_do_learning = false;
- };
-
- virtual ~IMEEngineHandlerInterface() {}
-
- // Called when the Chrome input field get the focus.
- virtual void FocusIn(const InputContext& input_context) = 0;
-
- // Called when the Chrome input field lose the focus.
- virtual void FocusOut() = 0;
-
- // Called when the IME is enabled.
- virtual void Enable(const std::string& component_id) = 0;
-
- // Called when the IME is disabled.
- virtual void Disable() = 0;
-
- // Called when the IME is reset.
- virtual void Reset() = 0;
-
- // Called when the key event is received.
- // Actual implementation must call |callback| after key event handling.
- virtual void ProcessKeyEvent(const KeyEvent& key_event,
- KeyEventDoneCallback callback) = 0;
-
- // Called when a new surrounding text is set. The |text| is surrounding text
- // and |cursor_pos| is 0 based index of cursor position in |text|. If there is
- // selection range, |anchor_pos| represents opposite index from |cursor_pos|.
- // Otherwise |anchor_pos| is equal to |cursor_pos|. If not all surrounding
- // text is given |offset_pos| indicates the starting offset of |text|.
- virtual void SetSurroundingText(const base::string16& text,
- uint32_t cursor_pos,
- uint32_t anchor_pos,
- uint32_t offset_pos) = 0;
-
- // Called when the composition bounds changed.
- virtual void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) = 0;
-
- // Gets the implementation of the keyboard controller.
- virtual ui::InputMethodKeyboardController* GetInputMethodKeyboardController()
- const = 0;
-
-#if defined(OS_CHROMEOS)
-
- // Called when a property is activated or changed.
- virtual void PropertyActivate(const std::string& property_name) = 0;
-
- // Called when the candidate in lookup table is clicked. The |index| is 0
- // based candidate index in lookup table.
- virtual void CandidateClicked(uint32_t index) = 0;
-
- // Called when assistive window is clicked.
- virtual void AssistiveWindowButtonClicked(
- const ui::ime::AssistiveWindowButton& button) {}
-
- // Sets the mirroring/casting enable states.
- virtual void SetMirroringEnabled(bool mirroring_enabled) = 0;
- virtual void SetCastingEnabled(bool casting_enabled) = 0;
-
-#endif // defined(OS_CHROMEOS)
- protected:
- IMEEngineHandlerInterface() {}
-};
-
-} // namespace ui
-
-#endif // UI_BASE_IME_IME_ENGINE_HANDLER_INTERFACE_H_
diff --git a/chromium/ui/base/ime/ime_input_context_handler_interface.h b/chromium/ui/base/ime/ime_input_context_handler_interface.h
deleted file mode 100644
index 46fd1fef8ea..00000000000
--- a/chromium/ui/base/ime/ime_input_context_handler_interface.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_IME_IME_INPUT_CONTEXT_HANDLER_INTERFACE_H_
-#define UI_BASE_IME_IME_INPUT_CONTEXT_HANDLER_INTERFACE_H_
-
-#include <stdint.h>
-
-#include <string>
-#include "base/component_export.h"
-#include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/events/event.h"
-
-namespace ui {
-
-struct SurroundingTextInfo {
- base::string16 surrounding_text;
- gfx::Range selection_range;
-};
-
-class COMPONENT_EXPORT(UI_BASE_IME) IMEInputContextHandlerInterface {
- public:
- // Called when the engine commit a text.
- virtual void CommitText(const std::string& text) = 0;
-
-#if defined(OS_CHROMEOS)
- // Called when the engine changes the composition range.
- // Returns true if the operation was successful.
- virtual bool SetCompositionRange(
- uint32_t before,
- uint32_t after,
- const std::vector<ui::ImeTextSpan>& text_spans) = 0;
-
- // Set the autocorrect range with text.
- virtual bool SetAutocorrectRange(const base::string16& autocorrect_text,
- uint32_t start,
- uint32_t end) = 0;
-
- // Called when the engine changes the selection range.
- // Returns true if the operation was successful.
- virtual bool SetSelectionRange(uint32_t start, uint32_t end) = 0;
-#endif
-
- // Called when the engine updates composition text.
- virtual void UpdateCompositionText(const CompositionText& text,
- uint32_t cursor_pos,
- bool visible) = 0;
-
- // Called when the engine request deleting surrounding string.
- virtual void DeleteSurroundingText(int32_t offset, uint32_t length) = 0;
-
- // Called from the extension API.
- virtual SurroundingTextInfo GetSurroundingTextInfo() = 0;
-
- // Called when the engine sends a key event.
- virtual void SendKeyEvent(KeyEvent* event) = 0;
-
- // Gets the input method pointer.
- virtual InputMethod* GetInputMethod() = 0;
-
- // Commits any composition text.
- // Set |reset_engine| to false if this was triggered from the extension.
- virtual void ConfirmCompositionText(bool reset_engine,
- bool keep_selection) = 0;
-
- // Returns true if there is any composition text.
- virtual bool HasCompositionText() = 0;
-};
-
-} // namespace ui
-
-#endif // UI_BASE_IME_IME_INPUT_CONTEXT_HANDLER_INTERFACE_H_
diff --git a/chromium/ui/base/ime/init/BUILD.gn b/chromium/ui/base/ime/init/BUILD.gn
index 26e7a60f901..dd772acb8ec 100644
--- a/chromium/ui/base/ime/init/BUILD.gn
+++ b/chromium/ui/base/ime/init/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
-jumbo_component("init") {
+component("init") {
output_name = "ui_base_ime_init"
sources = [
diff --git a/chromium/ui/base/ime/init/input_method_factory.cc b/chromium/ui/base/ime/init/input_method_factory.cc
index d923c6b2331..0eaea0f4dc2 100644
--- a/chromium/ui/base/ime/init/input_method_factory.cc
+++ b/chromium/ui/base/ime/init/input_method_factory.cc
@@ -16,7 +16,7 @@
#if defined(OS_WIN)
#include "ui/base/ime/win/input_method_win_imm32.h"
#include "ui/base/ime/win/input_method_win_tsf.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include "ui/base/ime/mac/input_method_mac.h"
#elif defined(USE_X11) || defined(USE_OZONE)
#if defined(USE_X11)
@@ -67,7 +67,7 @@ std::unique_ptr<InputMethod> CreateInputMethod(
return std::make_unique<InputMethodWinTSF>(delegate, widget);
}
return std::make_unique<InputMethodWinImm32>(delegate, widget);
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
return std::make_unique<InputMethodMac>(delegate);
#elif defined(USE_X11) || defined(USE_OZONE)
#if defined(USE_OZONE)
diff --git a/chromium/ui/base/ime/init/input_method_initializer.cc b/chromium/ui/base/ime/init/input_method_initializer.cc
index 1bfc48a8005..d182f5fe320 100644
--- a/chromium/ui/base/ime/init/input_method_initializer.cc
+++ b/chromium/ui/base/ime/init/input_method_initializer.cc
@@ -62,7 +62,7 @@ void InitializeInputMethodForTesting() {
LinuxInputMethodContextFactory::SetInstance(
g_linux_input_method_context_factory_for_testing);
#elif defined(OS_WIN)
- TSFBridge::Initialize();
+ TSFBridge::InitializeForTesting();
#endif
}
diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc
index b5f26b6bf9d..eac91312d04 100644
--- a/chromium/ui/base/ime/input_method_base.cc
+++ b/chromium/ui/base/ime/input_method_base.cc
@@ -143,6 +143,11 @@ void InputMethodBase::OnInputMethodChanged() const {
ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME(
ui::KeyEvent* event) const {
+ if (text_input_client_) {
+ text_input_client_->OnDispatchingKeyEventPostIME(event);
+ if (event->handled())
+ return EventDispatchDetails();
+ }
return delegate_ ? delegate_->DispatchKeyEventPostIME(event)
: ui::EventDispatchDetails();
}
@@ -198,91 +203,4 @@ bool InputMethodBase::SendFakeProcessKeyEvent(bool pressed) const {
return evt.stopped_propagation();
}
-void InputMethodBase::CommitText(const std::string& text) {
- if (text.empty() || !GetTextInputClient() || IsTextInputTypeNone())
- return;
-
- const base::string16 utf16_text = base::UTF8ToUTF16(text);
- if (utf16_text.empty())
- return;
-
- if (!SendFakeProcessKeyEvent(true))
- GetTextInputClient()->InsertText(utf16_text);
- SendFakeProcessKeyEvent(false);
-}
-
-void InputMethodBase::UpdateCompositionText(const CompositionText& composition_,
- uint32_t cursor_pos,
- bool visible) {
- if (IsTextInputTypeNone())
- return;
-
- if (!SendFakeProcessKeyEvent(true)) {
- if (visible && !composition_.text.empty())
- GetTextInputClient()->SetCompositionText(composition_);
- else
- GetTextInputClient()->ClearCompositionText();
- }
- SendFakeProcessKeyEvent(false);
-}
-
-#if defined(OS_CHROMEOS)
-bool InputMethodBase::SetCompositionRange(
- uint32_t before,
- uint32_t after,
- const std::vector<ui::ImeTextSpan>& text_spans) {
- return false;
-}
-
-bool InputMethodBase::SetAutocorrectRange(
- const base::string16& autocorrect_text,
- uint32_t start,
- uint32_t end) {
- return false;
-}
-
-bool InputMethodBase::SetSelectionRange(uint32_t start, uint32_t end) {
- return false;
-}
-#endif
-
-void InputMethodBase::DeleteSurroundingText(int32_t offset, uint32_t length) {}
-
-SurroundingTextInfo InputMethodBase::GetSurroundingTextInfo() {
- gfx::Range text_range;
- SurroundingTextInfo info;
- TextInputClient* client = GetTextInputClient();
- if (!client->GetTextRange(&text_range) ||
- !client->GetTextFromRange(text_range, &info.surrounding_text) ||
- !client->GetEditableSelectionRange(&info.selection_range)) {
- return SurroundingTextInfo();
- }
- // Makes the |selection_range| be relative to the |surrounding_text|.
- info.selection_range.set_start(info.selection_range.start() -
- text_range.start());
- info.selection_range.set_end(info.selection_range.end() - text_range.start());
- return info;
-}
-
-void InputMethodBase::SendKeyEvent(KeyEvent* event) {
- ui::EventDispatchDetails details = DispatchKeyEvent(event);
- DCHECK(!details.dispatcher_destroyed);
-}
-
-InputMethod* InputMethodBase::GetInputMethod() {
- return this;
-}
-
-void InputMethodBase::ConfirmCompositionText(bool reset_engine,
- bool keep_selection) {
- TextInputClient* client = GetTextInputClient();
- if (client && client->HasCompositionText())
- client->ConfirmCompositionText(keep_selection);
-}
-
-bool InputMethodBase::HasCompositionText() {
- TextInputClient* client = GetTextInputClient();
- return client && client->HasCompositionText();
-}
-
} // namespace ui
diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h
index e87cdcab71e..70edaa143dc 100644
--- a/chromium/ui/base/ime/input_method_base.h
+++ b/chromium/ui/base/ime/input_method_base.h
@@ -14,7 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "build/build_config.h"
-#include "ui/base/ime/ime_input_context_handler_interface.h"
+#include "ui/base/ime/composition_text.h"
#include "ui/base/ime/input_method.h"
#include "ui/events/event_dispatcher.h"
@@ -33,8 +33,7 @@ class TextInputClient;
// implementations.
class COMPONENT_EXPORT(UI_BASE_IME) InputMethodBase
: public InputMethod,
- public base::SupportsWeakPtr<InputMethodBase>,
- public IMEInputContextHandlerInterface {
+ public base::SupportsWeakPtr<InputMethodBase> {
public:
~InputMethodBase() override;
@@ -81,30 +80,6 @@ class COMPONENT_EXPORT(UI_BASE_IME) InputMethodBase
virtual void OnDidChangeFocusedClient(TextInputClient* focused_before,
TextInputClient* focused) {}
- // IMEInputContextHandlerInterface:
- void CommitText(const std::string& text) override;
- void UpdateCompositionText(const CompositionText& text,
- uint32_t cursor_pos,
- bool visible) override;
-
-#if defined(OS_CHROMEOS)
- bool SetCompositionRange(
- uint32_t before,
- uint32_t after,
- const std::vector<ui::ImeTextSpan>& text_spans) override;
- bool SetAutocorrectRange(const base::string16& autocorrect_text,
- uint32_t start,
- uint32_t end) override;
- bool SetSelectionRange(uint32_t start, uint32_t end) override;
-#endif
-
- void DeleteSurroundingText(int32_t offset, uint32_t length) override;
- SurroundingTextInfo GetSurroundingTextInfo() override;
- void SendKeyEvent(KeyEvent* event) override;
- InputMethod* GetInputMethod() override;
- void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
- bool HasCompositionText() override;
-
// Sends a fake key event for IME composing without physical key events.
// Returns true if the faked key event is stopped propagation.
bool SendFakeProcessKeyEvent(bool pressed) const;
diff --git a/chromium/ui/base/ime/linux/BUILD.gn b/chromium/ui/base/ime/linux/BUILD.gn
index dbef616118c..b5a2b419c00 100644
--- a/chromium/ui/base/ime/linux/BUILD.gn
+++ b/chromium/ui/base/ime/linux/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/linux/pangocairo/pangocairo.gni")
# Allows base/ime/linux to be built if it's just for making kythe annotations.
@@ -14,7 +13,7 @@ import("//build/config/linux/pangocairo/pangocairo.gni")
import("//build/toolchain/kythe.gni")
assert((is_linux && !is_chromeos) || enable_kythe_annotations)
-jumbo_component("linux") {
+component("linux") {
output_name = "ui_base_ime_linux"
sources = [
"fake_input_method_context.cc",
diff --git a/chromium/ui/base/ime/linux/input_method_auralinux.cc b/chromium/ui/base/ime/linux/input_method_auralinux.cc
index de49a5b15f1..10549686e63 100644
--- a/chromium/ui/base/ime/linux/input_method_auralinux.cc
+++ b/chromium/ui/base/ime/linux/input_method_auralinux.cc
@@ -356,7 +356,7 @@ void InputMethodAuraLinux::OnPreeditEnd() {
void InputMethodAuraLinux::OnWillChangeFocusedClient(
TextInputClient* focused_before,
TextInputClient* focused) {
- ConfirmCompositionText(/* reset_engine */ true, /* keep_selection */ false);
+ ConfirmCompositionText();
}
void InputMethodAuraLinux::OnDidChangeFocusedClient(
@@ -393,12 +393,7 @@ ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent(
return details;
}
-void InputMethodAuraLinux::ConfirmCompositionText(bool reset_engine,
- bool keep_selection) {
- if (keep_selection) {
- NOTIMPLEMENTED_LOG_ONCE();
- }
- InputMethodBase::ConfirmCompositionText(reset_engine, keep_selection);
+void InputMethodAuraLinux::ConfirmCompositionText() {
ResetContext();
}
diff --git a/chromium/ui/base/ime/linux/input_method_auralinux.h b/chromium/ui/base/ime/linux/input_method_auralinux.h
index e560171df5b..52c1cc614c9 100644
--- a/chromium/ui/base/ime/linux/input_method_auralinux.h
+++ b/chromium/ui/base/ime/linux/input_method_auralinux.h
@@ -47,9 +47,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) InputMethodAuraLinux
TextInputClient* focused) override;
void OnDidChangeFocusedClient(TextInputClient* focused_before,
TextInputClient* focused) override;
- void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
private:
+ void ConfirmCompositionText();
bool HasInputMethodResult();
bool NeedInsertChar() const;
ui::EventDispatchDetails SendFakeProcessKeyEvent(ui::KeyEvent* event) const
diff --git a/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc b/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc
index c7d29003977..dfea78d1fc1 100644
--- a/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc
+++ b/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc
@@ -215,7 +215,7 @@ class TextInputClientForTesting : public DummyTextInputClient {
bool HasCompositionText() const override { return !composition_text.empty(); }
- void ConfirmCompositionText(bool keep_selection) override {
+ uint32_t ConfirmCompositionText(bool keep_selection) override {
// TODO(b/134473433) Modify this function so that when keep_selection is
// true, the selection is not changed when text committed
if (keep_selection) {
@@ -225,7 +225,10 @@ class TextInputClientForTesting : public DummyTextInputClient {
base::ASCIIToUTF16("compositionend"));
TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16("textinput:") +
composition_text);
+ const uint32_t composition_text_length =
+ static_cast<uint32_t>(composition_text.length());
composition_text.clear();
+ return composition_text_length;
}
void ClearCompositionText() override {
diff --git a/chromium/ui/base/ime/mac/BUILD.gn b/chromium/ui/base/ime/mac/BUILD.gn
index 7d10cc8225b..df1698a7b5c 100644
--- a/chromium/ui/base/ime/mac/BUILD.gn
+++ b/chromium/ui/base/ime/mac/BUILD.gn
@@ -2,11 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
assert(is_mac)
-jumbo_component("mac") {
+component("mac") {
output_name = "ui_base_ime_mac"
sources = [
@@ -18,5 +16,5 @@ jumbo_component("mac") {
public_deps = [ "//ui/base/ime" ]
- libs = [ "AppKit.framework" ]
+ frameworks = [ "AppKit.framework" ]
}
diff --git a/chromium/ui/base/ime/mock_ime_input_context_handler.cc b/chromium/ui/base/ime/mock_ime_input_context_handler.cc
deleted file mode 100644
index f2ba25fbeb6..00000000000
--- a/chromium/ui/base/ime/mock_ime_input_context_handler.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/ime/mock_ime_input_context_handler.h"
-
-#include "base/logging.h"
-#include "base/notreached.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/gfx/range/range.h"
-
-namespace ui {
-
-MockIMEInputContextHandler::MockIMEInputContextHandler()
- : commit_text_call_count_(0),
- set_selection_range_call_count_(0),
- update_preedit_text_call_count_(0),
- delete_surrounding_text_call_count_(0) {}
-
-MockIMEInputContextHandler::~MockIMEInputContextHandler() {}
-
-void MockIMEInputContextHandler::CommitText(const std::string& text) {
- ++commit_text_call_count_;
- last_commit_text_ = text;
-}
-
-void MockIMEInputContextHandler::UpdateCompositionText(
- const CompositionText& text,
- uint32_t cursor_pos,
- bool visible) {
- ++update_preedit_text_call_count_;
- last_update_composition_arg_.composition_text = text;
- last_update_composition_arg_.selection = gfx::Range(cursor_pos);
- last_update_composition_arg_.is_visible = visible;
-}
-
-#if defined(OS_CHROMEOS)
-bool MockIMEInputContextHandler::SetCompositionRange(
- uint32_t before,
- uint32_t after,
- const std::vector<ui::ImeTextSpan>& text_spans) {
- // TODO(shend): Make this work with before, after and different text contents.
- last_update_composition_arg_.composition_text.text =
- base::UTF8ToUTF16(last_commit_text_);
- return true;
-}
-
-bool MockIMEInputContextHandler::SetAutocorrectRange(
- const base::string16& autocorrect_text,
- uint32_t start,
- uint32_t end) {
- // TODO(crbug.com/1091088): Implement function.
- return false;
-}
-
-bool MockIMEInputContextHandler::SetSelectionRange(uint32_t start,
- uint32_t end) {
- ++set_selection_range_call_count_;
- last_update_composition_arg_.selection = gfx::Range(start, end);
- return true;
-}
-#endif
-
-void MockIMEInputContextHandler::DeleteSurroundingText(int32_t offset,
- uint32_t length) {
- ++delete_surrounding_text_call_count_;
- last_delete_surrounding_text_arg_.offset = offset;
- last_delete_surrounding_text_arg_.length = length;
-}
-
-SurroundingTextInfo MockIMEInputContextHandler::GetSurroundingTextInfo() {
- return SurroundingTextInfo();
-}
-
-void MockIMEInputContextHandler::Reset() {
- commit_text_call_count_ = 0;
- set_selection_range_call_count_ = 0;
- update_preedit_text_call_count_ = 0;
- delete_surrounding_text_call_count_ = 0;
- last_commit_text_.clear();
- sent_key_events_.clear();
-}
-
-void MockIMEInputContextHandler::SendKeyEvent(KeyEvent* event) {
- sent_key_events_.emplace_back(*event);
-}
-
-InputMethod* MockIMEInputContextHandler::GetInputMethod() {
- return nullptr;
-}
-
-void MockIMEInputContextHandler::ConfirmCompositionText(bool reset_engine,
- bool keep_selection) {
- // TODO(b/134473433) Modify this function so that when keep_selection is
- // true, the selection is not changed when text committed
- if (keep_selection) {
- NOTIMPLEMENTED_LOG_ONCE();
- }
- if (!HasCompositionText())
- return;
-
- CommitText(
- base::UTF16ToUTF8(last_update_composition_arg_.composition_text.text));
- last_update_composition_arg_.composition_text.text = base::string16();
-}
-
-bool MockIMEInputContextHandler::HasCompositionText() {
- return !last_update_composition_arg_.composition_text.text.empty();
-}
-
-} // namespace ui
diff --git a/chromium/ui/base/ime/mock_ime_input_context_handler.h b/chromium/ui/base/ime/mock_ime_input_context_handler.h
deleted file mode 100644
index 6890e6b538a..00000000000
--- a/chromium/ui/base/ime/mock_ime_input_context_handler.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_IME_MOCK_IME_INPUT_CONTEXT_HANDLER_H_
-#define UI_BASE_IME_MOCK_IME_INPUT_CONTEXT_HANDLER_H_
-
-#include <stdint.h>
-
-#include "base/component_export.h"
-#include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/ime_input_context_handler_interface.h"
-#include "ui/events/event.h"
-#include "ui/gfx/range/range.h"
-
-namespace ui {
-class InputMethod;
-
-class COMPONENT_EXPORT(UI_BASE_IME) MockIMEInputContextHandler
- : public IMEInputContextHandlerInterface {
- public:
- struct UpdateCompositionTextArg {
- CompositionText composition_text;
- gfx::Range selection;
- bool is_visible;
- };
-
- struct DeleteSurroundingTextArg {
- int32_t offset;
- uint32_t length;
- };
-
- MockIMEInputContextHandler();
- virtual ~MockIMEInputContextHandler();
-
- void CommitText(const std::string& text) override;
- void UpdateCompositionText(const CompositionText& text,
- uint32_t cursor_pos,
- bool visible) override;
-
-#if defined(OS_CHROMEOS)
- bool SetCompositionRange(
- uint32_t before,
- uint32_t after,
- const std::vector<ui::ImeTextSpan>& text_spans) override;
-
- bool SetAutocorrectRange(const base::string16& autocorrect_text,
- uint32_t start,
- uint32_t end) override;
-
- bool SetSelectionRange(uint32_t start, uint32_t end) override;
-#endif
-
- void DeleteSurroundingText(int32_t offset, uint32_t length) override;
- SurroundingTextInfo GetSurroundingTextInfo() override;
- void SendKeyEvent(KeyEvent* event) override;
- InputMethod* GetInputMethod() override;
- void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
- bool HasCompositionText() override;
-
- int commit_text_call_count() const { return commit_text_call_count_; }
- int set_selection_range_call_count() const {
- return set_selection_range_call_count_;
- }
- int update_preedit_text_call_count() const {
- return update_preedit_text_call_count_;
- }
-
- int delete_surrounding_text_call_count() const {
- return delete_surrounding_text_call_count_;
- }
-
- int send_key_event_call_count() const { return sent_key_events_.size(); }
-
- const std::string& last_commit_text() const { return last_commit_text_; }
-
- const UpdateCompositionTextArg& last_update_composition_arg() const {
- return last_update_composition_arg_;
- }
-
- const DeleteSurroundingTextArg& last_delete_surrounding_text_arg() const {
- return last_delete_surrounding_text_arg_;
- }
-
- const std::vector<ui::KeyEvent>& sent_key_events() const {
- return sent_key_events_;
- }
-
- // Resets all call count.
- void Reset();
-
- private:
- int commit_text_call_count_;
- int set_selection_range_call_count_;
- int update_preedit_text_call_count_;
- int delete_surrounding_text_call_count_;
- std::string last_commit_text_;
- std::vector<ui::KeyEvent> sent_key_events_;
- UpdateCompositionTextArg last_update_composition_arg_;
- DeleteSurroundingTextArg last_delete_surrounding_text_arg_;
-};
-} // ui
-
-#endif // UI_BASE_IME_MOCK_IME_INPUT_CONTEXT_HANDLER_H_
diff --git a/chromium/ui/base/ime/mock_input_method.cc b/chromium/ui/base/ime/mock_input_method.cc
index 5cd43dd5165..62702077507 100644
--- a/chromium/ui/base/ime/mock_input_method.cc
+++ b/chromium/ui/base/ime/mock_input_method.cc
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "build/build_config.h"
#include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/text_input_client.h"
#include "ui/events/event.h"
namespace ui {
@@ -44,6 +45,11 @@ TextInputClient* MockInputMethod::GetTextInputClient() const {
ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(
ui::KeyEvent* event) {
+ if (text_input_client_) {
+ text_input_client_->OnDispatchingKeyEventPostIME(event);
+ if (event->handled())
+ return EventDispatchDetails();
+ }
return delegate_->DispatchKeyEventPostIME(event);
}
diff --git a/chromium/ui/base/ime/mojom/BUILD.gn b/chromium/ui/base/ime/mojom/BUILD.gn
index c69ea02d7a9..6b37e7f2e31 100644
--- a/chromium/ui/base/ime/mojom/BUILD.gn
+++ b/chromium/ui/base/ime/mojom/BUILD.gn
@@ -44,6 +44,10 @@ mojom("mojom") {
cpp = "::ui::ImeTextSpan::Thickness"
},
{
+ mojom = "ui.mojom.ImeTextSpanType"
+ cpp = "::ui::ImeTextSpan::Type"
+ },
+ {
mojom = "ui.mojom.ImeTextSpanUnderlineStyle"
cpp = "::ui::ImeTextSpan::UnderlineStyle"
},
diff --git a/chromium/ui/base/ime/mojom/text_input_state.mojom b/chromium/ui/base/ime/mojom/text_input_state.mojom
index 63746cb4d67..d52feb42d0a 100644
--- a/chromium/ui/base/ime/mojom/text_input_state.mojom
+++ b/chromium/ui/base/ime/mojom/text_input_state.mojom
@@ -11,6 +11,12 @@ import "ui/base/ime/mojom/virtual_keyboard_types.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/range/mojom/range.mojom";
+// This structure contains ime text span and its bounds.
+struct ImeTextSpanInfo {
+ ui.mojom.ImeTextSpan span;
+ gfx.mojom.Rect bounds;
+};
+
// This structure represents the current editing state.
struct TextInputState {
// Type of the input field.
@@ -64,5 +70,8 @@ struct TextInputState {
// authors when the virtualkeyboardpolicy is manual.
ui.mojom.VirtualKeyboardVisibilityRequest last_vk_visibility_request =
ui.mojom.VirtualKeyboardVisibilityRequest.NONE;
+
+ // Information of ime text spans at the cursor position.
+ array<ImeTextSpanInfo> ime_text_spans_info;
};
diff --git a/chromium/ui/base/ime/text_input_client.h b/chromium/ui/base/ime/text_input_client.h
index b6302623328..e6e900080ff 100644
--- a/chromium/ui/base/ime/text_input_client.h
+++ b/chromium/ui/base/ime/text_input_client.h
@@ -19,6 +19,7 @@
#include "build/build_config.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/base/ime/composition_text.h"
+#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/native_widget_types.h"
@@ -51,6 +52,15 @@ class COMPONENT_EXPORT(UI_BASE_IME) TextInputClient {
FOCUS_REASON_OTHER,
};
+#if defined(OS_CHROMEOS)
+ enum SubClass {
+ kRenderWidgetHostViewAura = 0,
+ kArcImeService = 1,
+ kTextField = 2,
+ kMaxValue = kTextField,
+ };
+#endif
+
virtual ~TextInputClient();
// Input method result -------------------------------------------------------
@@ -64,7 +74,10 @@ class COMPONENT_EXPORT(UI_BASE_IME) TextInputClient {
// Converts current composition text into final content.
// If keep_selection is true, keep the selected range unchanged
// otherwise, set it to be after the newly committed text.
- virtual void ConfirmCompositionText(bool keep_selection) = 0;
+ // If text was committed, return the number of characters committed.
+ // If we do not know what the number of characters committed is, return
+ // UINT32_MAX.
+ virtual uint32_t ConfirmCompositionText(bool keep_selection) = 0;
// Removes current composition text.
virtual void ClearCompositionText() = 0;
@@ -218,10 +231,25 @@ class COMPONENT_EXPORT(UI_BASE_IME) TextInputClient {
#endif
#if defined(OS_CHROMEOS)
+ // Return the start and end index of the autocorrect range. If non-existent,
+ // return an empty Range.
+ virtual gfx::Range GetAutocorrectRange() const = 0;
+
+ // Return the location of the autocorrect range as a gfx::Rect object.
+ // If gfx::Rect is empty, then the autocorrect character bounds have not been
+ // set.
+ // These bounds are in screen coordinates.
+ virtual gfx::Rect GetAutocorrectCharacterBounds() const = 0;
+
// Set the autocorrect range and return if it has been set correctly as a
- // boolean value.
+ // boolean value. If text or range is empty, existing autocorrect range is
+ // cleared. Out of range results in failure and no modification will be made.
virtual bool SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) = 0;
+
+ // Clear the autocorrect range and remove the underline under the
+ // autocorrect text.
+ virtual void ClearAutocorrectRange() = 0;
#endif
#if defined(OS_WIN)
@@ -244,6 +272,11 @@ class COMPONENT_EXPORT(UI_BASE_IME) TextInputClient {
const base::string16& active_composition_text,
bool is_composition_committed) = 0;
#endif
+
+ // Called before ui::InputMethod dispatches a not-consumed event to PostIME
+ // phase. This method gives TextInputClient a chance to intercept event
+ // dispatching.
+ virtual void OnDispatchingKeyEventPostIME(ui::KeyEvent* event) {}
};
} // namespace ui
diff --git a/chromium/ui/base/ime/win/BUILD.gn b/chromium/ui/base/ime/win/BUILD.gn
index 5a7c0c7d53c..13d94842f69 100644
--- a/chromium/ui/base/ime/win/BUILD.gn
+++ b/chromium/ui/base/ime/win/BUILD.gn
@@ -2,11 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
assert(is_win)
-jumbo_component("win") {
+component("win") {
output_name = "ui_base_ime_win"
sources = [
"imm32_manager.cc",
@@ -17,6 +15,8 @@ jumbo_component("win") {
"input_method_win_imm32.h",
"input_method_win_tsf.cc",
"input_method_win_tsf.h",
+ "mock_tsf_bridge.cc",
+ "mock_tsf_bridge.h",
"on_screen_keyboard_display_manager_input_pane.cc",
"on_screen_keyboard_display_manager_input_pane.h",
"on_screen_keyboard_display_manager_tab_tip.cc",
@@ -45,11 +45,4 @@ jumbo_component("win") {
libs = [ "imm32.lib" ]
ldflags = [ "/DELAYLOAD:imm32.dll" ]
-
- jumbo_excluded_sources = [
- # tsf_text_store.cc needs INITGUID to be defined before
- # including any header to properly generate GUID objects. That
- # is not guaranteed when included in a jumbo build.
- "tsf_text_store.cc",
- ]
}
diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.cc b/chromium/ui/base/ime/win/input_method_win_imm32.cc
index 324a6bc91fb..20defba624b 100644
--- a/chromium/ui/base/ime/win/input_method_win_imm32.cc
+++ b/chromium/ui/base/ime/win/input_method_win_imm32.cc
@@ -151,8 +151,7 @@ void InputMethodWinImm32::OnWillChangeFocusedClient(
TextInputClient* focused_before,
TextInputClient* focused) {
if (IsWindowFocused(focused_before))
- ConfirmCompositionText(/* reset_engine */ true,
- /* keep_selection */ false);
+ ConfirmCompositionText();
}
void InputMethodWinImm32::OnDidChangeFocusedClient(
@@ -315,10 +314,7 @@ void InputMethodWinImm32::RefreshInputLanguage() {
}
}
-void InputMethodWinImm32::ConfirmCompositionText(bool reset_engine,
- bool keep_selection) {
- InputMethodBase::ConfirmCompositionText(reset_engine, keep_selection);
-
+void InputMethodWinImm32::ConfirmCompositionText() {
// Makes sure the native IME app can be informed about the composition is
// cleared, so that it can clean up its internal states.
if (composing_window_handle_)
diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.h b/chromium/ui/base/ime/win/input_method_win_imm32.h
index c95a8ae7c06..b9ec2f5ddbb 100644
--- a/chromium/ui/base/ime/win/input_method_win_imm32.h
+++ b/chromium/ui/base/ime/win/input_method_win_imm32.h
@@ -27,7 +27,6 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) InputMethodWinImm32
// Overridden from InputMethodBase:
void OnFocus() override;
- void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
// Overridden from InputMethod:
bool OnUntranslatedIMEMessage(const MSG event,
@@ -79,6 +78,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) InputMethodWinImm32
// Enables or disables the IME according to the current text input type.
void UpdateIMEState();
+ void ConfirmCompositionText();
+
// Windows IMM32 wrapper.
// (See "ui/base/ime/win/ime_input.h" for its details.)
ui::IMM32Manager imm32_manager_;
diff --git a/chromium/ui/base/ime/win/input_method_win_tsf.cc b/chromium/ui/base/ime/win/input_method_win_tsf.cc
index b4e4fcc2e67..aca6422dfe3 100644
--- a/chromium/ui/base/ime/win/input_method_win_tsf.cc
+++ b/chromium/ui/base/ime/win/input_method_win_tsf.cc
@@ -143,8 +143,7 @@ void InputMethodWinTSF::OnWillChangeFocusedClient(
TextInputClient* focused_before,
TextInputClient* focused) {
if (IsWindowFocused(focused_before)) {
- ConfirmCompositionText(/* reset_engine */ true,
- /* keep_selection */ false);
+ ConfirmCompositionText();
ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before);
}
}
@@ -168,13 +167,7 @@ void InputMethodWinTSF::OnDidChangeFocusedClient(
InputMethodWinBase::OnDidChangeFocusedClient(focused_before, focused);
}
-void InputMethodWinTSF::ConfirmCompositionText(bool reset_engine,
- bool keep_selection) {
- // TODO(b/134473433) Modify this function so that when keep_selection is
- // true, the selection is not changed when text committed
- if (keep_selection) {
- NOTIMPLEMENTED_LOG_ONCE();
- }
+void InputMethodWinTSF::ConfirmCompositionText() {
if (IsTextInputTypeNone())
return;
diff --git a/chromium/ui/base/ime/win/input_method_win_tsf.h b/chromium/ui/base/ime/win/input_method_win_tsf.h
index 58e0cfe007c..34d9616ab08 100644
--- a/chromium/ui/base/ime/win/input_method_win_tsf.h
+++ b/chromium/ui/base/ime/win/input_method_win_tsf.h
@@ -41,11 +41,11 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) InputMethodWinTSF
TextInputClient* focused) override;
void OnDidChangeFocusedClient(TextInputClient* focused_before,
TextInputClient* focused) override;
- void ConfirmCompositionText(bool reset_engine, bool keep_selection) override;
-
void ShowVirtualKeyboardIfEnabled() override;
private:
+ void ConfirmCompositionText();
+
class TSFEventObserver;
// TSF event router and observer.
diff --git a/chromium/ui/base/ime/win/mock_tsf_bridge.cc b/chromium/ui/base/ime/win/mock_tsf_bridge.cc
index 935da4d4e2f..6989d94b370 100644
--- a/chromium/ui/base/ime/win/mock_tsf_bridge.cc
+++ b/chromium/ui/base/ime/win/mock_tsf_bridge.cc
@@ -40,7 +40,6 @@ void MockTSFBridge::SetFocusedClient(HWND focused_window,
void MockTSFBridge::RemoveFocusedClient(TextInputClient* client) {
++remove_focused_client_call_count_;
- DCHECK_EQ(client, text_input_client_);
text_input_client_ = nullptr;
focused_window_ = nullptr;
}
diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc
index 5c19abd64d6..f9898d22a40 100644
--- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc
+++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc
@@ -76,6 +76,11 @@ class OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane
~VirtualKeyboardInputPane() {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ if (input_pane_) {
+ // Remove the callbacks that were registered.
+ input_pane_->remove_Showing(show_event_token_);
+ input_pane_->remove_Hiding(hide_event_token_);
+ }
}
bool EnsureInputPanePointersInBackgroundThread(HWND hwnd) {
@@ -101,8 +106,10 @@ class OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane
if (FAILED(hr))
return false;
- if (FAILED(input_pane_.As(&input_pane2_)))
+ if (FAILED(input_pane_.As(&input_pane2_))) {
+ input_pane_.Reset();
return false;
+ }
AddCallbacksOnInputPaneShownOrHiddenInBackgroundThread();
return true;
@@ -135,6 +142,11 @@ class OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane
ABI::Windows::UI::ViewManagement::IInputPane* pane,
ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs* args) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ // Due to timing this could be called by the OS even when input_pane_
+ // is null, so just bail out to avoid crashes.
+ if (!input_pane_)
+ return S_OK;
+
ABI::Windows::Foundation::Rect rect;
input_pane_->get_OccludedRect(&rect);
gfx::Rect dip_rect(rect.X, rect.Y, rect.Width, rect.Height);
@@ -154,6 +166,11 @@ class OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane
ABI::Windows::UI::ViewManagement::IInputPane* pane,
ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs* args) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ // Due to timing this could be called by the OS even when input_pane_
+ // is null, so just bail out to avoid crashes.
+ if (!input_pane_)
+ return S_OK;
+
TRACE_EVENT0("vk",
"OnScreenKeyboardDisplayManagerInputPane::"
"VirtualKeyboardInputPane::OnInputPaneHidden");
diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
index 00a2eca6e97..9f14005acfd 100644
--- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
+++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
@@ -93,6 +93,8 @@ class MockInputPane
return S_OK;
}
+ bool CallbacksValid() const { return show_handler_ && hide_handler_; }
+
private:
~MockInputPane() override = default;
@@ -221,4 +223,36 @@ TEST_F(OnScreenKeyboardTest, InputPaneDebounceTimerTest) {
keyboard_display_manager->RemoveObserver(observer.get());
}
+TEST_F(OnScreenKeyboardTest, InputPaneDestruction) {
+ // InputPane is supported only on RS1 and later.
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return;
+ std::unique_ptr<OnScreenKeyboardDisplayManagerInputPane>
+ keyboard_display_manager = CreateInputPane();
+
+ std::unique_ptr<MockInputMethodKeyboardControllerObserver> observer =
+ std::make_unique<MockInputMethodKeyboardControllerObserver>();
+
+ Microsoft::WRL::ComPtr<MockInputPane> input_pane =
+ Microsoft::WRL::Make<MockInputPane>();
+ keyboard_display_manager->SetInputPaneForTesting(input_pane);
+
+ EXPECT_CALL(*observer, OnKeyboardVisible(testing::_)).Times(1);
+ keyboard_display_manager->AddObserver(observer.get());
+ keyboard_display_manager->DisplayVirtualKeyboard();
+ // Additional 300ms for debounce timer.
+ WaitForEventsWithTimeDelay(400);
+ EXPECT_TRUE(input_pane->CallbacksValid());
+
+ testing::Mock::VerifyAndClearExpectations(observer.get());
+ EXPECT_CALL(*observer, OnKeyboardHidden()).Times(1);
+ keyboard_display_manager->DismissVirtualKeyboard();
+ // Additional 300ms for debounce timer.
+ WaitForEventsWithTimeDelay(400);
+ keyboard_display_manager->RemoveObserver(observer.get());
+ keyboard_display_manager = nullptr;
+ WaitForEventsWithTimeDelay(400);
+ EXPECT_FALSE(input_pane->CallbacksValid());
+}
+
} // namespace ui
diff --git a/chromium/ui/base/ime/win/tsf_bridge.cc b/chromium/ui/base/ime/win/tsf_bridge.cc
index f5c8c7155d4..08a7cbb1d8e 100644
--- a/chromium/ui/base/ime/win/tsf_bridge.cc
+++ b/chromium/ui/base/ime/win/tsf_bridge.cc
@@ -8,13 +8,14 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
+#include "base/task/current_thread.h"
#include "base/threading/thread_local_storage.h"
#include "base/win/scoped_variant.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/text_input_client.h"
+#include "ui/base/ime/win/mock_tsf_bridge.h"
#include "ui/base/ime/win/tsf_bridge.h"
#include "ui/base/ime/win/tsf_text_store.h"
#include "ui/base/ui_base_features.h"
@@ -31,7 +32,7 @@ class TSFBridgeImpl : public TSFBridge {
TSFBridgeImpl();
~TSFBridgeImpl() override;
- bool Initialize();
+ HRESULT Initialize();
// TsfBridge:
void OnTextInputTypeChanged(const TextInputClient* client) override;
@@ -48,24 +49,24 @@ class TSFBridgeImpl : public TSFBridge {
void SetInputPanelPolicy(bool input_panel_policy_manual) override;
private:
- // Returns true if |tsf_document_map_| is successfully initialized. This
+ // Returns S_OK if |tsf_document_map_| is successfully initialized. This
// method should be called from and only from Initialize().
- bool InitializeDocumentMapInternal();
+ HRESULT InitializeDocumentMapInternal();
- // Returns true if |context| is successfully updated to be a disabled
+ // Returns S_OK if |context| is successfully updated to be a disabled
// context, where an IME should be deactivated. This is suitable for some
// special input context such as password fields.
- bool InitializeDisabledContext(ITfContext* context);
+ HRESULT InitializeDisabledContext(ITfContext* context);
- // Returns true if a TSF document manager and a TSF context is successfully
+ // Returns S_OK if a TSF document manager and a TSF context is successfully
// created with associating with given |text_store|. The returned
// |source_cookie| indicates the binding between |text_store| and |context|.
// You can pass nullptr to |text_store| and |source_cookie| when text store is
// not necessary.
- bool CreateDocumentManager(TSFTextStore* text_store,
- ITfDocumentMgr** document_manager,
- ITfContext** context,
- DWORD* source_cookie);
+ HRESULT CreateDocumentManager(TSFTextStore* text_store,
+ ITfDocumentMgr** document_manager,
+ ITfContext** context,
+ DWORD* source_cookie);
// Returns true if |document_manager| is the focused document manager.
bool IsFocused(ITfDocumentMgr* document_manager);
@@ -111,10 +112,6 @@ class TSFBridgeImpl : public TSFBridge {
// An ITfThreadMgr object to be used in focus and document management.
Microsoft::WRL::ComPtr<ITfThreadMgr> thread_manager_;
- // An ITfInputProcessorProfiles object to be used to get current language
- // locale profile.
- Microsoft::WRL::ComPtr<ITfInputProcessorProfiles> input_processor_profiles_;
-
// A map from TextInputType to an editable document for TSF. We use multiple
// TSF documents that have different InputScopes and TSF attributes based on
// the TextInputType associated with the target document. For a TextInputType
@@ -132,22 +129,22 @@ class TSFBridgeImpl : public TSFBridge {
// Current focused text input client. Do not free |client_|.
TextInputClient* client_ = nullptr;
+ // Input Type of current focused text input client.
+ TextInputType input_type_ = TEXT_INPUT_TYPE_NONE;
+
// Represents the window that is currently owns text input focus.
HWND attached_window_handle_ = nullptr;
// Handle to ITfKeyTraceEventSink.
DWORD key_trace_sink_cookie_ = 0;
- // Handle to ITfLanguageProfileNotifySink
- DWORD language_profile_cookie_ = 0;
-
DISALLOW_COPY_AND_ASSIGN(TSFBridgeImpl);
};
TSFBridgeImpl::TSFBridgeImpl() = default;
TSFBridgeImpl::~TSFBridgeImpl() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
if (!IsInitialized())
return;
@@ -156,11 +153,6 @@ TSFBridgeImpl::~TSFBridgeImpl() {
if (SUCCEEDED(thread_manager_->QueryInterface(IID_PPV_ARGS(&source)))) {
source->UnadviseSink(key_trace_sink_cookie_);
}
- Microsoft::WRL::ComPtr<ITfSource> language_source;
- if (SUCCEEDED(input_processor_profiles_->QueryInterface(
- IID_PPV_ARGS(&language_source)))) {
- language_source->UnadviseSink(language_profile_cookie_);
- }
}
for (TSFDocumentMap::iterator it = tsf_document_map_.begin();
@@ -168,9 +160,8 @@ TSFBridgeImpl::~TSFBridgeImpl() {
Microsoft::WRL::ComPtr<ITfContext> context;
Microsoft::WRL::ComPtr<ITfSource> source;
if (it->second.cookie != TF_INVALID_COOKIE &&
- SUCCEEDED(
- it->second.document_manager->GetBase(context.GetAddressOf())) &&
- SUCCEEDED(context.CopyTo(source.GetAddressOf()))) {
+ SUCCEEDED(it->second.document_manager->GetBase(&context)) &&
+ SUCCEEDED(context.As(&source))) {
source->UnadviseSink(it->second.cookie);
}
}
@@ -179,66 +170,62 @@ TSFBridgeImpl::~TSFBridgeImpl() {
client_id_ = TF_CLIENTID_NULL;
}
-bool TSFBridgeImpl::Initialize() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+HRESULT TSFBridgeImpl::Initialize() {
+ DCHECK(base::CurrentUIThread::IsSet());
if (client_id_ != TF_CLIENTID_NULL) {
DVLOG(1) << "Already initialized.";
- return false;
+ return S_FALSE;
}
- if (FAILED(::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
- CLSCTX_ALL,
- IID_PPV_ARGS(&input_processor_profiles_)))) {
- DVLOG(1) << "Failed to create InputProcessorProfiles instance.";
- return false;
- }
-
- if (FAILED(::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL,
- IID_PPV_ARGS(&thread_manager_)))) {
+ HRESULT hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL,
+ IID_PPV_ARGS(&thread_manager_));
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to create ThreadManager instance.";
- return false;
+ return hr;
}
- if (FAILED(thread_manager_->Activate(&client_id_))) {
+ hr = thread_manager_->Activate(&client_id_);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to activate Thread Manager.";
- return false;
+ return hr;
}
- if (!InitializeDocumentMapInternal())
- return false;
+ hr = InitializeDocumentMapInternal();
+ if (FAILED(hr))
+ return hr;
// Japanese IME expects the default value of this compartment is
// TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is
// managed per thread, so that it is enough to set this value at once. This
// value does not affect other language's IME behaviors.
Microsoft::WRL::ComPtr<ITfCompartmentMgr> thread_compartment_manager;
- if (FAILED(
- thread_manager_.CopyTo(thread_compartment_manager.GetAddressOf()))) {
+ hr = thread_manager_.As(&thread_compartment_manager);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get ITfCompartmentMgr.";
- return false;
+ return hr;
}
Microsoft::WRL::ComPtr<ITfCompartment> sentence_compartment;
- if (FAILED(thread_compartment_manager->GetCompartment(
- GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
- sentence_compartment.GetAddressOf()))) {
+ hr = thread_compartment_manager->GetCompartment(
+ GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, &sentence_compartment);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get sentence compartment.";
- return false;
+ return hr;
}
base::win::ScopedVariant sentence_variant;
sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
- if (FAILED(
- sentence_compartment->SetValue(client_id_, sentence_variant.ptr()))) {
+ hr = sentence_compartment->SetValue(client_id_, sentence_variant.ptr());
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to change the sentence mode.";
- return false;
+ return hr;
}
- return true;
+ return S_OK;
}
void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(IsInitialized());
if (client != client_) {
@@ -246,6 +233,7 @@ void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) {
return;
}
+ input_type_ = client_->GetTextInputType();
TSFDocument* document = GetAssociatedDocument();
if (!document)
return;
@@ -254,7 +242,7 @@ void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) {
// focus notifications for the same text input type so we don't
// call AssociateFocus and SetFocus together. Just calling SetFocus
// should be sufficient for setting focus on a textstore.
- if (client_->GetTextInputType() != TEXT_INPUT_TYPE_NONE)
+ if (input_type_ != TEXT_INPUT_TYPE_NONE)
thread_manager_->SetFocus(document->document_manager.Get());
else
UpdateAssociateFocus();
@@ -280,7 +268,7 @@ void TSFBridgeImpl::SetInputPanelPolicy(bool input_panel_policy_manual) {
}
bool TSFBridgeImpl::CancelComposition() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(IsInitialized());
TSFDocument* document = GetAssociatedDocument();
@@ -293,7 +281,7 @@ bool TSFBridgeImpl::CancelComposition() {
}
bool TSFBridgeImpl::ConfirmComposition() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(IsInitialized());
TSFDocument* document = GetAssociatedDocument();
@@ -307,7 +295,7 @@ bool TSFBridgeImpl::ConfirmComposition() {
void TSFBridgeImpl::SetFocusedClient(HWND focused_window,
TextInputClient* client) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(client);
DCHECK(IsInitialized());
if (attached_window_handle_ != focused_window)
@@ -327,7 +315,7 @@ void TSFBridgeImpl::SetFocusedClient(HWND focused_window,
}
void TSFBridgeImpl::RemoveFocusedClient(TextInputClient* client) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
if (!IsInitialized())
return;
if (client_ != client)
@@ -345,7 +333,7 @@ void TSFBridgeImpl::RemoveFocusedClient(TextInputClient* client) {
void TSFBridgeImpl::SetInputMethodDelegate(
internal::InputMethodDelegate* delegate) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(delegate);
DCHECK(IsInitialized());
@@ -358,7 +346,7 @@ void TSFBridgeImpl::SetInputMethodDelegate(
}
void TSFBridgeImpl::RemoveInputMethodDelegate() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(IsInitialized());
for (TSFDocumentMap::iterator it = tsf_document_map_.begin();
@@ -383,88 +371,78 @@ TextInputClient* TSFBridgeImpl::GetFocusedTextInputClient() const {
}
Microsoft::WRL::ComPtr<ITfThreadMgr> TSFBridgeImpl::GetThreadManager() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(IsInitialized());
return thread_manager_;
}
-bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
- ITfDocumentMgr** document_manager,
- ITfContext** context,
- DWORD* source_cookie) {
- if (FAILED(thread_manager_->CreateDocumentMgr(document_manager))) {
+HRESULT TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
+ ITfDocumentMgr** document_manager,
+ ITfContext** context,
+ DWORD* source_cookie) {
+ HRESULT hr = thread_manager_->CreateDocumentMgr(document_manager);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to create Document Manager.";
- return false;
+ return hr;
}
if (!text_store || !source_cookie)
- return true;
+ return S_OK;
DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
- if (FAILED((*document_manager)
- ->CreateContext(client_id_, 0,
- static_cast<ITextStoreACP*>(text_store),
- context, &edit_cookie))) {
+ hr = (*document_manager)
+ ->CreateContext(client_id_, 0,
+ static_cast<ITextStoreACP*>(text_store), context,
+ &edit_cookie);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to create Context.";
- return false;
+ return hr;
}
- if (FAILED((*document_manager)->Push(*context))) {
+ hr = (*document_manager)->Push(*context);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to push context.";
- return false;
+ return hr;
}
Microsoft::WRL::ComPtr<ITfSource> source;
- if (FAILED((*context)->QueryInterface(IID_PPV_ARGS(&source)))) {
+ hr = (*context)->QueryInterface(IID_PPV_ARGS(&source));
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get source.";
- return false;
+ return hr;
}
- if (FAILED(source->AdviseSink(IID_ITfTextEditSink,
- static_cast<ITfTextEditSink*>(text_store),
- source_cookie))) {
+ hr = source->AdviseSink(IID_ITfTextEditSink,
+ static_cast<ITfTextEditSink*>(text_store),
+ source_cookie);
+ if (FAILED(hr)) {
DVLOG(1) << "AdviseSink failed.";
- return false;
+ return hr;
}
Microsoft::WRL::ComPtr<ITfSource> source_ITfThreadMgr;
- if (FAILED(thread_manager_->QueryInterface(
- IID_PPV_ARGS(&source_ITfThreadMgr)))) {
+ hr = thread_manager_->QueryInterface(IID_PPV_ARGS(&source_ITfThreadMgr));
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get source_ITfThreadMgr.";
- return false;
+ return hr;
}
- if (FAILED(source_ITfThreadMgr->AdviseSink(
- IID_ITfKeyTraceEventSink,
- static_cast<ITfKeyTraceEventSink*>(text_store),
- &key_trace_sink_cookie_))) {
+ hr = source_ITfThreadMgr->AdviseSink(
+ IID_ITfKeyTraceEventSink, static_cast<ITfKeyTraceEventSink*>(text_store),
+ &key_trace_sink_cookie_);
+ if (FAILED(hr)) {
DVLOG(1) << "AdviseSink for ITfKeyTraceEventSink failed.";
- return false;
- }
-
- Microsoft::WRL::ComPtr<ITfSource> language_source;
- if (FAILED(input_processor_profiles_->QueryInterface(
- IID_PPV_ARGS(&language_source)))) {
- DVLOG(1) << "Failed to get source_ITfInputProcessorProfiles.";
- return false;
- }
-
- if (FAILED(
- language_source->AdviseSink(IID_ITfLanguageProfileNotifySink,
- static_cast<ITfTextEditSink*>(text_store),
- &language_profile_cookie_))) {
- DVLOG(1) << "AdviseSink for language profile notify sink failed.";
- return false;
+ return hr;
}
if (*source_cookie == TF_INVALID_COOKIE) {
DVLOG(1) << "The result of cookie is invalid.";
- return false;
+ return E_FAIL;
}
- return true;
+ return S_OK;
}
-bool TSFBridgeImpl::InitializeDocumentMapInternal() {
+HRESULT TSFBridgeImpl::InitializeDocumentMapInternal() {
const TextInputType kTextInputTypes[] = {
TEXT_INPUT_TYPE_NONE, TEXT_INPUT_TYPE_TEXT,
TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_TYPE_SEARCH,
@@ -480,59 +458,70 @@ bool TSFBridgeImpl::InitializeDocumentMapInternal() {
DWORD* cookie_ptr = use_null_text_store ? nullptr : &cookie;
scoped_refptr<TSFTextStore> text_store =
use_null_text_store ? nullptr : new TSFTextStore();
- if (!CreateDocumentManager(text_store.get(),
- document_manager.GetAddressOf(),
- context.GetAddressOf(), cookie_ptr))
- return false;
- if ((input_type == TEXT_INPUT_TYPE_PASSWORD) &&
- !InitializeDisabledContext(context.Get()))
- return false;
+ HRESULT hr = S_OK;
+ if (text_store) {
+ HRESULT hr = text_store->Initialize();
+ if (FAILED(hr))
+ return hr;
+ }
+ hr = CreateDocumentManager(text_store.get(), &document_manager, &context,
+ cookie_ptr);
+ if (FAILED(hr))
+ return hr;
+ if (input_type == TEXT_INPUT_TYPE_PASSWORD) {
+ hr = InitializeDisabledContext(context.Get());
+ if (FAILED(hr))
+ return hr;
+ }
tsf_document_map_[input_type].text_store = text_store;
tsf_document_map_[input_type].document_manager = document_manager;
tsf_document_map_[input_type].cookie = cookie;
if (text_store)
text_store->OnContextInitialized(context.Get());
}
- return true;
+ return S_OK;
}
-bool TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) {
+HRESULT TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) {
Microsoft::WRL::ComPtr<ITfCompartmentMgr> compartment_mgr;
- if (FAILED(context->QueryInterface(IID_PPV_ARGS(&compartment_mgr)))) {
+ HRESULT hr = context->QueryInterface(IID_PPV_ARGS(&compartment_mgr));
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get CompartmentMgr.";
- return false;
+ return hr;
}
Microsoft::WRL::ComPtr<ITfCompartment> disabled_compartment;
- if (FAILED(compartment_mgr->GetCompartment(
- GUID_COMPARTMENT_KEYBOARD_DISABLED,
- disabled_compartment.GetAddressOf()))) {
+ hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
+ &disabled_compartment);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get keyboard disabled compartment.";
- return false;
+ return hr;
}
base::win::ScopedVariant variant;
variant.Set(1);
- if (FAILED(disabled_compartment->SetValue(client_id_, variant.ptr()))) {
+ hr = disabled_compartment->SetValue(client_id_, variant.ptr());
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to disable the DocumentMgr.";
- return false;
+ return hr;
}
Microsoft::WRL::ComPtr<ITfCompartment> empty_context;
- if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
- empty_context.GetAddressOf()))) {
+ hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
+ &empty_context);
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to get empty context compartment.";
- return false;
+ return hr;
}
base::win::ScopedVariant empty_context_variant;
empty_context_variant.Set(static_cast<int32_t>(1));
- if (FAILED(
- empty_context->SetValue(client_id_, empty_context_variant.ptr()))) {
+ hr = empty_context->SetValue(client_id_, empty_context_variant.ptr());
+ if (FAILED(hr)) {
DVLOG(1) << "Failed to set empty context.";
- return false;
+ return hr;
}
- return true;
+ return S_OK;
}
bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) {
@@ -541,8 +530,7 @@ bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) {
return false;
}
Microsoft::WRL::ComPtr<ITfDocumentMgr> focused_document_manager;
- if (FAILED(
- thread_manager_->GetFocus(focused_document_manager.GetAddressOf())))
+ if (FAILED(thread_manager_->GetFocus(&focused_document_manager)))
return false;
return focused_document_manager.Get() == document_manager;
}
@@ -572,7 +560,7 @@ void TSFBridgeImpl::UpdateAssociateFocus() {
Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus;
thread_manager_->AssociateFocus(attached_window_handle_,
document->document_manager.Get(),
- previous_focus.GetAddressOf());
+ &previous_focus);
}
void TSFBridgeImpl::ClearAssociateFocus() {
@@ -584,14 +572,13 @@ void TSFBridgeImpl::ClearAssociateFocus() {
return;
Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus;
thread_manager_->AssociateFocus(attached_window_handle_, nullptr,
- previous_focus.GetAddressOf());
+ &previous_focus);
}
TSFBridgeImpl::TSFDocument* TSFBridgeImpl::GetAssociatedDocument() {
if (!client_)
return nullptr;
- TSFDocumentMap::iterator it =
- tsf_document_map_.find(client_->GetTextInputType());
+ TSFDocumentMap::iterator it = tsf_document_map_.find(input_type_);
if (it == tsf_document_map_.end()) {
it = tsf_document_map_.find(TEXT_INPUT_TYPE_TEXT);
// This check is necessary because it's possible that we failed to
@@ -622,26 +609,38 @@ TSFBridge::TSFBridge() {}
TSFBridge::~TSFBridge() {}
// static
-void TSFBridge::Initialize() {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+HRESULT TSFBridge::Initialize() {
+ if (!base::CurrentUIThread::IsSet()) {
DVLOG(1) << "Do not use TSFBridge without UI thread.";
- return;
+ return E_FAIL;
}
TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get());
if (delegate)
- return;
+ return S_OK;
// If we aren't supporting TSF early out.
if (!base::FeatureList::IsEnabled(features::kTSFImeSupport))
- return;
+ return E_FAIL;
delegate = new TSFBridgeImpl();
TSFBridgeTLS().Set(delegate);
- delegate->Initialize();
+ return delegate->Initialize();
+}
+
+// static
+void TSFBridge::InitializeForTesting() {
+ if (!base::CurrentUIThread::IsSet()) {
+ return;
+ }
+ TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get());
+ if (delegate)
+ return;
+ if (!base::FeatureList::IsEnabled(features::kTSFImeSupport))
+ return;
+ TSFBridgeTLS().Set(new MockTSFBridge());
}
// static
TSFBridge* TSFBridge::ReplaceForTesting(TSFBridge* bridge) {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
- DVLOG(1) << "Do not use TSFBridge without UI thread.";
+ if (!base::CurrentUIThread::IsSet()) {
return nullptr;
}
TSFBridge* old_bridge = TSFBridge::GetInstance();
@@ -651,7 +650,7 @@ TSFBridge* TSFBridge::ReplaceForTesting(TSFBridge* bridge) {
// static
void TSFBridge::Shutdown() {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
DVLOG(1) << "Do not use TSFBridge without UI thread.";
}
TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get());
@@ -661,7 +660,7 @@ void TSFBridge::Shutdown() {
// static
TSFBridge* TSFBridge::GetInstance() {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
DVLOG(1) << "Do not use TSFBridge without UI thread.";
return nullptr;
}
diff --git a/chromium/ui/base/ime/win/tsf_bridge.h b/chromium/ui/base/ime/win/tsf_bridge.h
index e9a30587ac4..0a9cb1b10ea 100644
--- a/chromium/ui/base/ime/win/tsf_bridge.h
+++ b/chromium/ui/base/ime/win/tsf_bridge.h
@@ -36,7 +36,11 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFBridge {
// Sets the thread local instance. Must be called before any calls to
// GetInstance().
- static void Initialize();
+ static HRESULT Initialize();
+
+ // Sets the thread local instance for testing only. Must be called before
+ // any calls to GetInstance().
+ static void InitializeForTesting();
// Injects an alternative TSFBridge such as MockTSFBridge for testing. The
// injected object should be released by the caller. This function returns
diff --git a/chromium/ui/base/ime/win/tsf_event_router.cc b/chromium/ui/base/ime/win/tsf_event_router.cc
index 9d0584dbd3f..54e17693f88 100644
--- a/chromium/ui/base/ime/win/tsf_event_router.cc
+++ b/chromium/ui/base/ime/win/tsf_event_router.cc
@@ -112,12 +112,12 @@ HRESULT TSFEventRouter::Delegate::OnEndEdit(ITfContext* context,
// updated text.
Microsoft::WRL::ComPtr<IEnumTfRanges> ranges;
if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, nullptr,
- 0, ranges.GetAddressOf())))
+ 0, &ranges)))
return S_OK; // Don't care about failures.
ULONG fetched_count = 0;
Microsoft::WRL::ComPtr<ITfRange> range;
- if (FAILED(ranges->Next(1, range.GetAddressOf(), &fetched_count)))
+ if (FAILED(ranges->Next(1, &range, &fetched_count)))
return S_OK; // Don't care about failures.
const gfx::Range composition_range = GetCompositionRange(context);
@@ -184,10 +184,9 @@ void TSFEventRouter::Delegate::SetManager(ITfThreadMgr* thread_manager) {
return;
Microsoft::WRL::ComPtr<ITfDocumentMgr> document_manager;
- if (FAILED(thread_manager->GetFocus(document_manager.GetAddressOf())) ||
- !document_manager.Get() ||
- FAILED(document_manager->GetBase(context_.GetAddressOf())) ||
- FAILED(context_.CopyTo(context_source_.GetAddressOf())))
+ if (FAILED(thread_manager->GetFocus(&document_manager)) ||
+ !document_manager.Get() || FAILED(document_manager->GetBase(&context_)) ||
+ FAILED(context_.As(&context_source_)))
return;
context_source_->AdviseSink(IID_ITfTextEditSink,
static_cast<ITfTextEditSink*>(this),
@@ -195,7 +194,7 @@ void TSFEventRouter::Delegate::SetManager(ITfThreadMgr* thread_manager) {
if (FAILED(
thread_manager->QueryInterface(IID_PPV_ARGS(&ui_element_manager_))) ||
- FAILED(ui_element_manager_.CopyTo(ui_source_.GetAddressOf())))
+ FAILED(ui_element_manager_.As(&ui_source_)))
return;
ui_source_->AdviseSink(IID_ITfUIElementSink,
static_cast<ITfUIElementSink*>(this),
@@ -213,20 +212,18 @@ gfx::Range TSFEventRouter::Delegate::GetCompositionRange(ITfContext* context) {
if (FAILED(context->QueryInterface(IID_PPV_ARGS(&context_composition))))
return gfx::Range::InvalidRange();
Microsoft::WRL::ComPtr<IEnumITfCompositionView> enum_composition_view;
- if (FAILED(context_composition->EnumCompositions(
- enum_composition_view.GetAddressOf())))
+ if (FAILED(context_composition->EnumCompositions(&enum_composition_view)))
return gfx::Range::InvalidRange();
Microsoft::WRL::ComPtr<ITfCompositionView> composition_view;
- if (enum_composition_view->Next(1, composition_view.GetAddressOf(),
- nullptr) != S_OK)
+ if (enum_composition_view->Next(1, &composition_view, nullptr) != S_OK)
return gfx::Range::InvalidRange();
Microsoft::WRL::ComPtr<ITfRange> range;
- if (FAILED(composition_view->GetRange(range.GetAddressOf())))
+ if (FAILED(composition_view->GetRange(&range)))
return gfx::Range::InvalidRange();
Microsoft::WRL::ComPtr<ITfRangeACP> range_acp;
- if (FAILED(range.CopyTo(range_acp.GetAddressOf())))
+ if (FAILED(range.As(&range_acp)))
return gfx::Range::InvalidRange();
LONG start = 0;
@@ -240,11 +237,10 @@ gfx::Range TSFEventRouter::Delegate::GetCompositionRange(ITfContext* context) {
bool TSFEventRouter::Delegate::IsCandidateWindowInternal(DWORD element_id) {
DCHECK(ui_element_manager_.Get());
Microsoft::WRL::ComPtr<ITfUIElement> ui_element;
- if (FAILED(ui_element_manager_->GetUIElement(element_id,
- ui_element.GetAddressOf())))
+ if (FAILED(ui_element_manager_->GetUIElement(element_id, &ui_element)))
return false;
Microsoft::WRL::ComPtr<ITfCandidateListUIElement> candidate_list_ui_element;
- return SUCCEEDED(ui_element.CopyTo(candidate_list_ui_element.GetAddressOf()));
+ return SUCCEEDED(ui_element.As(&candidate_list_ui_element));
}
// TSFEventRouter ------------------------------------------------------------
diff --git a/chromium/ui/base/ime/win/tsf_input_policy_unittest.cc b/chromium/ui/base/ime/win/tsf_input_policy_unittest.cc
index 03f99a30ec6..ad602b71a4a 100644
--- a/chromium/ui/base/ime/win/tsf_input_policy_unittest.cc
+++ b/chromium/ui/base/ime/win/tsf_input_policy_unittest.cc
@@ -39,7 +39,7 @@ class MockTextInputClient : public TextInputClient {
public:
~MockTextInputClient() {}
MOCK_METHOD1(SetCompositionText, void(const ui::CompositionText&));
- MOCK_METHOD1(ConfirmCompositionText, void(bool));
+ MOCK_METHOD1(ConfirmCompositionText, uint32_t(bool));
MOCK_METHOD0(ClearCompositionText, void());
MOCK_METHOD1(InsertText, void(const base::string16&));
MOCK_METHOD1(InsertChar, void(const ui::KeyEvent&));
@@ -187,6 +187,7 @@ class TSFInputPanelTest : public testing::Test {
protected:
void SetUp() override {
text_store_ = new TSFTextStore();
+ EXPECT_EQ(S_OK, text_store_->Initialize());
sink_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(),
TS_AS_ALL_SINKS));
@@ -216,7 +217,9 @@ class TSFMultipleInputPanelTest : public testing::Test {
protected:
void SetUp() override {
text_store1_ = new TSFTextStore();
+ EXPECT_EQ(S_OK, text_store1_->Initialize());
text_store2_ = new TSFTextStore();
+ EXPECT_EQ(S_OK, text_store2_->Initialize());
sink1_ = new MockStoreACPSink();
sink2_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store1_->AdviseSink(IID_ITextStoreACPSink,
diff --git a/chromium/ui/base/ime/win/tsf_input_scope.cc b/chromium/ui/base/ime/win/tsf_input_scope.cc
index 54e009e0a1e..d60126e2ea4 100644
--- a/chromium/ui/base/ime/win/tsf_input_scope.cc
+++ b/chromium/ui/base/ime/win/tsf_input_scope.cc
@@ -9,8 +9,8 @@
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/stl_util.h"
+#include "base/task/current_thread.h"
#include "base/win/windows_version.h"
namespace ui {
@@ -96,8 +96,8 @@ class TSFInputScope final : public ITfInputScope {
// The corresponding text input types.
std::vector<InputScope> input_scopes_;
- // The refrence count of this instance.
- volatile LONG ref_count_;
+ // The reference count of this instance.
+ volatile ULONG ref_count_;
DISALLOW_COPY_AND_ASSIGN(TSFInputScope);
};
@@ -155,7 +155,7 @@ InputScope ConvertTextInputModeToInputScope(TextInputMode text_input_mode) {
} // namespace
void InitializeTsfForInputScopes() {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
// Thread safety is not required because this function is under UI thread.
if (!g_get_proc_done) {
g_get_proc_done = true;
diff --git a/chromium/ui/base/ime/win/tsf_text_store.cc b/chromium/ui/base/ime/win/tsf_text_store.cc
index 98ca302320d..1eafee49d3e 100644
--- a/chromium/ui/base/ime/win/tsf_text_store.cc
+++ b/chromium/ui/base/ime/win/tsf_text_store.cc
@@ -47,21 +47,27 @@ bool GetWindowClientRect(HWND window_handle,
} // namespace
-TSFTextStore::TSFTextStore() {
- if (FAILED(::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL,
- IID_PPV_ARGS(&category_manager_)))) {
- LOG(FATAL) << "Failed to initialize CategoryMgr.";
- return;
+TSFTextStore::TSFTextStore() {}
+
+TSFTextStore::~TSFTextStore() {}
+
+HRESULT TSFTextStore::Initialize() {
+ HRESULT hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL,
+ IID_PPV_ARGS(&category_manager_));
+ if (FAILED(hr)) {
+ DVLOG(1) << "Failed to initialize CategoryMgr.";
+ return hr;
}
- if (FAILED(::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
- CLSCTX_ALL,
- IID_PPV_ARGS(&display_attribute_manager_)))) {
- LOG(FATAL) << "Failed to initialize DisplayAttributeMgr.";
- return;
+
+ hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr, CLSCTX_ALL,
+ IID_PPV_ARGS(&display_attribute_manager_));
+ if (FAILED(hr)) {
+ DVLOG(1) << "Failed to initialize DisplayAttributeMgr.";
+ return hr;
}
-}
-TSFTextStore::~TSFTextStore() {}
+ return S_OK;
+}
ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() {
return InterlockedIncrement(&ref_count_);
@@ -81,8 +87,6 @@ HRESULT TSFTextStore::QueryInterface(REFIID iid, void** result) {
*result = static_cast<ITextStoreACP*>(this);
} else if (iid == IID_ITfContextOwnerCompositionSink) {
*result = static_cast<ITfContextOwnerCompositionSink*>(this);
- } else if (iid == IID_ITfLanguageProfileNotifySink) {
- *result = static_cast<ITfLanguageProfileNotifySink*>(this);
} else if (iid == IID_ITfTextEditSink) {
*result = static_cast<ITfTextEditSink*>(this);
} else if (iid == IID_ITfKeyTraceEventSink) {
@@ -462,6 +466,29 @@ HRESULT TSFTextStore::InsertTextAtSelection(DWORD flags,
if (!text_buffer)
return E_INVALIDARG;
+ if (text_buffer_size >= 0) {
+ if (!new_text_inserted_) {
+ new_text_inserted_ = true;
+ replace_text_range_.set_start(start_pos);
+ replace_text_range_.set_end(end_pos);
+ replace_text_size_ = text_buffer_size;
+ } else {
+ // aggregate new replace text with previous replace text into one range.
+ LONG old_delta = (LONG)replace_text_range_.start() -
+ (LONG)replace_text_range_.end() + replace_text_size_;
+ LONG new_delta = start_pos - end_pos + text_buffer_size;
+ replace_text_range_.set_start(
+ std::min((uint32_t)start_pos, replace_text_range_.start()));
+ // New replacement text ends after previous replacement text. We need to
+ // use the new end after adjusting with previous delta.
+ if ((uint32_t)end_pos >=
+ replace_text_range_.start() + replace_text_size_) {
+ replace_text_range_.set_end(end_pos - old_delta);
+ }
+ replace_text_size_ = replace_text_range_.length() + old_delta + new_delta;
+ }
+ }
+
DCHECK_LE(start_pos, end_pos);
string_buffer_document_ =
string_buffer_document_.substr(0, start_pos) +
@@ -584,6 +611,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
// if nothing has changed from input service, then only need to
// compare our cache with latest textinputstate.
if (!edit_flag_) {
+ ResetCacheAfterEditSession();
CalculateTextandSelectionDiffAndNotifyIfNeeded();
return S_OK;
}
@@ -601,7 +629,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
// into blink to complete the existing composition(later in this method).
if (string_pending_insertion_.empty()) {
if (!text_input_client_->HasCompositionText()) {
- if (has_composition_range_) {
+ if (has_composition_range_ && on_start_composition_called_) {
// Remove replacing text first before starting composition.
if (new_text_inserted_ && !replace_text_range_.is_empty() &&
!replace_text_size_) {
@@ -615,6 +643,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
composition_start_ = selection_.start();
CalculateTextandSelectionDiffAndNotifyIfNeeded();
}
+ ResetCacheAfterEditSession();
return S_OK;
} else {
composition_start_ = last_composition_start;
@@ -630,12 +659,14 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
lparam_keydown_cached_);
}
- // reset |on_start_composition_called_| for next edit session.
- on_start_composition_called_ = false;
-
// If the text store is edited in OnLockGranted(), we may need to call
// TextInputClient::InsertText() or TextInputClient::SetCompositionText().
- const size_t new_composition_start = composition_start_;
+ // Calculate the end location. we use the replace text end pos if there is no
+ // more active composition.
+ size_t new_composition_start =
+ !has_composition_range_ && new_text_inserted_
+ ? replace_text_range_.start() + replace_text_size_
+ : composition_start_;
// There are several scenarios that we want to commit composition text. For
// those scenarios, we need to call TextInputClient::InsertText to complete
@@ -693,15 +724,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
StartCompositionOnNewText(new_composition_start, composition_string);
}
- // reset the flag since we've already inserted/replaced the text.
- new_text_inserted_ = false;
- is_selection_interim_char_ = false;
-
- // reset string_buffer_ if composition is no longer active.
- if (!text_input_client_->HasCompositionText()) {
- string_pending_insertion_.clear();
- }
-
+ ResetCacheAfterEditSession();
CalculateTextandSelectionDiffAndNotifyIfNeeded();
return S_OK;
@@ -788,13 +811,6 @@ HRESULT TSFTextStore::SetText(DWORD flags,
return ret;
TS_TEXTCHANGE change;
- if (text_buffer_size >= 0) {
- new_text_inserted_ = true;
- replace_text_range_.set_start(acp_start);
- replace_text_range_.set_end(acp_end);
- replace_text_size_ = text_buffer_size;
- }
-
ret = InsertTextAtSelection(0, text_buffer, text_buffer_size, &acp_start,
&acp_end, &change);
if (ret != S_OK)
@@ -832,15 +848,6 @@ HRESULT TSFTextStore::OnEndComposition(ITfCompositionView* composition_view) {
return S_OK;
}
-HRESULT TSFTextStore::OnLanguageChange(LANGID langid, BOOL* pfAccept) {
- return S_OK;
-}
-HRESULT TSFTextStore::OnLanguageChanged() {
- if (text_input_client_)
- text_input_client_->OnInputMethodChanged();
- return S_OK;
-}
-
HRESULT TSFTextStore::OnKeyTraceDown(WPARAM wParam, LPARAM lParam) {
// fire the event right away if we're in composition
if (has_composition_range_) {
@@ -973,7 +980,7 @@ bool TSFTextStore::GetDisplayAttribute(TfGuidAtom guid_atom,
Microsoft::WRL::ComPtr<ITfDisplayAttributeInfo> display_attribute_info;
if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo(
- guid, display_attribute_info.GetAddressOf(), nullptr))) {
+ guid, &display_attribute_info, nullptr))) {
return false;
}
// Display Attribute can be null so query for attributes only when its
@@ -993,8 +1000,8 @@ bool TSFTextStore::GetCompositionStatus(
DCHECK(spans);
const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE};
Microsoft::WRL::ComPtr<ITfReadOnlyProperty> track_property;
- if (FAILED(context->TrackProperties(rgGuids, 2, nullptr, 0,
- track_property.GetAddressOf()))) {
+ if (FAILED(
+ context->TrackProperties(rgGuids, 2, nullptr, 0, &track_property))) {
return false;
}
@@ -1002,11 +1009,10 @@ bool TSFTextStore::GetCompositionStatus(
spans->clear();
Microsoft::WRL::ComPtr<ITfRange> start_to_end_range;
Microsoft::WRL::ComPtr<ITfRange> end_range;
- if (FAILED(context->GetStart(read_only_edit_cookie,
- start_to_end_range.GetAddressOf()))) {
+ if (FAILED(context->GetStart(read_only_edit_cookie, &start_to_end_range))) {
return false;
}
- if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.GetAddressOf())))
+ if (FAILED(context->GetEnd(read_only_edit_cookie, &end_range)))
return false;
if (FAILED(start_to_end_range->ShiftEndToRange(
read_only_edit_cookie, end_range.Get(), TF_ANCHOR_END))) {
@@ -1014,15 +1020,14 @@ bool TSFTextStore::GetCompositionStatus(
}
Microsoft::WRL::ComPtr<IEnumTfRanges> ranges;
- if (FAILED(track_property->EnumRanges(read_only_edit_cookie,
- ranges.GetAddressOf(),
+ if (FAILED(track_property->EnumRanges(read_only_edit_cookie, &ranges,
start_to_end_range.Get()))) {
return false;
}
while (true) {
Microsoft::WRL::ComPtr<ITfRange> range;
- if (ranges->Next(1, range.GetAddressOf(), nullptr) != S_OK)
+ if (ranges->Next(1, &range, nullptr) != S_OK)
break;
base::win::ScopedVariant value;
Microsoft::WRL::ComPtr<IEnumTfPropertyValue> enum_prop_value;
@@ -1051,7 +1056,7 @@ bool TSFTextStore::GetCompositionStatus(
}
Microsoft::WRL::ComPtr<ITfRangeACP> range_acp;
- range.CopyTo(range_acp.GetAddressOf());
+ range.As(&range_acp);
LONG start_pos, length;
range_acp->GetExtent(&start_pos, &length);
if (!is_composition) {
@@ -1306,8 +1311,9 @@ void TSFTextStore::StartCompositionOnExistingText() const {
void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size,
size_t new_size) const {
- if (new_text_inserted_ && !replace_text_range_.is_empty() &&
- !text_input_client_->HasCompositionText()) {
+ size_t new_committed_string_offset;
+ size_t new_committed_string_size;
+ if (new_text_inserted_ && !text_input_client_->HasCompositionText()) {
// This is a special case to handle text replacement scenarios during
// English typing when we are trying to replace an existing text with some
// new text. Some third-party IMEs also use SetText() API instead of
@@ -1325,14 +1331,13 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size,
// current composition text so that |TextInputClient::InsertText| will
// commit current composition text.
// Also clamp the offsets if they are out of bounds of the buffer
- const size_t new_committed_string_offset =
+ new_committed_string_offset =
std::min(static_cast<ULONG>(replace_text_range_.start()),
static_cast<ULONG>(string_buffer_document_.size()));
- const base::string16& new_committed_string = string_buffer_document_.substr(
- new_committed_string_offset,
+ new_committed_string_size =
(new_text_size == 0 && selection_.end() > new_committed_string_offset)
? selection_.end() - new_committed_string_offset
- : new_text_size);
+ : new_text_size;
// if the |replace_text_range_| start is greater than |old_size|, then we
// don't need to delete anything because the replacement text hasn't been
// inserted into blink yet.
@@ -1340,14 +1345,9 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size,
text_input_client_->ExtendSelectionAndDelete(
old_size - replace_text_range_.start(), 0);
}
- // TODO(crbug.com/978678): Unify the behavior of
- // |TextInputClient::InsertText(text)| for the empty text.
- if (!new_committed_string.empty())
- text_input_client_->InsertText(new_committed_string);
} else {
- // Construct string to be committed.
- size_t new_committed_string_offset = old_size;
- size_t new_committed_string_size = new_size - old_size;
+ new_committed_string_offset = old_size;
+ new_committed_string_size = new_size - old_size;
// This is a special case. We should only replace existing text and commit
// the new text if replacement text has already been inserted into Blink.
if (new_text_inserted_ && (old_size > replace_text_range_.start()) &&
@@ -1363,24 +1363,43 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size,
new_committed_string_offset =
std::min(static_cast<ULONG>(new_committed_string_offset),
static_cast<ULONG>(string_buffer_document_.size()));
- const base::string16& new_committed_string = string_buffer_document_.substr(
- new_committed_string_offset,
+ new_committed_string_size =
(new_committed_string_size == 0 &&
selection_.end() > new_committed_string_offset)
? selection_.end() - new_committed_string_offset
- : new_committed_string_size);
- // TODO(crbug.com/978678): Unify the behavior of
- // |TextInputClient::InsertText(text)| for the empty text.
- if (!new_committed_string.empty()) {
- text_input_client_->InsertText(new_committed_string);
- } else {
- text_input_client_->ClearCompositionText();
+ : new_committed_string_size;
+ }
+
+ // Construct string to be committed.
+ const base::string16& new_committed_string = string_buffer_document_.substr(
+ new_committed_string_offset, new_committed_string_size);
+ // TODO(crbug.com/978678): Unify the behavior of
+ // |TextInputClient::InsertText(text)| for the empty text.
+ if (!new_committed_string.empty()) {
+ // If composition was started and committed in one edit session, we still
+ // need to start the composition first and then commit it.
+ if (!text_input_client_->HasCompositionText() &&
+ on_start_composition_called_) {
+ ImeTextSpans spans;
+ ImeTextSpan span;
+ span.start_offset = 0;
+ span.end_offset = new_committed_string.size();
+ spans.push_back(span);
+ CompositionText composition_text;
+ composition_text.text = new_committed_string;
+ composition_text.ime_text_spans = spans;
+ composition_text.selection.set_start(new_committed_string.size());
+ composition_text.selection.set_end(new_committed_string.size());
+ text_input_client_->SetCompositionText(composition_text);
}
- // Notify accessibility about this committed composition
- text_input_client_->SetActiveCompositionForAccessibility(
- replace_text_range_, new_committed_string,
- /*is_composition_committed*/ true);
+ text_input_client_->InsertText(new_committed_string);
+ } else {
+ text_input_client_->ClearCompositionText();
}
+ // Notify accessibility about this committed composition
+ text_input_client_->SetActiveCompositionForAccessibility(
+ replace_text_range_, new_committed_string,
+ /*is_composition_committed*/ true);
}
void TSFTextStore::StartCompositionOnNewText(
@@ -1471,4 +1490,16 @@ void TSFTextStore::GetStyle(const TF_DISPLAYATTRIBUTE& attribute,
}
}
+void TSFTextStore::ResetCacheAfterEditSession() {
+ // reset the flag since we've already inserted/replaced the text.
+ new_text_inserted_ = false;
+ is_selection_interim_char_ = false;
+ // reset |on_start_composition_called_| for next edit session.
+ on_start_composition_called_ = false;
+
+ // reset string_buffer_ if composition is no longer active.
+ if (text_input_client_ && !text_input_client_->HasCompositionText())
+ string_pending_insertion_.clear();
+}
+
} // namespace ui
diff --git a/chromium/ui/base/ime/win/tsf_text_store.h b/chromium/ui/base/ime/win/tsf_text_store.h
index bf02704ffff..fd1e8a20625 100644
--- a/chromium/ui/base/ime/win/tsf_text_store.h
+++ b/chromium/ui/base/ime/win/tsf_text_store.h
@@ -102,12 +102,12 @@ class TextInputClient;
class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore
: public ITextStoreACP,
public ITfContextOwnerCompositionSink,
- public ITfLanguageProfileNotifySink,
public ITfKeyTraceEventSink,
public ITfTextEditSink {
public:
TSFTextStore();
virtual ~TSFTextStore();
+ HRESULT Initialize();
// ITextStoreACP:
IFACEMETHODIMP_(ULONG) AddRef() override;
@@ -217,10 +217,6 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore
IFACEMETHODIMP OnEndComposition(
ITfCompositionView* composition_view) override;
- // ITfLanguageProfileNotifySink:
- IFACEMETHODIMP OnLanguageChange(LANGID langid, BOOL* pfAccept) override;
- IFACEMETHODIMP OnLanguageChanged() override;
-
// ITfTextEditSink:
IFACEMETHODIMP OnEndEdit(ITfContext* context,
TfEditCookie read_only_edit_cookie,
@@ -300,6 +296,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore
size_t* committed_size,
ImeTextSpans* spans);
+ // Reset all cached flags when |TSFTextStore::RequestLock| returns.
+ void ResetCacheAfterEditSession();
+
// Gets the style information from the display attribute for the actively
// composed text.
void GetStyle(const TF_DISPLAYATTRIBUTE& attribute, ImeTextSpan* span);
diff --git a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc
index 4e81d7c1012..06ca415f763 100644
--- a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc
+++ b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc
@@ -35,7 +35,7 @@ class MockTextInputClient : public TextInputClient {
public:
~MockTextInputClient() {}
MOCK_METHOD1(SetCompositionText, void(const ui::CompositionText&));
- MOCK_METHOD1(ConfirmCompositionText, void(bool));
+ MOCK_METHOD1(ConfirmCompositionText, uint32_t(bool));
MOCK_METHOD0(ClearCompositionText, void());
MOCK_METHOD1(InsertText, void(const base::string16&));
MOCK_METHOD1(InsertChar, void(const ui::KeyEvent&));
@@ -140,6 +140,7 @@ class TSFTextStoreTest : public testing::Test {
protected:
void SetUp() override {
text_store_ = new TSFTextStore();
+ EXPECT_EQ(S_OK, text_store_->Initialize());
sink_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(),
TS_AS_ALL_SINKS));
@@ -2517,6 +2518,7 @@ class RegressionTestCallback : public TSFTextStoreTestCallback {
HRESULT LockGranted3(DWORD flags) {
GetTextTest(0, -1, L"aa", 2);
+ SetSelectionTest(2, 2, S_OK);
text_spans()->clear();
*edit_flag() = true;
@@ -2546,6 +2548,7 @@ class RegressionTestCallback : public TSFTextStoreTestCallback {
text_store_->OnKeyTraceUp(65u, 1966081u);
text_store_->OnStartComposition(nullptr, nullptr);
+ text_store_->OnEndComposition(nullptr);
return S_OK;
}
@@ -2555,6 +2558,14 @@ class RegressionTestCallback : public TSFTextStoreTestCallback {
return ui::EventDispatchDetails();
}
+ // We expect this call since the composition was started and committed during
+ // same edit session.
+ void SetCompositionText4(const ui::CompositionText& composition) {
+ EXPECT_EQ(L"c", composition.text);
+ ASSERT_EQ(1u, composition.ime_text_spans.size());
+ ASSERT_EQ(gfx::Range(1, 1), composition.selection);
+ }
+
void InsertText4(const base::string16& text) { EXPECT_EQ(L"c", text); }
HRESULT LockGranted5(DWORD flags) {
@@ -2633,6 +2644,7 @@ TEST_F(TSFTextStoreTest, RegressionTest) {
EXPECT_CALL(text_input_client_, SetCompositionText(_))
.WillOnce(Invoke(&callback, &RegressionTestCallback::SetCompositionText1))
.WillOnce(Invoke(&callback, &RegressionTestCallback::SetCompositionText2))
+ .WillOnce(Invoke(&callback, &RegressionTestCallback::SetCompositionText4))
.WillOnce(
Invoke(&callback, &RegressionTestCallback::SetCompositionText5));
@@ -3328,6 +3340,7 @@ class RegressionTest7Callback : public TSFTextStoreTestCallback {
*has_composition_range() = true;
text_store_->OnKeyTraceDown(65u, 1966081u);
+ text_store_->OnStartComposition(nullptr, nullptr);
return S_OK;
}
@@ -3481,5 +3494,239 @@ TEST_F(TSFTextStoreTest, RegressionTest8) {
EXPECT_EQ(S_OK, result);
}
+// We should use the last composition end pos instead of the cached
+// |coposition_start_| to calculate last composition.
+class RegressionTest9Callback : public TSFTextStoreTestCallback {
+ public:
+ explicit RegressionTest9Callback(TSFTextStore* text_store)
+ : TSFTextStoreTestCallback(text_store) {}
+
+ HRESULT LockGranted1(DWORD flags) {
+ SetTextTest(0, 0, L"a", S_OK);
+ SetSelectionTest(1, 1, S_OK);
+ *composition_start() = 1;
+ return S_OK;
+ }
+
+ HRESULT LockGranted2(DWORD flags) {
+ GetTextTest(0, -1, L"a", 1);
+ SetTextTest(1, 1, L"bbbb", S_OK);
+ SetSelectionTest(1, 5, S_OK);
+
+ text_spans()->clear();
+ ImeTextSpan text_span_1;
+ text_span_1.start_offset = 0;
+ text_span_1.end_offset = 4;
+ text_span_1.underline_color = SK_ColorBLACK;
+ text_span_1.thickness = ImeTextSpan::Thickness::kThin;
+ text_span_1.background_color = SK_ColorTRANSPARENT;
+ text_spans()->push_back(text_span_1);
+
+ *edit_flag() = true;
+ composition_range()->set_start(1);
+ composition_range()->set_end(5);
+ *has_composition_range() = true;
+
+ text_store_->OnKeyTraceDown(65u, 1966081u);
+ return S_OK;
+ }
+
+ void SetCompositionText2(const ui::CompositionText& composition) {
+ EXPECT_EQ(L"bbbb", composition.text);
+ EXPECT_EQ(0u, composition.selection.start());
+ EXPECT_EQ(4u, composition.selection.end());
+ ASSERT_EQ(1u, composition.ime_text_spans.size());
+ *has_composition_range() = true;
+ has_composition_text_ = true;
+ }
+
+ HRESULT LockGranted3(DWORD flags) {
+ GetTextTest(0, -1, L"abbbb", 5);
+ SetTextTest(1, 3, L"bb", S_OK);
+ SetTextTest(3, 5, L"cc", S_OK);
+
+ text_spans()->clear();
+ ImeTextSpan text_span_1;
+ text_span_1.start_offset = 0;
+ text_span_1.end_offset = 4;
+ text_span_1.underline_color = SK_ColorBLACK;
+ text_span_1.thickness = ImeTextSpan::Thickness::kThin;
+ text_span_1.background_color = SK_ColorTRANSPARENT;
+ text_spans()->push_back(text_span_1);
+
+ *edit_flag() = true;
+ composition_range()->set_start(1);
+ composition_range()->set_end(5);
+ *has_composition_range() = true;
+
+ text_store_->OnKeyTraceDown(65u, 1966081u);
+ return S_OK;
+ }
+
+ void SetCompositionText3(const ui::CompositionText& composition) {
+ EXPECT_EQ(L"bbcc", composition.text);
+ EXPECT_EQ(2u, composition.selection.start());
+ EXPECT_EQ(4u, composition.selection.end());
+ ASSERT_EQ(1u, composition.ime_text_spans.size());
+ *has_composition_range() = true;
+ has_composition_text_ = true;
+ }
+
+ HRESULT LockGranted4(DWORD flags) {
+ GetTextTest(0, -1, L"abbcc", 5);
+ SetTextTest(3, 5, L"cc", S_OK);
+ SetSelectionTest(3, 5, S_OK);
+
+ text_spans()->clear();
+ *edit_flag() = true;
+ composition_range()->set_start(0);
+ composition_range()->set_end(0);
+
+ *has_composition_range() = false;
+ *composition_start() = 3;
+ text_store_->OnKeyTraceUp(65u, 1966081u);
+ return S_OK;
+ }
+
+ void InsertText4(const base::string16& text) {
+ EXPECT_EQ(L"bbcc", text);
+ SetHasCompositionText(false);
+ }
+
+ HRESULT LockGranted5(DWORD flags) {
+ GetTextTest(0, -1, L"abbcc", 5);
+ return S_OK;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegressionTest9Callback);
+};
+
+TEST_F(TSFTextStoreTest, RegressionTest9) {
+ RegressionTest9Callback callback(text_store_.get());
+ EXPECT_CALL(text_input_client_, SetCompositionText(_))
+ .WillOnce(
+ Invoke(&callback, &RegressionTest9Callback::SetCompositionText2))
+ .WillOnce(
+ Invoke(&callback, &RegressionTest9Callback::SetCompositionText3));
+
+ EXPECT_CALL(text_input_client_, InsertText(_))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::InsertText4));
+
+ EXPECT_CALL(*sink_, OnLockGranted(_))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::LockGranted1))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::LockGranted2))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::LockGranted3))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::LockGranted4))
+ .WillOnce(Invoke(&callback, &RegressionTest9Callback::LockGranted5));
+
+ ON_CALL(text_input_client_, HasCompositionText())
+ .WillByDefault(
+ Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText));
+
+ HRESULT result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+}
+
+// Test multiple |SetText| call in one edit session.
+class MultipleSetTextCallback : public TSFTextStoreTestCallback {
+ public:
+ explicit MultipleSetTextCallback(TSFTextStore* text_store)
+ : TSFTextStoreTestCallback(text_store) {}
+
+ HRESULT LockGranted1(DWORD flags) {
+ SetTextRange(0, 6);
+ SetTextBuffer(L"123456");
+ SetTextTest(0, 0, L"123456", S_OK);
+ SetSelectionRange(6, 6);
+ *composition_start() = 1;
+ return S_OK;
+ }
+
+ HRESULT LockGranted2(DWORD flags) {
+ SetTextTest(3, 3, L"a", S_OK);
+ SetTextTest(4, 4, L"b", S_OK);
+ GetTextTest(0, -1, L"123ab456", 8);
+ SetSelectionTest(5, 5, S_OK);
+
+ *edit_flag() = true;
+ *composition_start() = 5;
+ return S_OK;
+ }
+
+ HRESULT LockGranted3(DWORD flags) {
+ SetTextTest(2, 6, L"cd", S_OK);
+ SetTextTest(5, 6, L"e", S_OK);
+ GetTextTest(0, -1, L"12cd5e", 6);
+ SetSelectionRange(6, 6);
+
+ *edit_flag() = true;
+ *composition_start() = 6;
+ return S_OK;
+ }
+
+ HRESULT LockGranted4(DWORD flags) {
+ SetTextTest(5, 6, L"fg", S_OK);
+ GetTextTest(0, -1, L"12cd5fg", 7);
+ SetTextTest(1, 3, L"h", S_OK);
+ GetTextTest(0, -1, L"1hd5fg", 6);
+ SetSelectionRange(6, 6);
+
+ *edit_flag() = true;
+ *composition_start() = 5;
+ return S_OK;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MultipleSetTextCallback);
+};
+
+TEST_F(TSFTextStoreTest, MultipleSetText) {
+ MultipleSetTextCallback callback(text_store_.get());
+ EXPECT_CALL(*sink_, OnLockGranted(_))
+ .WillOnce(Invoke(&callback, &MultipleSetTextCallback::LockGranted1))
+ .WillOnce(Invoke(&callback, &MultipleSetTextCallback::LockGranted2))
+ .WillOnce(Invoke(&callback, &MultipleSetTextCallback::LockGranted3))
+ .WillOnce(Invoke(&callback, &MultipleSetTextCallback::LockGranted4));
+
+ ON_CALL(text_input_client_, GetTextRange(_))
+ .WillByDefault(
+ Invoke(&callback, &TSFTextStoreTestCallback::GetTextRange));
+
+ ON_CALL(text_input_client_, GetTextFromRange(_, _))
+ .WillByDefault(
+ Invoke(&callback, &TSFTextStoreTestCallback::GetTextFromRange));
+
+ ON_CALL(text_input_client_, GetEditableSelectionRange(_))
+ .WillByDefault(Invoke(
+ &callback, &TSFTextStoreTestCallback::GetEditableSelectionRange));
+
+ HRESULT result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+ result = kInvalidResult;
+ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+ EXPECT_EQ(S_OK, result);
+}
+
} // namespace
} // namespace ui
diff --git a/chromium/ui/base/l10n/l10n_util.cc b/chromium/ui/base/l10n/l10n_util.cc
index 29deb4da711..fdfc14ef542 100644
--- a/chromium/ui/base/l10n/l10n_util.cc
+++ b/chromium/ui/base/l10n/l10n_util.cc
@@ -52,40 +52,41 @@
namespace {
static const char* const kAcceptLanguageList[] = {
- "af", // Afrikaans
- "am", // Amharic
- "an", // Aragonese
- "ar", // Arabic
- "ast", // Asturian
- "az", // Azerbaijani
- "be", // Belarusian
- "bg", // Bulgarian
- "bh", // Bihari
- "bn", // Bengali
- "br", // Breton
- "bs", // Bosnian
- "ca", // Catalan
- "ceb", // Cebuano
- "ckb", // Kurdish (Arabci), Sorani
- "co", // Corsican
- "cs", // Czech
- "cy", // Welsh
- "da", // Danish
- "de", // German
- "de-AT", // German (Austria)
- "de-CH", // German (Switzerland)
- "de-DE", // German (Germany)
- "de-LI", // German (Liechtenstein)
- "el", // Greek
- "en", // English
- "en-AU", // English (Australia)
- "en-CA", // English (Canada)
- "en-GB", // English (UK)
- "en-IN", // English (India)
- "en-NZ", // English (New Zealand)
- "en-US", // English (US)
- "en-ZA", // English (South Africa)
- "eo", // Esperanto
+ "af", // Afrikaans
+ "am", // Amharic
+ "an", // Aragonese
+ "ar", // Arabic
+ "ast", // Asturian
+ "az", // Azerbaijani
+ "be", // Belarusian
+ "bg", // Bulgarian
+ "bh", // Bihari
+ "bn", // Bengali
+ "br", // Breton
+ "bs", // Bosnian
+ "ca", // Catalan
+ "ceb", // Cebuano
+ "ckb", // Kurdish (Arabic), Sorani
+ "co", // Corsican
+ "cs", // Czech
+ "cy", // Welsh
+ "da", // Danish
+ "de", // German
+ "de-AT", // German (Austria)
+ "de-CH", // German (Switzerland)
+ "de-DE", // German (Germany)
+ "de-LI", // German (Liechtenstein)
+ "el", // Greek
+ "en", // English
+ "en-AU", // English (Australia)
+ "en-CA", // English (Canada)
+ "en-GB", // English (UK)
+ "en-GB-oxendict", // English (UK, OED spelling)
+ "en-IN", // English (India)
+ "en-NZ", // English (New Zealand)
+ "en-US", // English (US)
+ "en-ZA", // English (South Africa)
+ "eo", // Esperanto
// TODO(jungshik) : Do we want to list all es-Foo for Latin-American
// Spanish speaking countries?
"es", // Spanish
@@ -257,7 +258,7 @@ bool IsLocalePartiallyPopulated(const std::string& locale_name) {
return !l10n_util::IsLocaleNameTranslated("en", locale_name);
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
bool IsLocaleAvailable(const std::string& locale) {
// If locale has any illegal characters in it, we don't want to try to
// load it because it may be pointing outside the locale data file directory.
@@ -282,7 +283,7 @@ bool IsLocaleAvailable(const std::string& locale) {
// if "foo bar" is RTL. So this function prepends the necessary RLM in such
// cases.
void AdjustParagraphDirectionality(base::string16* paragraph) {
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_APPLE) && !defined(OS_ANDROID)
if (base::i18n::IsRTL() &&
base::i18n::StringContainsStrongRTLChars(*paragraph)) {
paragraph->insert(0, 1,
@@ -343,7 +344,7 @@ std::string GetLanguage(const std::string& locale) {
// and generic locale fallback based on ICU/CLDR.
bool CheckAndResolveLocale(const std::string& locale,
std::string* resolved_locale) {
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
if (IsLocaleAvailable(locale)) {
*resolved_locale = locale;
return true;
@@ -383,18 +384,15 @@ bool CheckAndResolveLocale(const std::string& locale,
tmp_locale.append("-CN");
}
} else if (base::LowerCaseEqualsASCII(lang, "en")) {
- // Map Australian, Canadian, Indian, New Zealand and South African
- // English to British English for now.
+ // Map Liberian and Filipino English to US English, and everything
+ // else to British English.
// TODO(jungshik): en-CA may have to change sides once
// we have OS locale separate from app locale (Chrome's UI language).
- if (base::LowerCaseEqualsASCII(region, "au") ||
- base::LowerCaseEqualsASCII(region, "ca") ||
- base::LowerCaseEqualsASCII(region, "in") ||
- base::LowerCaseEqualsASCII(region, "nz") ||
- base::LowerCaseEqualsASCII(region, "za")) {
- tmp_locale.append("-GB");
- } else {
+ if (base::LowerCaseEqualsASCII(region, "lr") ||
+ base::LowerCaseEqualsASCII(region, "ph")) {
tmp_locale.append("-US");
+ } else {
+ tmp_locale.append("-GB");
}
}
if (IsLocaleAvailable(tmp_locale)) {
@@ -423,12 +421,12 @@ bool CheckAndResolveLocale(const std::string& locale,
}
#else
NOTIMPLEMENTED();
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
return false;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
std::string GetApplicationLocaleInternalMac(const std::string& pref_locale) {
// Use any override (Cocoa for the browser), otherwise use the preference
// passed to the function.
@@ -445,7 +443,7 @@ std::string GetApplicationLocaleInternalMac(const std::string& pref_locale) {
}
#endif
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
std::string GetApplicationLocaleInternalNonMac(const std::string& pref_locale) {
std::string resolved_locale;
std::vector<std::string> candidates;
@@ -512,10 +510,10 @@ std::string GetApplicationLocaleInternalNonMac(const std::string& pref_locale) {
return std::string();
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
std::string GetApplicationLocaleInternal(const std::string& pref_locale) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return GetApplicationLocaleInternalMac(pref_locale);
#else
return GetApplicationLocaleInternalNonMac(pref_locale);
diff --git a/chromium/ui/base/l10n/l10n_util.h b/chromium/ui/base/l10n/l10n_util.h
index 6a9af307289..124248defa8 100644
--- a/chromium/ui/base/l10n/l10n_util.h
+++ b/chromium/ui/base/l10n/l10n_util.h
@@ -18,9 +18,9 @@
#include "base/strings/string16.h"
#include "build/build_config.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/l10n/l10n_util_mac.h"
-#endif // OS_MACOSX
+#endif // OS_APPLE
namespace l10n_util {
diff --git a/chromium/ui/base/l10n/l10n_util_unittest.cc b/chromium/ui/base/l10n/l10n_util_unittest.cc
index cb652df1649..4c2fd2a2b1b 100644
--- a/chromium/ui/base/l10n/l10n_util_unittest.cc
+++ b/chromium/ui/base/l10n/l10n_util_unittest.cc
@@ -27,7 +27,7 @@
#include "ui/base/l10n/l10n_util_collator.h"
#include "ui/base/ui_base_paths.h"
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_POSIX) && !defined(OS_APPLE)
#include <cstdlib>
#endif
@@ -68,7 +68,7 @@ TEST_F(L10nUtilTest, GetString) {
EXPECT_EQ(UTF8ToUTF16("You owe me $$1."), s16);
}
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if !defined(OS_APPLE) && !defined(OS_ANDROID)
// On Mac, we are disabling this test because GetApplicationLocale() as an
// API isn't something that we'll easily be able to unit test in this manner.
// The meaning of that API, on the Mac, is "the locale used by Cocoa's main
@@ -380,7 +380,7 @@ TEST_F(L10nUtilTest, GetAppLocale) {
// Clean up.
base::i18n::SetICUDefaultLocale(original_locale);
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
TEST_F(L10nUtilTest, SortStringsUsingFunction) {
std::vector<std::unique_ptr<StringWrapper>> strings;
diff --git a/chromium/ui/base/l10n/time_format.cc b/chromium/ui/base/l10n/time_format.cc
index 25b4336923f..2e51714bc35 100644
--- a/chromium/ui/base/l10n/time_format.cc
+++ b/chromium/ui/base/l10n/time_format.cc
@@ -10,6 +10,7 @@
#include "base/component_export.h"
#include "base/lazy_instance.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "third_party/icu/source/common/unicode/unistr.h"
@@ -55,86 +56,87 @@ base::string16 TimeFormat::DetailedWithMonthAndYear(
int cutoff,
const base::TimeDelta& delta,
bool with_month_and_year) {
- if (delta < TimeDelta::FromSeconds(0)) {
- NOTREACHED() << "Negative duration";
- return base::string16();
- }
+ DCHECK_GE(delta, TimeDelta());
// Negative cutoff: always use two-value format.
if (cutoff < 0)
cutoff = std::numeric_limits<int>::max();
- const TimeDelta one_minute(TimeDelta::FromMinutes(1));
- const TimeDelta one_hour(TimeDelta::FromHours(1));
- const TimeDelta one_day(TimeDelta::FromDays(1));
-
- // An average month is a twelfth of a year.
- const TimeDelta one_month(TimeDelta::FromDays(365) / 12);
+ constexpr TimeDelta kMinute = TimeDelta::FromMinutes(1);
+ constexpr TimeDelta kHour = TimeDelta::FromHours(1);
+ constexpr TimeDelta kDay = TimeDelta::FromDays(1);
// Simplify one year to be 365 days.
- const TimeDelta one_year(TimeDelta::FromDays(365));
+ constexpr TimeDelta kYear = 365 * kDay;
- const TimeDelta half_second(TimeDelta::FromMilliseconds(500));
- const TimeDelta half_minute(TimeDelta::FromSeconds(30));
- const TimeDelta half_hour(TimeDelta::FromMinutes(30));
- const TimeDelta half_day(TimeDelta::FromHours(12));
+ // An average month is a twelfth of a year.
+ constexpr TimeDelta kMonth = kYear / 12;
+
+ constexpr TimeDelta kHalfSecond = TimeDelta::FromSeconds(1) / 2;
+ constexpr TimeDelta kHalfMinute = kMinute / 2;
+ constexpr TimeDelta kHalfHour = kHour / 2;
+ constexpr TimeDelta kHalfDay = kDay / 2;
// Rationale: Start by determining major (first) unit, then add minor (second)
// unit if mandated by |cutoff|.
icu::UnicodeString time_string;
const Formatter* formatter = g_container.Get().Get(format, length);
- if (delta < one_minute - half_second) {
+ if (delta < kMinute - kHalfSecond) {
// Anything up to 59.500 seconds is formatted as seconds.
- const int seconds = static_cast<int>((delta + half_second).InSeconds());
+ const int seconds = base::ClampRound(delta.InSecondsF());
formatter->Format(Formatter::UNIT_SEC, seconds, &time_string);
-
- } else if (delta < one_hour - (cutoff < 60 ? half_minute : half_second)) {
+ } else if (delta < kHour - (cutoff < base::Time::kMinutesPerHour
+ ? kHalfMinute
+ : kHalfSecond)) {
// Anything up to 59.5 minutes (respectively 59:59.500 when |cutoff| permits
// two-value output) is formatted as minutes (respectively minutes and
// seconds).
- if (delta >= cutoff * one_minute - half_second) {
- const int minutes = (delta + half_minute).InMinutes();
+ if (delta >= cutoff * kMinute - kHalfSecond) {
+ const int minutes = (delta + kHalfMinute).InMinutes();
formatter->Format(Formatter::UNIT_MIN, minutes, &time_string);
} else {
- const int minutes = (delta + half_second).InMinutes();
- const int seconds = static_cast<int>(
- (delta + half_second).InSeconds() % 60);
+ const int minutes = (delta + kHalfSecond).InMinutes();
+ const int seconds =
+ base::ClampRound(delta.InSecondsF()) % base::Time::kSecondsPerMinute;
formatter->Format(Formatter::TWO_UNITS_MIN_SEC,
minutes, seconds, &time_string);
}
-
- } else if (delta < one_day - (cutoff < 24 ? half_hour : half_minute)) {
+ } else if (delta < kDay - (cutoff < base::Time::kHoursPerDay ? kHalfHour
+ : kHalfMinute)) {
// Anything up to 23.5 hours (respectively 23:59:30.000 when |cutoff|
// permits two-value output) is formatted as hours (respectively hours and
// minutes).
- if (delta >= cutoff * one_hour - half_minute) {
- const int hours = (delta + half_hour).InHours();
+ if (delta >= cutoff * kHour - kHalfMinute) {
+ const int hours = (delta + kHalfHour).InHours();
formatter->Format(Formatter::UNIT_HOUR, hours, &time_string);
} else {
- const int hours = (delta + half_minute).InHours();
- const int minutes = (delta + half_minute).InMinutes() % 60;
+ const int hours = (delta + kHalfMinute).InHours();
+ const int minutes =
+ (delta + kHalfMinute).InMinutes() % base::Time::kMinutesPerHour;
formatter->Format(Formatter::TWO_UNITS_HOUR_MIN,
hours, minutes, &time_string);
}
- } else if (!with_month_and_year || delta < one_month) {
+ } else if (!with_month_and_year || delta < kMonth) {
// Anything bigger is formatted as days (respectively days and hours).
- if (delta >= cutoff * one_day - half_hour) {
- const int days = (delta + half_day).InDays();
+ if (delta >= cutoff * kDay - kHalfHour) {
+ const int days = (delta + kHalfDay).InDays();
formatter->Format(Formatter::UNIT_DAY, days, &time_string);
} else {
- const int days = (delta + half_hour).InDays();
- const int hours = (delta + half_hour).InHours() % 24;
- formatter->Format(Formatter::TWO_UNITS_DAY_HOUR,
- days, hours, &time_string);
+ const int days = (delta + kHalfHour).InDays();
+ const int hours =
+ (delta + kHalfHour).InHours() % base::Time::kHoursPerDay;
+ formatter->Format(Formatter::TWO_UNITS_DAY_HOUR, days, hours,
+ &time_string);
}
- } else if (delta < one_year) {
+ } else if (delta < kYear) {
DCHECK(with_month_and_year);
- int month = delta / one_month;
- DCHECK(month >= 1 && month <= 12);
+ const int month = base::ClampFloor(delta / kMonth);
+ DCHECK_GE(month, 1);
+ DCHECK_LE(month, 12);
formatter->Format(Formatter::UNIT_MONTH, month, &time_string);
} else {
DCHECK(with_month_and_year);
- int year = delta / one_year;
+ const int year = base::ClampFloor(delta / kYear);
formatter->Format(Formatter::UNIT_YEAR, year, &time_string);
}
@@ -152,19 +154,19 @@ base::string16 TimeFormat::DetailedWithMonthAndYear(
base::string16 TimeFormat::RelativeDate(
const base::Time& time,
const base::Time* optional_midnight_today) {
- base::Time midnight_today = optional_midnight_today
- ? *optional_midnight_today
- : base::Time::Now().LocalMidnight();
- TimeDelta day = TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerDay);
- base::Time tomorrow = midnight_today + day;
- base::Time yesterday = midnight_today - day;
+ const base::Time midnight_today = optional_midnight_today
+ ? *optional_midnight_today
+ : base::Time::Now().LocalMidnight();
+ constexpr TimeDelta kDay = TimeDelta::FromDays(1);
+ const base::Time tomorrow = midnight_today + kDay;
+ const base::Time yesterday = midnight_today - kDay;
if (time >= tomorrow)
return base::string16();
- else if (time >= midnight_today)
+ if (time >= midnight_today)
return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY);
- else if (time >= yesterday)
- return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY);
- return base::string16();
+ return (time >= yesterday)
+ ? l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY)
+ : base::string16();
}
} // namespace ui
diff --git a/chromium/ui/base/models/dialog_model.cc b/chromium/ui/base/models/dialog_model.cc
new file mode 100644
index 00000000000..1d6e6d6adfb
--- /dev/null
+++ b/chromium/ui/base/models/dialog_model.cc
@@ -0,0 +1,191 @@
+// 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 "ui/base/models/dialog_model.h"
+
+#include "base/bind_helpers.h"
+
+namespace ui {
+
+DialogModel::Builder::Builder(std::unique_ptr<DialogModelDelegate> delegate)
+ : model_(std::make_unique<DialogModel>(util::PassKey<Builder>(),
+ std::move(delegate))) {}
+DialogModel::Builder::~Builder() {
+ DCHECK(!model_) << "Model should've been built.";
+}
+
+std::unique_ptr<DialogModel> DialogModel::Builder::Build() {
+ DCHECK(model_);
+ return std::move(model_);
+}
+
+DialogModel::Builder& DialogModel::Builder::SetShowCloseButton(
+ bool show_close_button) {
+ model_->show_close_button_ = show_close_button;
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::SetTitle(base::string16 title) {
+ model_->title_ = std::move(title);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::SetCloseCallback(
+ base::OnceClosure callback) {
+ model_->close_callback_ = std::move(callback);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::SetWindowClosingCallback(
+ base::OnceClosure callback) {
+ model_->window_closing_callback_ = std::move(callback);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddOkButton(
+ base::OnceClosure callback,
+ base::string16 label,
+ const DialogModelButton::Params& params) {
+ DCHECK(!model_->accept_callback_);
+ model_->accept_callback_ = std::move(callback);
+ // NOTREACHED() is used below to make sure this callback isn't used.
+ // DialogModelHost should be using OnDialogAccepted() instead.
+ model_->ok_button_.emplace(
+ model_->GetPassKey(), model_.get(),
+ base::BindRepeating([](const Event&) { NOTREACHED(); }), std::move(label),
+ params);
+
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddCancelButton(
+ base::OnceClosure callback,
+ base::string16 label,
+ const DialogModelButton::Params& params) {
+ DCHECK(!model_->cancel_callback_);
+ model_->cancel_callback_ = std::move(callback);
+ // NOTREACHED() is used below to make sure this callback isn't used.
+ // DialogModelHost should be using OnDialogCanceled() instead.
+ model_->cancel_button_.emplace(
+ model_->GetPassKey(), model_.get(),
+ base::BindRepeating([](const Event&) { NOTREACHED(); }), std::move(label),
+ params);
+
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddDialogExtraButton(
+ base::RepeatingCallback<void(const Event&)> callback,
+ base::string16 label,
+ const DialogModelButton::Params& params) {
+ model_->extra_button_.emplace(model_->GetPassKey(), model_.get(),
+ std::move(callback), std::move(label), params);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddBodyText(
+ base::string16 text,
+ const DialogModelBodyText::Params& params) {
+ model_->AddBodyText(std::move(text), params);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddCombobox(
+ base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const DialogModelCombobox::Params& params) {
+ model_->AddCombobox(std::move(label), std::move(combobox_model), params);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::AddTextfield(
+ base::string16 label,
+ base::string16 text,
+ const DialogModelTextfield::Params& params) {
+ model_->AddTextfield(std::move(label), std::move(text), params);
+ return *this;
+}
+
+DialogModel::Builder& DialogModel::Builder::SetInitiallyFocusedField(
+ int unique_id) {
+ // This must be called with unique_id >= 0 (-1 is "no ID").
+ DCHECK_GE(unique_id, 0);
+ // This can only be called once.
+ DCHECK(!model_->initially_focused_field_);
+ model_->initially_focused_field_ = unique_id;
+ return *this;
+}
+
+DialogModel::DialogModel(util::PassKey<Builder>,
+ std::unique_ptr<DialogModelDelegate> delegate)
+ : delegate_(std::move(delegate)) {
+ delegate_->set_dialog_model(this);
+}
+
+DialogModel::~DialogModel() = default;
+
+void DialogModel::AddBodyText(base::string16 text,
+ const DialogModelBodyText::Params& params) {
+ AddField(std::make_unique<DialogModelBodyText>(GetPassKey(), this,
+ std::move(text), params));
+}
+
+void DialogModel::AddCombobox(base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const DialogModelCombobox::Params& params) {
+ AddField(std::make_unique<DialogModelCombobox>(
+ GetPassKey(), this, std::move(label), std::move(combobox_model), params));
+}
+
+void DialogModel::AddTextfield(base::string16 label,
+ base::string16 text,
+ const DialogModelTextfield::Params& params) {
+ AddField(std::make_unique<DialogModelTextfield>(
+ GetPassKey(), this, std::move(label), std::move(text), params));
+}
+
+DialogModelField* DialogModel::GetFieldByUniqueId(int unique_id) {
+ for (auto& field : fields_) {
+ if (field->unique_id_ == unique_id)
+ return field.get();
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+DialogModelCombobox* DialogModel::GetComboboxByUniqueId(int unique_id) {
+ return GetFieldByUniqueId(unique_id)->AsCombobox();
+}
+
+DialogModelTextfield* DialogModel::GetTextfieldByUniqueId(int unique_id) {
+ return GetFieldByUniqueId(unique_id)->AsTextfield();
+}
+
+void DialogModel::OnDialogAccepted(util::PassKey<DialogModelHost>) {
+ if (accept_callback_)
+ std::move(accept_callback_).Run();
+}
+
+void DialogModel::OnDialogCancelled(util::PassKey<DialogModelHost>) {
+ if (cancel_callback_)
+ std::move(cancel_callback_).Run();
+}
+
+void DialogModel::OnDialogClosed(util::PassKey<DialogModelHost>) {
+ if (close_callback_)
+ std::move(close_callback_).Run();
+}
+
+void DialogModel::OnWindowClosing(util::PassKey<DialogModelHost>) {
+ if (window_closing_callback_)
+ std::move(window_closing_callback_).Run();
+}
+
+void DialogModel::AddField(std::unique_ptr<DialogModelField> field) {
+ fields_.push_back(std::move(field));
+ if (host_)
+ host_->OnFieldAdded(fields_.back().get());
+}
+
+} // namespace ui \ No newline at end of file
diff --git a/chromium/ui/base/models/dialog_model.h b/chromium/ui/base/models/dialog_model.h
new file mode 100644
index 00000000000..4b61946f886
--- /dev/null
+++ b/chromium/ui/base/models/dialog_model.h
@@ -0,0 +1,254 @@
+// 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 UI_BASE_MODELS_DIALOG_MODEL_H_
+#define UI_BASE_MODELS_DIALOG_MODEL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+#include "base/strings/string16.h"
+#include "base/util/type_safety/pass_key.h"
+#include "ui/base/models/dialog_model_field.h"
+#include "ui/base/models/dialog_model_host.h"
+#include "ui/base/ui_base_types.h"
+
+namespace ui {
+
+class ComboboxModel;
+
+// Base class for a Delegate associated with (owned by) a model. Provides a link
+// from the delegate back to the model it belongs to (through ::dialog_model()),
+// from which fields and the DialogModelHost can be accessed.
+class COMPONENT_EXPORT(UI_BASE) DialogModelDelegate {
+ public:
+ DialogModelDelegate() = default;
+ DialogModelDelegate(const DialogModelDelegate&) = delete;
+ DialogModelDelegate& operator=(const DialogModelDelegate&) = delete;
+ virtual ~DialogModelDelegate() = default;
+
+ DialogModel* dialog_model() { return dialog_model_; }
+
+ private:
+ friend class DialogModel;
+ void set_dialog_model(DialogModel* model) { dialog_model_ = model; }
+
+ DialogModel* dialog_model_ = nullptr;
+};
+
+// DialogModel represents a platform-and-toolkit agnostic data + behavior
+// portion of a dialog. This contains the semantics of a dialog, whereas
+// DialogModelHost implementations (like views::BubbleDialogModelHost) are
+// responsible for interfacing with toolkits to display them. This provides a
+// separation of concerns where a DialogModel only needs to be concerned with
+// what goes into a dialog, not how it shows.
+//
+// Example usage (with views as an example DialogModelHost implementation). Note
+// that visual presentation (except order of elements) is entirely up to
+// DialogModelHost, and separate from client code:
+//
+// constexpr int kNameTextfield = 1;
+// class Delegate : public ui::DialogModelDelegate {
+// public:
+// void OnDialogAccepted() {
+// LOG(ERROR) << "Hello "
+// << dialog_model()->GetTextfield(kNameTextfield)->text();
+// }
+// };
+// auto model_delegate = std::make_unique<Delegate>();
+// auto* model_delegate_ptr = model_delegate.get();
+//
+// auto dialog_model =
+// ui::DialogModel::Builder(std::move(model_delegate))
+// .SetTitle(base::ASCIIToUTF16("Hello, world!"))
+// .AddOkButton(base::BindOnce(&Delegate::OnDialogAccepted,
+// base::Unretained(model_delegate_ptr)))
+// .AddTextfield(
+// base::ASCIIToUTF16("Name"), base::string16(),
+// ui::DialogModelTextfield::Params().SetUniqueId(kNameTextfield))
+// .Build();
+//
+// // DialogModelBase::Host specific. In this example, uses views-specific
+// // code to set a view as an anchor.
+// auto bubble =
+// std::make_unique<views::BubbleDialogModelHost>(std::move(dialog_model));
+// bubble->SetAnchorView(anchor_view);
+// views::Widget* const widget =
+// views::BubbleDialogDelegateView::CreateBubble(bubble.release());
+// widget->Show();
+class COMPONENT_EXPORT(UI_BASE) DialogModel final {
+ public:
+ // Builder for DialogModel. Used for properties that are either only or
+ // commonly const after construction.
+ class COMPONENT_EXPORT(UI_BASE) Builder {
+ public:
+ explicit Builder(std::unique_ptr<DialogModelDelegate> delegate);
+ ~Builder();
+
+ std::unique_ptr<DialogModel> Build() WARN_UNUSED_RESULT;
+
+ Builder& SetShowCloseButton(bool show_close_button);
+ Builder& SetTitle(base::string16 title);
+
+ // Called when the dialog is explicitly closed (Esc, close-x). Not called
+ // during accept/cancel.
+ Builder& SetCloseCallback(base::OnceClosure callback);
+
+ // TODO(pbos): Clarify and enforce (through tests) that this is called after
+ // {accept,cancel,close} callbacks.
+ // Unconditionally called when the dialog closes. Called on top of
+ // {accept,cancel,close} callbacks.
+ Builder& SetWindowClosingCallback(base::OnceClosure callback);
+
+ // Adds a dialog button (ok, cancel) to the dialog. The |callback| is called
+ // when the dialog is accepted or cancelled, before it closes. Use
+ // base::DoNothing() as callback if you want nothing extra to happen as a
+ // result, besides the dialog closing.
+ // If no |label| is provided, default strings are chosen by the
+ // DialogModelHost implementation.
+ Builder& AddOkButton(
+ base::OnceClosure callback,
+ base::string16 label = base::string16(),
+ const DialogModelButton::Params& params = DialogModelButton::Params());
+ Builder& AddCancelButton(
+ base::OnceClosure callback,
+ base::string16 label = base::string16(),
+ const DialogModelButton::Params& params = DialogModelButton::Params());
+
+ // Use of the extra button in new dialogs are discouraged. If this is deemed
+ // necessary please double-check with UX before adding any new dialogs with
+ // them.
+ Builder& AddDialogExtraButton(
+ base::RepeatingCallback<void(const Event&)> callback,
+ base::string16 label,
+ const DialogModelButton::Params& params);
+
+ // Adds a textfield. See DialogModel::AddTextfield().
+ Builder& AddTextfield(base::string16 label,
+ base::string16 text,
+ const ui::DialogModelTextfield::Params& params);
+
+ // Adds a combobox. See DialogModel::AddCombobox().
+ Builder& AddCombobox(base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const DialogModelCombobox::Params& params);
+
+ // Adds body text. See DialogModel::AddBodyText().
+ Builder& AddBodyText(base::string16 label,
+ const DialogModelBodyText::Params& params);
+
+ // Sets which field should be initially focused in the dialog model. Must be
+ // called after that field has been added. Can only be called once.
+ Builder& SetInitiallyFocusedField(int unique_id);
+
+ private:
+ std::unique_ptr<DialogModel> model_;
+ };
+
+ DialogModel(util::PassKey<DialogModel::Builder>,
+ std::unique_ptr<DialogModelDelegate> delegate);
+ ~DialogModel();
+
+ // The host in which this model is hosted. Set by the Host implementation
+ // during Host construction where it takes ownership of |this|.
+ DialogModelHost* host() { return host_; }
+
+ // Adds body text at the end of the dialog model.
+ void AddBodyText(base::string16 label,
+ const DialogModelBodyText::Params& params);
+
+ // Adds a labeled combobox (label: [model]) at the end of the dialog model.
+ void AddCombobox(base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const DialogModelCombobox::Params& params);
+
+ // Adds a labeled textfield (label: [text]) at the end of the dialog model.
+ void AddTextfield(base::string16 label,
+ base::string16 text,
+ const ui::DialogModelTextfield::Params& params);
+
+ // Gets DialogModelFields from their unique identifier. |unique_id| is
+ // supplied to the ::Params class during construction.
+ DialogModelField* GetFieldByUniqueId(int unique_id);
+ DialogModelCombobox* GetComboboxByUniqueId(int unique_id);
+ DialogModelTextfield* GetTextfieldByUniqueId(int unique_id);
+
+ // Methods with util::PassKey<DialogModelHost> are only intended to be called
+ // by the DialogModelHost implementation.
+ void OnDialogAccepted(util::PassKey<DialogModelHost>);
+ void OnDialogCancelled(util::PassKey<DialogModelHost>);
+ void OnDialogClosed(util::PassKey<DialogModelHost>);
+ void OnWindowClosing(util::PassKey<DialogModelHost>);
+
+ // Called when added to a DialogModelHost.
+ void set_host(util::PassKey<DialogModelHost>, DialogModelHost* host) {
+ host_ = host;
+ }
+
+ bool show_close_button(util::PassKey<DialogModelHost>) const {
+ return show_close_button_;
+ }
+
+ const base::string16& title(util::PassKey<DialogModelHost>) const {
+ return title_;
+ }
+
+ base::Optional<int> initially_focused_field(
+ util::PassKey<DialogModelHost>) const {
+ return initially_focused_field_;
+ }
+
+ DialogModelButton* ok_button(util::PassKey<DialogModelHost>) {
+ return ok_button_.has_value() ? &ok_button_.value() : nullptr;
+ }
+
+ DialogModelButton* cancel_button(util::PassKey<DialogModelHost>) {
+ return cancel_button_.has_value() ? &cancel_button_.value() : nullptr;
+ }
+
+ DialogModelButton* extra_button(util::PassKey<DialogModelHost>) {
+ return extra_button_.has_value() ? &extra_button_.value() : nullptr;
+ }
+
+ // Accessor for ordered fields in the model. This includes DialogButtons even
+ // though they should be handled separately (OK button has fixed position in
+ // dialog).
+ const std::vector<std::unique_ptr<DialogModelField>>& fields(
+ util::PassKey<DialogModelHost>) {
+ return fields_;
+ }
+
+ private:
+ util::PassKey<DialogModel> GetPassKey() {
+ return util::PassKey<DialogModel>();
+ }
+
+ void AddField(std::unique_ptr<DialogModelField> field);
+
+ std::unique_ptr<DialogModelDelegate> delegate_;
+ DialogModelHost* host_ = nullptr;
+
+ bool show_close_button_ = false;
+ base::string16 title_;
+
+ static constexpr int kExtraButtonId = DIALOG_BUTTON_LAST + 1;
+ std::vector<std::unique_ptr<DialogModelField>> fields_;
+ base::Optional<int> initially_focused_field_;
+
+ base::Optional<DialogModelButton> ok_button_;
+ base::Optional<DialogModelButton> cancel_button_;
+ base::Optional<DialogModelButton> extra_button_;
+
+ base::OnceClosure accept_callback_;
+ base::OnceClosure cancel_callback_;
+ base::OnceClosure close_callback_;
+
+ base::OnceClosure window_closing_callback_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_MODELS_DIALOG_MODEL_H_ \ No newline at end of file
diff --git a/chromium/ui/base/models/dialog_model_field.cc b/chromium/ui/base/models/dialog_model_field.cc
new file mode 100644
index 00000000000..9294525f360
--- /dev/null
+++ b/chromium/ui/base/models/dialog_model_field.cc
@@ -0,0 +1,223 @@
+// 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 "ui/base/models/dialog_model_field.h"
+#include "ui/base/models/dialog_model.h"
+
+namespace ui {
+
+DialogModelField::DialogModelField(util::PassKey<DialogModel>,
+ DialogModel* model,
+ Type type,
+ int unique_id,
+ base::flat_set<Accelerator> accelerators)
+ : model_(model),
+ type_(type),
+ unique_id_(unique_id),
+ accelerators_(std::move(accelerators)) {
+ // TODO(pbos): Assert that unique_id_ is unique.
+}
+
+DialogModelField::~DialogModelField() = default;
+
+DialogModelButton* DialogModelField::AsButton(util::PassKey<DialogModelHost>) {
+ return AsButton();
+}
+
+DialogModelBodyText* DialogModelField::AsBodyText(
+ util::PassKey<DialogModelHost>) {
+ return AsBodyText();
+}
+
+DialogModelCombobox* DialogModelField::AsCombobox(
+ util::PassKey<DialogModelHost>) {
+ return AsCombobox();
+}
+
+DialogModelTextfield* DialogModelField::AsTextfield(
+ util::PassKey<DialogModelHost>) {
+ return AsTextfield();
+}
+
+DialogModelButton* DialogModelField::AsButton() {
+ DCHECK_EQ(type_, kButton);
+ return static_cast<DialogModelButton*>(this);
+}
+
+DialogModelBodyText* DialogModelField::AsBodyText() {
+ DCHECK_EQ(type_, kBodyText);
+ return static_cast<DialogModelBodyText*>(this);
+}
+
+DialogModelCombobox* DialogModelField::AsCombobox() {
+ DCHECK_EQ(type_, kCombobox);
+ return static_cast<DialogModelCombobox*>(this);
+}
+
+DialogModelTextfield* DialogModelField::AsTextfield() {
+ DCHECK_EQ(type_, kTextfield);
+ return static_cast<DialogModelTextfield*>(this);
+}
+
+DialogModelButton::Params::Params() = default;
+DialogModelButton::Params::~Params() = default;
+
+DialogModelButton::Params& DialogModelButton::Params::SetUniqueId(
+ int unique_id) {
+ DCHECK_GE(unique_id, 0);
+ unique_id_ = unique_id;
+ return *this;
+}
+
+DialogModelButton::Params& DialogModelButton::Params::AddAccelerator(
+ Accelerator accelerator) {
+ accelerators_.insert(std::move(accelerator));
+ return *this;
+}
+
+DialogModelButton::DialogModelButton(
+ util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::RepeatingCallback<void(const Event&)> callback,
+ base::string16 label,
+ const DialogModelButton::Params& params)
+ : DialogModelField(pass_key,
+ model,
+ kButton,
+ params.unique_id_,
+ params.accelerators_),
+ label_(std::move(label)),
+ callback_(std::move(callback)) {
+ DCHECK(callback_);
+}
+
+DialogModelButton::~DialogModelButton() = default;
+
+void DialogModelButton::OnPressed(util::PassKey<DialogModelHost>,
+ const Event& event) {
+ callback_.Run(event);
+}
+
+DialogModelBodyText::Params& DialogModelBodyText::Params::SetIsSecondary() {
+ is_secondary_ = true;
+ return *this;
+}
+
+DialogModelBodyText::DialogModelBodyText(
+ util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 text,
+ const DialogModelBodyText::Params& params)
+ : DialogModelField(pass_key,
+ model,
+ kBodyText,
+ -1,
+ base::flat_set<Accelerator>()),
+ text_(std::move(text)),
+ is_secondary_(params.is_secondary_) {}
+
+DialogModelBodyText::~DialogModelBodyText() = default;
+
+DialogModelCombobox::Params::Params() = default;
+DialogModelCombobox::Params::~Params() = default;
+
+DialogModelCombobox::Params& DialogModelCombobox::Params::SetUniqueId(
+ int unique_id) {
+ DCHECK_GE(unique_id, 0);
+ unique_id_ = unique_id;
+ return *this;
+}
+
+DialogModelCombobox::Params& DialogModelCombobox::Params::SetCallback(
+ base::RepeatingClosure callback) {
+ callback_ = std::move(callback);
+ return *this;
+}
+
+DialogModelCombobox::Params& DialogModelCombobox::Params::AddAccelerator(
+ Accelerator accelerator) {
+ accelerators_.insert(std::move(accelerator));
+ return *this;
+}
+
+DialogModelCombobox::Params& DialogModelCombobox::Params::SetAccessibleName(
+ base::string16 accessible_name) {
+ accessible_name_ = std::move(accessible_name);
+ return *this;
+}
+
+DialogModelCombobox::DialogModelCombobox(
+ util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const DialogModelCombobox::Params& params)
+ : DialogModelField(pass_key,
+ model,
+ kCombobox,
+ params.unique_id_,
+ params.accelerators_),
+ label_(std::move(label)),
+ accessible_name_(params.accessible_name_),
+ selected_index_(combobox_model->GetDefaultIndex()),
+ combobox_model_(std::move(combobox_model)),
+ callback_(params.callback_) {}
+
+DialogModelCombobox::~DialogModelCombobox() = default;
+
+void DialogModelCombobox::OnSelectedIndexChanged(util::PassKey<DialogModelHost>,
+ int selected_index) {
+ selected_index_ = selected_index;
+}
+
+void DialogModelCombobox::OnPerformAction(util::PassKey<DialogModelHost>) {
+ if (callback_)
+ callback_.Run();
+}
+
+DialogModelTextfield::Params::Params() = default;
+DialogModelTextfield::Params::~Params() = default;
+
+DialogModelTextfield::Params& DialogModelTextfield::Params::SetUniqueId(
+ int unique_id) {
+ DCHECK_GE(unique_id, 0);
+ unique_id_ = unique_id;
+ return *this;
+}
+
+DialogModelTextfield::Params& DialogModelTextfield::Params::AddAccelerator(
+ Accelerator accelerator) {
+ accelerators_.insert(std::move(accelerator));
+ return *this;
+}
+
+DialogModelTextfield::Params& DialogModelTextfield::Params::SetAccessibleName(
+ base::string16 accessible_name) {
+ accessible_name_ = accessible_name;
+ return *this;
+}
+
+DialogModelTextfield::DialogModelTextfield(
+ util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 label,
+ base::string16 text,
+ const ui::DialogModelTextfield::Params& params)
+ : DialogModelField(pass_key,
+ model,
+ kTextfield,
+ params.unique_id_,
+ params.accelerators_),
+ label_(label),
+ accessible_name_(params.accessible_name_),
+ text_(std::move(text)) {}
+
+DialogModelTextfield::~DialogModelTextfield() = default;
+
+void DialogModelTextfield::OnTextChanged(util::PassKey<DialogModelHost>,
+ base::string16 text) {
+ text_ = std::move(text);
+}
+
+} // namespace ui \ No newline at end of file
diff --git a/chromium/ui/base/models/dialog_model_field.h b/chromium/ui/base/models/dialog_model_field.h
new file mode 100644
index 00000000000..98a00e2b796
--- /dev/null
+++ b/chromium/ui/base/models/dialog_model_field.h
@@ -0,0 +1,299 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_
+#define UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_
+
+#include "base/callback.h"
+#include "base/containers/flat_set.h"
+#include "base/strings/string16.h"
+#include "base/util/type_safety/pass_key.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/models/combobox_model.h"
+
+namespace ui {
+
+class DialogModel;
+class DialogModelButton;
+class DialogModelBodyText;
+class DialogModelCombobox;
+class DialogModelHost;
+class DialogModelTextfield;
+class Event;
+
+// These "field" classes represent entries in a DialogModel. They are owned
+// by the model and either created through the model or DialogModel::Builder.
+// These entries can be referred to by setting the field's unique id in
+// construction parameters (::Params::SetUniqueId()). They can then later be
+// acquired through DialogModel::GetFieldByUniqueId() methods.
+// These fields own the data corresponding to their field. For instance, the
+// text of a textfield in a model is read using DialogModelTextfield::text() and
+// stays in sync with the visible dialog (through DialogModelHosts).
+class COMPONENT_EXPORT(UI_BASE) DialogModelField {
+ public:
+ enum Type { kButton, kBodyText, kCombobox, kTextfield };
+
+ DialogModelField(const DialogModelField&) = delete;
+ DialogModelField& operator=(const DialogModelField&) = delete;
+ virtual ~DialogModelField();
+
+ // Methods with util::PassKey<DialogModelHost> are only intended to be called
+ // by the DialogModelHost implementation.
+ Type type(util::PassKey<DialogModelHost>) const { return type_; }
+ const base::flat_set<Accelerator>& accelerators(
+ util::PassKey<DialogModelHost>) const {
+ return accelerators_;
+ }
+ DialogModelButton* AsButton(util::PassKey<DialogModelHost>);
+ DialogModelBodyText* AsBodyText(util::PassKey<DialogModelHost>);
+ DialogModelCombobox* AsCombobox(util::PassKey<DialogModelHost>);
+ DialogModelTextfield* AsTextfield(util::PassKey<DialogModelHost>);
+
+ protected:
+ // Children of this class need to be constructed through DialogModel to help
+ // enforce that they're added to the model.
+ DialogModelField(util::PassKey<DialogModel>,
+ DialogModel* model,
+ Type type,
+ int unique_id,
+ base::flat_set<Accelerator> accelerators);
+
+ DialogModelButton* AsButton();
+ DialogModelBodyText* AsBodyText();
+ DialogModelCombobox* AsCombobox();
+ DialogModelTextfield* AsTextfield();
+
+ private:
+ friend class DialogModel;
+
+ DialogModel* const model_;
+ const Type type_;
+ const int unique_id_;
+
+ const base::flat_set<Accelerator> accelerators_;
+};
+
+// Field class representing a dialog button.
+class COMPONENT_EXPORT(UI_BASE) DialogModelButton : public DialogModelField {
+ public:
+ class COMPONENT_EXPORT(UI_BASE) Params {
+ public:
+ Params();
+ Params(const Params&) = delete;
+ Params& operator=(const Params&) = delete;
+ ~Params();
+
+ Params& SetUniqueId(int unique_id);
+
+ Params& AddAccelerator(Accelerator accelerator);
+ Params& SetAccessibleName(base::string16 accessible_name);
+
+ private:
+ friend class DialogModelButton;
+
+ int unique_id_ = -1;
+ base::flat_set<Accelerator> accelerators_;
+ };
+
+ // Note that this is constructed through a DialogModel which adds it to model
+ // fields.
+ DialogModelButton(util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::RepeatingCallback<void(const Event&)> callback,
+ base::string16 label,
+ const Params& params);
+ DialogModelButton(const DialogModelButton&) = delete;
+ DialogModelButton& operator=(const DialogModelButton&) = delete;
+ ~DialogModelButton() override;
+
+ // Methods with util::PassKey<DialogModelHost> are only intended to be called
+ // by the DialogModelHost implementation.
+ const base::string16& label(util::PassKey<DialogModelHost>) const {
+ return label_;
+ }
+ void OnPressed(util::PassKey<DialogModelHost>, const Event& event);
+
+ private:
+ friend class DialogModel;
+
+ const base::string16 label_;
+ // The button callback gets called when the button is activated. Whether
+ // that happens on key-press, release, etc. is implementation (and platform)
+ // dependent.
+ base::RepeatingCallback<void(const Event&)> callback_;
+};
+
+// Field class representing body text
+class COMPONENT_EXPORT(UI_BASE) DialogModelBodyText : public DialogModelField {
+ public:
+ class COMPONENT_EXPORT(UI_BASE) Params {
+ public:
+ Params() = default;
+ Params(const Params&) = delete;
+ Params& operator=(const Params&) = delete;
+
+ // The body text is "secondary", often adding detail and context to other,
+ // more prominent text.
+ Params& SetIsSecondary();
+
+ private:
+ friend class DialogModelBodyText;
+
+ bool is_secondary_ = false;
+ };
+
+ // Note that this is constructed through a DialogModel which adds it to model
+ // fields.
+ DialogModelBodyText(util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 text,
+ const Params& params);
+ DialogModelBodyText(const DialogModelBodyText&) = delete;
+ DialogModelBodyText& operator=(const DialogModelBodyText&) = delete;
+ ~DialogModelBodyText() override;
+
+ const base::string16& text(util::PassKey<DialogModelHost>) const {
+ return text_;
+ }
+ bool is_secondary(util::PassKey<DialogModelHost>) const {
+ return is_secondary_;
+ }
+
+ private:
+ const base::string16 text_;
+ const bool is_secondary_;
+};
+
+// Field class representing a combobox and corresponding label to describe the
+// combobox:
+//
+// <label> [combobox]
+// Ex: Folder [My Bookmarks]
+class COMPONENT_EXPORT(UI_BASE) DialogModelCombobox : public DialogModelField {
+ public:
+ class COMPONENT_EXPORT(UI_BASE) Params {
+ public:
+ Params();
+ Params(const Params&) = delete;
+ Params& operator=(const Params&) = delete;
+ ~Params();
+
+ Params& SetUniqueId(int unique_id);
+
+ Params& AddAccelerator(Accelerator accelerator);
+ Params& SetAccessibleName(base::string16 accessible_name);
+
+ // The combobox callback is invoked when an item has been selected. This
+ // nominally happens when selecting an item in the combobox menu. The
+ // selection notably does not change by hovering different items in the
+ // combobox menu or navigating it with up/down keys as long as the menu is
+ // open.
+ Params& SetCallback(base::RepeatingClosure callback);
+
+ private:
+ friend class DialogModelCombobox;
+
+ int unique_id_ = -1;
+ base::string16 accessible_name_;
+ base::RepeatingClosure callback_;
+ base::flat_set<Accelerator> accelerators_;
+ };
+
+ // Note that this is constructed through a DialogModel which adds it to model
+ // fields.
+ DialogModelCombobox(util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 label,
+ std::unique_ptr<ui::ComboboxModel> combobox_model,
+ const Params& params);
+ DialogModelCombobox(const DialogModelCombobox&) = delete;
+ DialogModelCombobox& operator=(const DialogModelCombobox&) = delete;
+ ~DialogModelCombobox() override;
+
+ int selected_index() const { return selected_index_; }
+ ui::ComboboxModel* combobox_model() { return combobox_model_.get(); }
+
+ // Methods with util::PassKey<DialogModelHost> are only intended to be called
+ // by the DialogModelHost implementation.
+ const base::string16& label(util::PassKey<DialogModelHost>) const {
+ return label_;
+ }
+ const base::string16& accessible_name(util::PassKey<DialogModelHost>) const {
+ return accessible_name_;
+ }
+ void OnSelectedIndexChanged(util::PassKey<DialogModelHost>,
+ int selected_index);
+ void OnPerformAction(util::PassKey<DialogModelHost>);
+
+ private:
+ friend class DialogModel;
+
+ const base::string16 label_;
+ const base::string16 accessible_name_;
+ int selected_index_;
+ std::unique_ptr<ui::ComboboxModel> combobox_model_;
+ base::RepeatingClosure callback_;
+};
+
+// Field class representing a textfield and corresponding label to describe the
+// textfield:
+//
+// <label> [textfield]
+// Ex: Name [My email]
+class COMPONENT_EXPORT(UI_BASE) DialogModelTextfield : public DialogModelField {
+ public:
+ class COMPONENT_EXPORT(UI_BASE) Params {
+ public:
+ Params();
+ Params(const Params&) = delete;
+ Params& operator=(const Params&) = delete;
+ ~Params();
+
+ Params& SetUniqueId(int unique_id);
+
+ Params& AddAccelerator(Accelerator accelerator);
+ Params& SetAccessibleName(base::string16 accessible_name);
+
+ private:
+ friend class DialogModelTextfield;
+
+ int unique_id_ = -1;
+ base::string16 accessible_name_;
+ base::flat_set<Accelerator> accelerators_;
+ };
+
+ // Note that this is constructed through a DialogModel which adds it to model
+ // fields.
+ DialogModelTextfield(util::PassKey<DialogModel> pass_key,
+ DialogModel* model,
+ base::string16 label,
+ base::string16 text,
+ const Params& params);
+ DialogModelTextfield(const DialogModelTextfield&) = delete;
+ DialogModelTextfield& operator=(const DialogModelTextfield&) = delete;
+ ~DialogModelTextfield() override;
+
+ const base::string16& text() const { return text_; }
+
+ // Methods with util::PassKey<DialogModelHost> are only intended to be called
+ // by the DialogModelHost implementation.
+ const base::string16& label(util::PassKey<DialogModelHost>) const {
+ return label_;
+ }
+ const base::string16& accessible_name(util::PassKey<DialogModelHost>) const {
+ return accessible_name_;
+ }
+ void OnTextChanged(util::PassKey<DialogModelHost>, base::string16 text);
+
+ private:
+ friend class DialogModel;
+
+ const base::string16 label_;
+ const base::string16 accessible_name_;
+ base::string16 text_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_ \ No newline at end of file
diff --git a/chromium/ui/base/models/dialog_model_host.h b/chromium/ui/base/models/dialog_model_host.h
new file mode 100644
index 00000000000..dacfa23e967
--- /dev/null
+++ b/chromium/ui/base/models/dialog_model_host.h
@@ -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.
+
+#ifndef UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
+#define UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
+
+#include "base/util/type_safety/pass_key.h"
+
+namespace ui {
+
+class DialogModel;
+
+// Platform-agnostic interface for toolkit integrations.
+class COMPONENT_EXPORT(UI_BASE) DialogModelHost {
+ public:
+ // TODO(pbos): Try to get Close semantically synchronous (guarantee
+ // synchronous destruction of model), so it cannot be observed as
+ // asynchronous even if GetWidget()->Close() under Views is async.
+ virtual void Close() = 0;
+
+ // Selects all text of a textfield.
+ // TODO(pbos): Reconsider whether this should be implied by if the textfield
+ // is initially focused.
+ virtual void SelectAllText(int unique_id) = 0;
+
+ protected:
+ friend class DialogModel;
+ friend class DialogModelField;
+
+ // This PassKey is used to make sure that some methods on DialogModel
+ // are only called as part of the host integration.
+ util::PassKey<DialogModelHost> GetPassKey() {
+ return util::PassKey<DialogModelHost>();
+ }
+
+ // Called when various parts of the model changes.
+ // TODO(pbos): Break this down to API that says what was added/removed/changed
+ // to not have to reset everything.
+ virtual void OnFieldAdded(DialogModelField* field) = 0;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
diff --git a/chromium/ui/base/models/image_model.cc b/chromium/ui/base/models/image_model.cc
index 9578759666b..acecd359f14 100644
--- a/chromium/ui/base/models/image_model.cc
+++ b/chromium/ui/base/models/image_model.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <tuple>
+
#include "ui/base/models/image_model.h"
namespace ui {
@@ -11,7 +13,7 @@ VectorIconModel::VectorIconModel() = default;
VectorIconModel::VectorIconModel(const gfx::VectorIcon& vector_icon,
int color_id,
int icon_size)
- : vector_icon_(&vector_icon), icon_size_(icon_size), color_id_(color_id) {}
+ : vector_icon_(&vector_icon), icon_size_(icon_size), color_(color_id) {}
VectorIconModel::VectorIconModel(const gfx::VectorIcon& vector_icon,
SkColor color,
@@ -28,12 +30,21 @@ VectorIconModel::VectorIconModel(VectorIconModel&&) = default;
VectorIconModel& VectorIconModel::operator=(VectorIconModel&&) = default;
+bool VectorIconModel::operator==(const VectorIconModel& other) const {
+ return std::tie(vector_icon_, icon_size_, color_) ==
+ std::tie(other.vector_icon_, other.icon_size_, other.color_);
+}
+
+bool VectorIconModel::operator!=(const VectorIconModel& other) const {
+ return !(*this == other);
+}
+
ImageModel::ImageModel() = default;
ImageModel::ImageModel(const VectorIconModel& vector_icon_model)
- : vector_icon_model_(vector_icon_model) {}
+ : icon_(vector_icon_model) {}
-ImageModel::ImageModel(const gfx::Image& image) : image_(image) {}
+ImageModel::ImageModel(const gfx::Image& image) : icon_(image) {}
ImageModel::ImageModel(const gfx::ImageSkia& image_skia)
: ImageModel(gfx::Image(image_skia)) {}
@@ -77,11 +88,13 @@ bool ImageModel::IsEmpty() const {
}
bool ImageModel::IsVectorIcon() const {
- return vector_icon_model_ && !vector_icon_model_.value().is_empty();
+ return absl::holds_alternative<VectorIconModel>(icon_) &&
+ !absl::get<VectorIconModel>(icon_).is_empty();
}
bool ImageModel::IsImage() const {
- return image_ && !image_.value().IsEmpty();
+ return absl::holds_alternative<gfx::Image>(icon_) &&
+ !absl::get<gfx::Image>(icon_).IsEmpty();
}
gfx::Size ImageModel::Size() const {
@@ -92,14 +105,22 @@ gfx::Size ImageModel::Size() const {
return IsImage() ? GetImage().Size() : gfx::Size();
}
-const VectorIconModel ImageModel::GetVectorIcon() const {
+VectorIconModel ImageModel::GetVectorIcon() const {
DCHECK(IsVectorIcon());
- return vector_icon_model_.value();
+ return absl::get<VectorIconModel>(icon_);
}
-const gfx::Image ImageModel::GetImage() const {
+gfx::Image ImageModel::GetImage() const {
DCHECK(IsImage());
- return image_.value();
+ return absl::get<gfx::Image>(icon_);
+}
+
+bool ImageModel::operator==(const ImageModel& other) const {
+ return icon_ == other.icon_;
+}
+
+bool ImageModel::operator!=(const ImageModel& other) const {
+ return !(*this == other);
}
-} // namespace ui \ No newline at end of file
+} // namespace ui
diff --git a/chromium/ui/base/models/image_model.h b/chromium/ui/base/models/image_model.h
index dbc4cf8b01d..7c9fb9bef17 100644
--- a/chromium/ui/base/models/image_model.h
+++ b/chromium/ui/base/models/image_model.h
@@ -7,8 +7,9 @@
#include "base/callback.h"
#include "base/component_export.h"
-#include "base/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
@@ -39,6 +40,9 @@ class COMPONENT_EXPORT(UI_BASE) VectorIconModel {
bool is_empty() const { return !vector_icon_; }
+ bool operator==(const VectorIconModel& other) const;
+ bool operator!=(const VectorIconModel& other) const;
+
private:
friend class ThemedVectorIcon;
friend class ImageModel;
@@ -54,17 +58,13 @@ class COMPONENT_EXPORT(UI_BASE) VectorIconModel {
const gfx::VectorIcon* vector_icon() const { return vector_icon_; }
int icon_size() const { return icon_size_; }
- int color_id() const { return color_id_.value(); }
- SkColor color() const { return color_.value(); }
- bool has_color() const { return color_.has_value(); }
+ int color_id() const { return absl::get<int>(color_); }
+ SkColor color() const { return absl::get<SkColor>(color_); }
+ bool has_color() const { return absl::holds_alternative<SkColor>(color_); }
const gfx::VectorIcon* vector_icon_ = nullptr;
int icon_size_ = 0;
- // Only one of the following will ever be assigned.
- // TODO: Update to use std::variant or base:Variant once one of them is
- // available to use.
- base::Optional<int> color_id_;
- base::Optional<SkColor> color_;
+ absl::variant<int, SkColor> color_ = gfx::kPlaceholderColor;
};
// ImageModel encapsulates either a gfx::Image or a VectorIconModel. Only one
@@ -94,19 +94,19 @@ class COMPONENT_EXPORT(UI_BASE) ImageModel {
bool IsImage() const;
gfx::Size Size() const;
// Only valid if IsVectorIcon() or IsImage() return true, respectively.
- const VectorIconModel GetVectorIcon() const;
- const gfx::Image GetImage() const;
+ VectorIconModel GetVectorIcon() const;
+ gfx::Image GetImage() const;
+
+ // Checks if both model yield equal images.
+ bool operator==(const ImageModel& other) const;
+ bool operator!=(const ImageModel& other) const;
private:
ImageModel(const gfx::Image& image);
ImageModel(const gfx::ImageSkia& image_skia);
ImageModel(const VectorIconModel& vector_icon_model);
- // Only one of the following will ever be assigned.
- // TODO: Update to use std::variant or base:Variant once one of them is
- // available to use.
- base::Optional<VectorIconModel> vector_icon_model_;
- base::Optional<gfx::Image> image_;
+ absl::variant<VectorIconModel, gfx::Image> icon_;
};
} // namespace ui
diff --git a/chromium/ui/base/models/image_model_unittest.cc b/chromium/ui/base/models/image_model_unittest.cc
index 8bb31fc06a1..660375004d1 100644
--- a/chromium/ui/base/models/image_model_unittest.cc
+++ b/chromium/ui/base/models/image_model_unittest.cc
@@ -16,7 +16,7 @@ namespace ui {
namespace {
-const gfx::VectorIcon& GetVectorIcon() {
+const gfx::VectorIcon& GetCircleVectorIcon() {
static constexpr gfx::PathElement path[] = {gfx::CommandType::CIRCLE, 24, 18,
5};
static const gfx::VectorIconRep rep[] = {{path, 4}};
@@ -25,6 +25,16 @@ const gfx::VectorIcon& GetVectorIcon() {
return circle_icon;
}
+const gfx::VectorIcon& GetRectVectorIcon() {
+ static constexpr gfx::PathElement path[] = {
+ gfx::CommandType::LINE_TO, 0, 10, gfx::CommandType::LINE_TO, 10, 10,
+ gfx::CommandType::LINE_TO, 10, 0, gfx::CommandType::CLOSE};
+ static const gfx::VectorIconRep rep[] = {{path, 10}};
+ static constexpr gfx::VectorIcon rect_icon = {rep, 1, "rect"};
+
+ return rect_icon;
+}
+
} // namespace
TEST(ImageModelTest, DefaultEmpty) {
@@ -40,7 +50,7 @@ TEST(ImageModelTest, DefaultVectorIconEmpty) {
}
TEST(ImageModelTest, CheckForVectorIcon) {
- ImageModel image_model = ImageModel::FromVectorIcon(GetVectorIcon());
+ ImageModel image_model = ImageModel::FromVectorIcon(GetCircleVectorIcon());
EXPECT_FALSE(image_model.IsEmpty());
EXPECT_TRUE(image_model.IsVectorIcon());
@@ -57,7 +67,7 @@ TEST(ImageModelTest, CheckForImage) {
TEST(ImageModelTest, Size) {
EXPECT_EQ(gfx::Size(), ImageModel().Size());
EXPECT_EQ(gfx::Size(16, 16),
- ImageModel::FromVectorIcon(GetVectorIcon(), -1, 16).Size());
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), -1, 16).Size());
EXPECT_EQ(gfx::Size(16, 16),
ImageModel::FromImage(gfx::test::CreateImage(16, 16)).Size());
}
@@ -65,7 +75,7 @@ TEST(ImageModelTest, Size) {
TEST(ImageModelTest, CheckAssignVectorIcon) {
VectorIconModel vector_icon_model_dest;
VectorIconModel vector_icon_model_src =
- ImageModel::FromVectorIcon(GetVectorIcon()).GetVectorIcon();
+ ImageModel::FromVectorIcon(GetCircleVectorIcon()).GetVectorIcon();
EXPECT_TRUE(vector_icon_model_dest.is_empty());
EXPECT_FALSE(vector_icon_model_src.is_empty());
@@ -90,7 +100,7 @@ TEST(ImageModelTest, CheckAssignImage) {
EXPECT_TRUE(image_model_dest.IsImage());
EXPECT_FALSE(image_model_dest.IsVectorIcon());
- image_model_src = ImageModel::FromVectorIcon(GetVectorIcon());
+ image_model_src = ImageModel::FromVectorIcon(GetCircleVectorIcon());
EXPECT_TRUE(image_model_src.IsVectorIcon());
@@ -100,4 +110,50 @@ TEST(ImageModelTest, CheckAssignImage) {
EXPECT_FALSE(image_model_dest.IsImage());
}
+TEST(ImageModelTest, CheckEqual) {
+ ImageModel image_model_src;
+ ImageModel image_model_dest;
+ EXPECT_EQ(image_model_src, image_model_dest);
+
+ auto first_image = gfx::test::CreateImage(16, 16);
+ image_model_src = ImageModel::FromImage(first_image);
+ EXPECT_NE(image_model_src, image_model_dest);
+ image_model_dest = ImageModel::FromImage(first_image);
+ EXPECT_EQ(image_model_src, image_model_dest);
+ image_model_dest = ImageModel::FromImage(gfx::test::CreateImage(16, 16));
+ EXPECT_NE(image_model_src, image_model_dest);
+ image_model_src = image_model_dest;
+ EXPECT_EQ(image_model_src, image_model_dest);
+
+ image_model_dest = ImageModel::FromVectorIcon(GetRectVectorIcon());
+ EXPECT_NE(image_model_src, image_model_dest);
+ image_model_src = ImageModel::FromVectorIcon(GetRectVectorIcon());
+ EXPECT_EQ(image_model_src, image_model_dest);
+ image_model_dest = ImageModel::FromVectorIcon(GetCircleVectorIcon());
+ EXPECT_NE(image_model_src, image_model_dest);
+ image_model_src = image_model_dest;
+ EXPECT_EQ(image_model_src, image_model_dest);
+
+ image_model_src = ImageModel::FromVectorIcon(GetCircleVectorIcon(), 1);
+ image_model_dest =
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorMAGENTA);
+ EXPECT_NE(image_model_src, image_model_dest);
+
+ image_model_src = ImageModel::FromVectorIcon(GetCircleVectorIcon(), 1);
+ image_model_dest = ImageModel::FromVectorIcon(GetCircleVectorIcon(), 2);
+ EXPECT_NE(image_model_src, image_model_dest);
+
+ image_model_src =
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorCYAN);
+ image_model_dest =
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorMAGENTA);
+ EXPECT_NE(image_model_src, image_model_dest);
+
+ image_model_src =
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorMAGENTA, 1);
+ image_model_dest =
+ ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorMAGENTA, 2);
+ EXPECT_NE(image_model_src, image_model_dest);
+}
+
} // namespace ui
diff --git a/chromium/ui/base/models/simple_menu_model.h b/chromium/ui/base/models/simple_menu_model.h
index 5f1116ba19b..31669898f91 100644
--- a/chromium/ui/base/models/simple_menu_model.h
+++ b/chromium/ui/base/models/simple_menu_model.h
@@ -27,6 +27,9 @@ class ButtonMenuItemModel;
// The breadth of MenuModel is not exposed through this API.
class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel {
public:
+ // Default icon size to be used for context menus.
+ static constexpr int kDefaultIconSize = 16;
+
class COMPONENT_EXPORT(UI_BASE) Delegate : public AcceleratorProvider {
public:
~Delegate() override {}
diff --git a/chromium/ui/base/mojom/BUILD.gn b/chromium/ui/base/mojom/BUILD.gn
index 49ea535072d..dd4b545a49c 100644
--- a/chromium/ui/base/mojom/BUILD.gn
+++ b/chromium/ui/base/mojom/BUILD.gn
@@ -27,6 +27,10 @@ mojom("mojom") {
cpp = "::ui::DialogButton"
},
{
+ mojom = "ui.mojom.MenuSourceType"
+ cpp = "::ui::MenuSourceType"
+ },
+ {
mojom = "ui.mojom.ModalType"
cpp = "::ui::ModalType"
},
@@ -45,4 +49,8 @@ mojom("mojom") {
traits_public_deps = [ "//ui/base" ]
},
]
+
+ export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+ export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+ export_header_blink = "third_party/blink/public/platform/web_common.h"
}
diff --git a/chromium/ui/base/mojom/clipboard_blink_mojom_traits.h b/chromium/ui/base/mojom/clipboard_blink_mojom_traits.h
index e993d501ea7..b49dd293a00 100644
--- a/chromium/ui/base/mojom/clipboard_blink_mojom_traits.h
+++ b/chromium/ui/base/mojom/clipboard_blink_mojom_traits.h
@@ -26,12 +26,8 @@ struct EnumTraits<blink::mojom::ClipboardBuffer, ui::ClipboardBuffer> {
*out = ui::ClipboardBuffer::kCopyPaste;
return true;
case blink::mojom::ClipboardBuffer::kSelection:
-#if defined(USE_X11)
*out = ui::ClipboardBuffer::kSelection;
return true;
-#else
- return false;
-#endif
}
return false;
}
diff --git a/chromium/ui/base/mojom/ui_base_types.mojom b/chromium/ui/base/mojom/ui_base_types.mojom
index 82bc476b639..034b4e3963f 100644
--- a/chromium/ui/base/mojom/ui_base_types.mojom
+++ b/chromium/ui/base/mojom/ui_base_types.mojom
@@ -26,3 +26,19 @@ enum ModalType {
// Window is modal to all other windows.
SYSTEM
};
+
+// It's mapped to ui::MenuSourceType. Any new type here needs to be synced
+// with ui::MenuSourceType in ui_base_types.h.
+enum MenuSourceType {
+ NONE,
+ MOUSE,
+ KEYBOARD,
+ TOUCH,
+ TOUCH_EDIT_MENU,
+ LONG_PRESS,
+ LONG_TAP,
+ TOUCH_HANDLE,
+ STYLUS,
+ ADJUST_SELECTION,
+ ADJUST_SELECTION_RESET
+}; \ No newline at end of file
diff --git a/chromium/ui/base/mojom/ui_base_types_mojom_traits.h b/chromium/ui/base/mojom/ui_base_types_mojom_traits.h
index 4abd43a66fd..7b5a4a94158 100644
--- a/chromium/ui/base/mojom/ui_base_types_mojom_traits.h
+++ b/chromium/ui/base/mojom/ui_base_types_mojom_traits.h
@@ -86,6 +86,79 @@ struct EnumTraits<ui::mojom::ModalType, ui::ModalType> {
}
};
+template <>
+struct EnumTraits<ui::mojom::MenuSourceType, ui::MenuSourceType> {
+ static ui::mojom::MenuSourceType ToMojom(ui::MenuSourceType modal_type) {
+ switch (modal_type) {
+ case ui::MENU_SOURCE_NONE:
+ return ui::mojom::MenuSourceType::NONE;
+ case ui::MENU_SOURCE_MOUSE:
+ return ui::mojom::MenuSourceType::MOUSE;
+ case ui::MENU_SOURCE_KEYBOARD:
+ return ui::mojom::MenuSourceType::KEYBOARD;
+ case ui::MENU_SOURCE_TOUCH:
+ return ui::mojom::MenuSourceType::TOUCH;
+ case ui::MENU_SOURCE_TOUCH_EDIT_MENU:
+ return ui::mojom::MenuSourceType::TOUCH_EDIT_MENU;
+ case ui::MENU_SOURCE_LONG_PRESS:
+ return ui::mojom::MenuSourceType::LONG_PRESS;
+ case ui::MENU_SOURCE_LONG_TAP:
+ return ui::mojom::MenuSourceType::LONG_TAP;
+ case ui::MENU_SOURCE_TOUCH_HANDLE:
+ return ui::mojom::MenuSourceType::TOUCH_HANDLE;
+ case ui::MENU_SOURCE_STYLUS:
+ return ui::mojom::MenuSourceType::STYLUS;
+ case ui::MENU_SOURCE_ADJUST_SELECTION:
+ return ui::mojom::MenuSourceType::ADJUST_SELECTION;
+ case ui::MENU_SOURCE_ADJUST_SELECTION_RESET:
+ return ui::mojom::MenuSourceType::ADJUST_SELECTION_RESET;
+ }
+ NOTREACHED();
+ return ui::mojom::MenuSourceType::NONE;
+ }
+
+ static bool FromMojom(ui::mojom::MenuSourceType modal_type,
+ ui::MenuSourceType* out) {
+ switch (modal_type) {
+ case ui::mojom::MenuSourceType::NONE:
+ *out = ui::MENU_SOURCE_NONE;
+ return true;
+ case ui::mojom::MenuSourceType::MOUSE:
+ *out = ui::MENU_SOURCE_MOUSE;
+ return true;
+ case ui::mojom::MenuSourceType::KEYBOARD:
+ *out = ui::MENU_SOURCE_KEYBOARD;
+ return true;
+ case ui::mojom::MenuSourceType::TOUCH:
+ *out = ui::MENU_SOURCE_TOUCH;
+ return true;
+ case ui::mojom::MenuSourceType::TOUCH_EDIT_MENU:
+ *out = ui::MENU_SOURCE_TOUCH_EDIT_MENU;
+ return true;
+ case ui::mojom::MenuSourceType::LONG_PRESS:
+ *out = ui::MENU_SOURCE_LONG_PRESS;
+ return true;
+ case ui::mojom::MenuSourceType::LONG_TAP:
+ *out = ui::MENU_SOURCE_LONG_TAP;
+ return true;
+ case ui::mojom::MenuSourceType::TOUCH_HANDLE:
+ *out = ui::MENU_SOURCE_TOUCH_HANDLE;
+ return true;
+ case ui::mojom::MenuSourceType::STYLUS:
+ *out = ui::MENU_SOURCE_STYLUS;
+ return true;
+ case ui::mojom::MenuSourceType::ADJUST_SELECTION:
+ *out = ui::MENU_SOURCE_ADJUST_SELECTION;
+ return true;
+ case ui::mojom::MenuSourceType::ADJUST_SELECTION_RESET:
+ *out = ui::MENU_SOURCE_ADJUST_SELECTION_RESET;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+ }
+};
+
} // namespace mojo
#endif // UI_BASE_MOJOM_UI_BASE_TYPES_MOJOM_TRAITS_H_
diff --git a/chromium/ui/base/pointer/touch_ui_controller.cc b/chromium/ui/base/pointer/touch_ui_controller.cc
index 326da1df5d9..d91151b4433 100644
--- a/chromium/ui/base/pointer/touch_ui_controller.cc
+++ b/chromium/ui/base/pointer/touch_ui_controller.cc
@@ -9,9 +9,9 @@
#include "base/bind.h"
#include "base/command_line.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "ui/base/ui_base_switches.h"
@@ -78,7 +78,7 @@ TouchUiController* TouchUiController::Get() {
TouchUiController::TouchUiController(TouchUiState touch_ui_state)
: touch_ui_state_(touch_ui_state) {
#if defined(OS_WIN)
- if (base::MessageLoopCurrentForUI::IsSet() &&
+ if (base::CurrentUIThread::IsSet() &&
(base::win::GetVersion() >= base::win::Version::WIN10)) {
singleton_hwnd_observer_ =
std::make_unique<gfx::SingletonHwndObserver>(base::BindRepeating(
diff --git a/chromium/ui/base/pointer/touch_ui_controller.h b/chromium/ui/base/pointer/touch_ui_controller.h
index 0240f4eaef3..bcd93ac60e9 100644
--- a/chromium/ui/base/pointer/touch_ui_controller.h
+++ b/chromium/ui/base/pointer/touch_ui_controller.h
@@ -23,7 +23,7 @@ namespace ui {
// Central controller to handle touch UI modes.
class COMPONENT_EXPORT(UI_BASE) TouchUiController {
public:
- using CallbackList = base::CallbackList<void()>;
+ using CallbackList = base::RepeatingClosureList;
using Subscription = CallbackList::Subscription;
enum class TouchUiState {
diff --git a/chromium/ui/base/prediction/BUILD.gn b/chromium/ui/base/prediction/BUILD.gn
new file mode 100644
index 00000000000..bfd837c7b7e
--- /dev/null
+++ b/chromium/ui/base/prediction/BUILD.gn
@@ -0,0 +1,37 @@
+# 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.
+
+component("prediction") {
+ sources = [
+ "empty_filter.cc",
+ "empty_filter.h",
+ "empty_predictor.cc",
+ "empty_predictor.h",
+ "input_filter.h",
+ "input_predictor.h",
+ "kalman_filter.cc",
+ "kalman_filter.h",
+ "kalman_predictor.cc",
+ "kalman_predictor.h",
+ "least_squares_predictor.cc",
+ "least_squares_predictor.h",
+ "linear_predictor.cc",
+ "linear_predictor.h",
+ "linear_resampling.cc",
+ "linear_resampling.h",
+ "one_euro_filter.cc",
+ "one_euro_filter.h",
+ "prediction_metrics_handler.cc",
+ "prediction_metrics_handler.h",
+ ]
+
+ defines = [ "IS_UI_BASE_PREDICTION_IMPL" ]
+
+ deps = [
+ "//base",
+ "//third_party/one_euro_filter",
+ "//ui/base:features",
+ "//ui/gfx/geometry:geometry",
+ ]
+}
diff --git a/chromium/ui/base/prediction/DEPS b/chromium/ui/base/prediction/DEPS
new file mode 100644
index 00000000000..3adfb850470
--- /dev/null
+++ b/chromium/ui/base/prediction/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/one_euro_filter/src"
+]
diff --git a/chromium/ui/base/prediction/empty_filter.cc b/chromium/ui/base/prediction/empty_filter.cc
new file mode 100644
index 00000000000..754167e50b1
--- /dev/null
+++ b/chromium/ui/base/prediction/empty_filter.cc
@@ -0,0 +1,28 @@
+// 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 "ui/base/prediction/empty_filter.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+EmptyFilter::EmptyFilter() {}
+EmptyFilter::~EmptyFilter() {}
+
+bool EmptyFilter::Filter(const base::TimeTicks& timestamp,
+ gfx::PointF* position) const {
+ return position;
+}
+
+const char* EmptyFilter::GetName() const {
+ return features::kFilterNameEmpty;
+}
+
+InputFilter* EmptyFilter::Clone() {
+ return new EmptyFilter();
+}
+
+void EmptyFilter::Reset() {}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/empty_filter.h b/chromium/ui/base/prediction/empty_filter.h
new file mode 100644
index 00000000000..bd83d723547
--- /dev/null
+++ b/chromium/ui/base/prediction/empty_filter.h
@@ -0,0 +1,40 @@
+// 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 UI_BASE_PREDICTION_EMPTY_FILTER_H_
+#define UI_BASE_PREDICTION_EMPTY_FILTER_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "ui/base/prediction/input_filter.h"
+
+namespace ui {
+
+// Empty filter is a fake filter. Always returns the same input position as
+// the filtered position. Mainly used for testing purpose.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) EmptyFilter : public InputFilter {
+ public:
+ explicit EmptyFilter();
+ ~EmptyFilter() override;
+
+ // Filters the position sent to the filter at a specific timestamp.
+ // Returns true if the value is filtered, false otherwise.
+ bool Filter(const base::TimeTicks& timestamp,
+ gfx::PointF* position) const override;
+
+ // Returns the name of the filter
+ const char* GetName() const override;
+
+ // Returns a copy of the filter.
+ InputFilter* Clone() override;
+
+ // Reset the filter to its initial state
+ void Reset() override;
+
+ DISALLOW_COPY_AND_ASSIGN(EmptyFilter);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_EMPTY_FILTER_H_
diff --git a/chromium/ui/base/prediction/empty_filter_unittests.cc b/chromium/ui/base/prediction/empty_filter_unittests.cc
new file mode 100644
index 00000000000..1c73f06993a
--- /dev/null
+++ b/chromium/ui/base/prediction/empty_filter_unittests.cc
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/empty_filter.h"
+#include "ui/base/prediction/input_filter_unittest_helpers.h"
+#include "ui/base/prediction/prediction_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+class EmptyFilterTest : public InputFilterTest {
+ public:
+ explicit EmptyFilterTest() {}
+
+ void SetUp() override { filter_ = std::make_unique<EmptyFilter>(); }
+
+ DISALLOW_COPY_AND_ASSIGN(EmptyFilterTest);
+};
+
+// Test the Clone function of the filter
+TEST_F(EmptyFilterTest, TestClone) {
+ TestCloneFilter();
+}
+
+// Test the Reset function of the filter
+TEST_F(EmptyFilterTest, TestReset) {
+ TestResetFilter();
+}
+
+// Test the empty filter gives the same values
+TEST_F(EmptyFilterTest, filteringValues) {
+ base::TimeTicks ts = PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ gfx::PointF point, filtered_point;
+ for (int i = 0; i < 100; i++) {
+ point.SetPoint(base::RandDouble(), base::RandDouble());
+ filtered_point = point;
+ EXPECT_TRUE(filter_->Filter(ts, &filtered_point));
+ EXPECT_EQ(point.x(), filtered_point.x());
+ EXPECT_EQ(point.y(), filtered_point.y());
+ }
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/empty_predictor.cc b/chromium/ui/base/prediction/empty_predictor.cc
new file mode 100644
index 00000000000..98598808b49
--- /dev/null
+++ b/chromium/ui/base/prediction/empty_predictor.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/empty_predictor.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+EmptyPredictor::EmptyPredictor() {
+ Reset();
+}
+
+EmptyPredictor::~EmptyPredictor() = default;
+
+const char* EmptyPredictor::GetName() const {
+ return features::kPredictorNameEmpty;
+}
+
+void EmptyPredictor::Reset() {
+ last_input_ = base::nullopt;
+}
+
+void EmptyPredictor::Update(const InputData& cur_input) {
+ last_input_ = cur_input;
+}
+
+bool EmptyPredictor::HasPrediction() const {
+ return last_input_.has_value();
+}
+
+std::unique_ptr<InputPredictor::InputData> EmptyPredictor::GeneratePrediction(
+ base::TimeTicks predict_time) const {
+ if (!HasPrediction())
+ return nullptr;
+ return std::make_unique<InputData>(last_input_.value());
+}
+
+base::TimeDelta EmptyPredictor::TimeInterval() const {
+ return kTimeInterval;
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/empty_predictor.h b/chromium/ui/base/prediction/empty_predictor.h
new file mode 100644
index 00000000000..408261bf596
--- /dev/null
+++ b/chromium/ui/base/prediction/empty_predictor.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_EMPTY_PREDICTOR_H_
+#define UI_BASE_PREDICTION_EMPTY_PREDICTOR_H_
+
+#include "base/component_export.h"
+#include "base/optional.h"
+#include "ui/base/prediction/input_predictor.h"
+
+namespace ui {
+
+// An empty predictor class. This will not generate any prediction.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) EmptyPredictor
+ : public InputPredictor {
+ public:
+ EmptyPredictor();
+ ~EmptyPredictor() override;
+
+ const char* GetName() const override;
+
+ void Reset() override;
+
+ // store the cur_input in last_input_
+ void Update(const InputData& cur_input) override;
+
+ // Always returns false;
+ bool HasPrediction() const override;
+
+ // Returns the last_input_ for testing.
+ std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks predict_time) const override;
+
+ // Returns kTimeInterval for testing.
+ base::TimeDelta TimeInterval() const override;
+
+ private:
+ // store the last_input_ point for testing
+ base::Optional<InputData> last_input_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmptyPredictor);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_EMPTY_PREDICTOR_H_
diff --git a/chromium/ui/base/prediction/input_filter.h b/chromium/ui/base/prediction/input_filter.h
new file mode 100644
index 00000000000..d6087be5f51
--- /dev/null
+++ b/chromium/ui/base/prediction/input_filter.h
@@ -0,0 +1,37 @@
+// 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 UI_BASE_PREDICTION_INPUT_FILTER_H_
+#define UI_BASE_PREDICTION_INPUT_FILTER_H_
+
+#include "base/component_export.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace ui {
+
+// This class expects a sequence of inputs with coordinates and timestamps to
+// return a smooth path from the sent coordinates.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) InputFilter {
+ public:
+ virtual ~InputFilter() = default;
+
+ // Filters the position sent to the filter at a specific timestamp.
+ // Returns true if the value is filtered, false otherwise.
+ virtual bool Filter(const base::TimeTicks& timestamp,
+ gfx::PointF* position) const = 0;
+
+ // Returns the name of the filter
+ virtual const char* GetName() const = 0;
+
+ // Returns a copy of the filter.
+ virtual InputFilter* Clone() = 0;
+
+ // Reset the filter to its initial state
+ virtual void Reset() = 0;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_INPUT_FILTER_H_
diff --git a/chromium/ui/base/prediction/input_filter_unittest_helpers.cc b/chromium/ui/base/prediction/input_filter_unittest_helpers.cc
new file mode 100644
index 00000000000..b02b9f0db2e
--- /dev/null
+++ b/chromium/ui/base/prediction/input_filter_unittest_helpers.cc
@@ -0,0 +1,83 @@
+// 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 "ui/base/prediction/input_filter_unittest_helpers.h"
+
+#include "base/rand_util.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+#include "ui/base/prediction/prediction_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+InputFilterTest::InputFilterTest() = default;
+InputFilterTest::~InputFilterTest() = default;
+
+// Check if the filter is well cloned. We send random values to the filter and
+// then we clone it. If we send the same new random values to both filters,
+// we should have the same filtered results
+void InputFilterTest::TestCloneFilter() {
+ gfx::PointF point;
+ base::TimeTicks ts = PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ base::TimeDelta delta = base::TimeDelta::FromMilliseconds(8);
+
+ for (int i = 0; i < 100; i++) {
+ point.SetPoint(base::RandDouble(), base::RandDouble());
+ EXPECT_TRUE(filter_->Filter(ts, &point)); // We just feed the filter
+ ts += delta;
+ }
+
+ std::unique_ptr<InputFilter> fork_filter;
+ fork_filter.reset(filter_->Clone());
+
+ gfx::PointF filtered_point, fork_filtered_point;
+ for (int i = 0; i < 100; i++) {
+ point.SetPoint(base::RandDouble(), base::RandDouble());
+ filtered_point = point;
+ fork_filtered_point = point;
+ EXPECT_TRUE(filter_->Filter(ts, &filtered_point));
+ EXPECT_TRUE(fork_filter->Filter(ts, &fork_filtered_point));
+ EXPECT_NEAR(filtered_point.x(), fork_filtered_point.x(), kEpsilon);
+ EXPECT_NEAR(filtered_point.y(), fork_filtered_point.y(), kEpsilon);
+ ts += delta;
+ }
+}
+
+// Check if the filter is well reset. We send random values, save the values and
+// results, then we reset the filter. We send again the same values and see if
+// we have the same results, which would be statistically impossible with 100
+// random without a proper resetting.
+void InputFilterTest::TestResetFilter() {
+ std::vector<gfx::PointF> points;
+ std::vector<base::TimeTicks> timestamps;
+ std::vector<gfx::PointF> results;
+ gfx::PointF point;
+ base::TimeTicks ts = PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ base::TimeDelta delta = base::TimeDelta::FromMilliseconds(8);
+
+ for (int i = 0; i < 100; i++) {
+ point.SetPoint(base::RandDouble(), base::RandDouble());
+ points.push_back(point);
+ timestamps.push_back(ts);
+ EXPECT_TRUE(filter_->Filter(ts, &point));
+ results.push_back(point);
+ ts += delta;
+ }
+
+ filter_->Reset();
+
+ EXPECT_EQ((int)points.size(), 100);
+ EXPECT_EQ((int)timestamps.size(), 100);
+ EXPECT_EQ((int)results.size(), 100);
+
+ for (int i = 0; i < 100; i++) {
+ point = points[i];
+ EXPECT_TRUE(filter_->Filter(timestamps[i], &point));
+ EXPECT_NEAR(results[i].x(), point.x(), kEpsilon);
+ EXPECT_NEAR(results[i].y(), point.y(), kEpsilon);
+ }
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/input_filter_unittest_helpers.h b/chromium/ui/base/prediction/input_filter_unittest_helpers.h
new file mode 100644
index 00000000000..2b26db1edb9
--- /dev/null
+++ b/chromium/ui/base/prediction/input_filter_unittest_helpers.h
@@ -0,0 +1,37 @@
+// 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 UI_BASE_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
+#define UI_BASE_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
+
+#include "ui/base/prediction/input_filter.h"
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+namespace test {
+
+constexpr double kEpsilon = 0.0001;
+
+// Base class for predictor unit tests
+class InputFilterTest : public testing::Test {
+ public:
+ InputFilterTest();
+ ~InputFilterTest() override;
+
+ void TestCloneFilter();
+
+ void TestResetFilter();
+
+ protected:
+ std::unique_ptr<InputFilter> filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputFilterTest);
+};
+
+} // namespace test
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_INPUT_FILTER_UNITTEST_HELPERS_H_
diff --git a/chromium/ui/base/prediction/input_predictor.h b/chromium/ui/base/prediction/input_predictor.h
new file mode 100644
index 00000000000..50b8a9c236c
--- /dev/null
+++ b/chromium/ui/base/prediction/input_predictor.h
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_INPUT_PREDICTOR_H_
+#define UI_BASE_PREDICTION_INPUT_PREDICTOR_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace ui {
+
+// This class expects a sequence of inputs with their coordinates and timestamps
+// and models the input path. It then can predict the coordinates at any given
+// time.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) InputPredictor {
+ public:
+ virtual ~InputPredictor() = default;
+
+ struct InputData {
+ gfx::PointF pos;
+ base::TimeTicks time_stamp;
+ InputData() {
+ pos = gfx::PointF();
+ time_stamp = base::TimeTicks();
+ }
+ InputData(const gfx::PointF& event_pos, const base::TimeTicks& event_time) {
+ pos = event_pos;
+ time_stamp = event_time;
+ }
+ };
+
+ // Returns the name of the predictor.
+ virtual const char* GetName() const = 0;
+
+ // Reset should be called each time when a new line start.
+ virtual void Reset() = 0;
+
+ // Update the predictor with new input points.
+ virtual void Update(const InputData& new_input) = 0;
+
+ // Return true if the predictor is able to predict points.
+ virtual bool HasPrediction() const = 0;
+
+ // Generate the prediction based on current points.
+ virtual std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks predict_time) const = 0;
+
+ // Returns the maximum of prediction available for resampling
+ // before having side effects (jitter, wrong orientation, etc..)
+ const base::TimeDelta MaxResampleTime() const { return kMaxResampleTime; }
+
+ // Returns the maximum prediction time available for the predictor
+ // before having side effects (jitter, wrong orientation, etc..)
+ const base::TimeDelta MaxPredictionTime() const { return kMaxPredictionTime; }
+
+ // Return the time interval based on current points.
+ virtual base::TimeDelta TimeInterval() const = 0;
+
+ protected:
+ static constexpr base::TimeDelta kMaxTimeDelta =
+ base::TimeDelta::FromMilliseconds(20);
+
+ // Default time interval between events.
+ static constexpr base::TimeDelta kTimeInterval =
+ base::TimeDelta::FromMilliseconds(8);
+ // Minimum time interval between events.
+ static constexpr base::TimeDelta kMinTimeInterval =
+ base::TimeDelta::FromMillisecondsD(2.5);
+
+ // Maximum amount of prediction when resampling.
+ static constexpr base::TimeDelta kMaxResampleTime =
+ base::TimeDelta::FromMilliseconds(20);
+ // Maximum time delta for prediction.
+ static constexpr base::TimeDelta kMaxPredictionTime =
+ base::TimeDelta::FromMilliseconds(25);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_INPUT_PREDICTOR_H_
diff --git a/chromium/ui/base/prediction/input_predictor_unittest_helpers.cc b/chromium/ui/base/prediction/input_predictor_unittest_helpers.cc
new file mode 100644
index 00000000000..304d77e9e90
--- /dev/null
+++ b/chromium/ui/base/prediction/input_predictor_unittest_helpers.cc
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+
+namespace ui {
+
+InputPredictorTest::InputPredictorTest() = default;
+InputPredictorTest::~InputPredictorTest() = default;
+
+void InputPredictorTest::ValidatePredictor(
+ const std::vector<double>& x,
+ const std::vector<double>& y,
+ const std::vector<double>& timestamp_ms) {
+ predictor_->Reset();
+ for (size_t i = 0; i < timestamp_ms.size(); i++) {
+ if (predictor_->HasPrediction()) {
+ auto result =
+ predictor_->GeneratePrediction(FromMilliseconds(timestamp_ms[i]));
+ EXPECT_TRUE(result);
+ EXPECT_NEAR(result->pos.x(), x[i], kEpsilon);
+ EXPECT_NEAR(result->pos.y(), y[i], kEpsilon);
+ }
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(timestamp_ms[i])};
+ predictor_->Update(data);
+ }
+}
+
+void InputPredictorTest::ValidatePredictor(
+ const std::vector<double>& events_x,
+ const std::vector<double>& events_y,
+ const std::vector<double>& events_time_ms,
+ const std::vector<double>& prediction_time_ms,
+ const std::vector<double>& predicted_x,
+ const std::vector<double>& predicted_y) {
+ predictor_->Reset();
+ std::vector<double> computed_x;
+ std::vector<double> computed_y;
+ size_t current_prediction_index = 0;
+ for (size_t i = 0; i < events_time_ms.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(events_x[i], events_y[i]),
+ FromMilliseconds(events_time_ms[i])};
+ predictor_->Update(data);
+
+ if (predictor_->HasPrediction()) {
+ auto result = predictor_->GeneratePrediction(
+ FromMilliseconds(prediction_time_ms[current_prediction_index]));
+ EXPECT_TRUE(result);
+ computed_x.push_back(result->pos.x());
+ computed_y.push_back(result->pos.y());
+ EXPECT_GT(result->time_stamp, base::TimeTicks());
+ current_prediction_index++;
+ }
+ }
+
+ EXPECT_TRUE(computed_x.size() == predicted_x.size());
+ if (computed_x.size() == predicted_x.size()) {
+ for (size_t i = 0; i < predicted_x.size(); i++) {
+ EXPECT_NEAR(computed_x[i], predicted_x[i], kEpsilon);
+ EXPECT_NEAR(computed_y[i], predicted_y[i], kEpsilon);
+ }
+ }
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/input_predictor_unittest_helpers.h b/chromium/ui/base/prediction/input_predictor_unittest_helpers.h
new file mode 100644
index 00000000000..db2dadfa896
--- /dev/null
+++ b/chromium/ui/base/prediction/input_predictor_unittest_helpers.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_
+#define UI_BASE_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor.h"
+#include "ui/base/prediction/prediction_unittest_helpers.h"
+
+namespace ui {
+
+constexpr base::TimeDelta kExpectedDefaultTimeInterval =
+ base::TimeDelta::FromMilliseconds(8);
+
+// Base class for predictor unit tests
+class InputPredictorTest : public testing::Test {
+ public:
+ InputPredictorTest();
+ ~InputPredictorTest() override;
+
+ static base::TimeTicks FromMilliseconds(int64_t ms) {
+ return test::PredictionUnittestHelpers::GetStaticTimeStampForTests() +
+ base::TimeDelta::FromMilliseconds(ms);
+ }
+
+ void ValidatePredictor(const std::vector<double>& x,
+ const std::vector<double>& y,
+ const std::vector<double>& timestamp_ms);
+
+ void ValidatePredictor(const std::vector<double>& events_x,
+ const std::vector<double>& events_y,
+ const std::vector<double>& events_ts_ms,
+ const std::vector<double>& prediction_ts_ms,
+ const std::vector<double>& predicted_x,
+ const std::vector<double>& predicted_y);
+
+ protected:
+ static constexpr double kEpsilon = 0.1;
+
+ std::unique_ptr<InputPredictor> predictor_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputPredictorTest);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_
diff --git a/chromium/ui/base/prediction/kalman_filter.cc b/chromium/ui/base/prediction/kalman_filter.cc
new file mode 100644
index 00000000000..d1b2760066a
--- /dev/null
+++ b/chromium/ui/base/prediction/kalman_filter.cc
@@ -0,0 +1,124 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/kalman_filter.h"
+
+#include "base/check_op.h"
+
+namespace {
+constexpr uint32_t kStableIterNum = 4;
+
+constexpr double kSigmaProcess = 0.01;
+constexpr double kSigmaMeasurement = 1.0;
+
+gfx::Matrix3F GetStateTransition(double kDt) {
+ gfx::Matrix3F state_transition = gfx::Matrix3F::Zeros();
+ // State translation matrix is basic physics.
+ // new_pos = pre_pos + v * dt + 1/2 * a * dt^2.
+ // new_v = v + a * dt.
+ // new_a = a .
+ state_transition.set(1.0, kDt, 0.5 * kDt * kDt, 0.0, 1.0, kDt, 0.0, 0.0, 1.0);
+ return state_transition;
+}
+
+gfx::Matrix3F GetProcessNoise(double kDt) {
+ gfx::Vector3dF process_noise(0.5 * kDt * kDt, kDt, 1.0);
+ // We model the system noise as noisy force on the pointer.
+ // The following matrix describes the impact of that noise on each state.
+ gfx::Matrix3F process_noise_covariance = gfx::Matrix3F::FromOuterProduct(
+ process_noise, gfx::ScaleVector3d(process_noise, kSigmaProcess));
+ return process_noise_covariance;
+}
+
+} // namespace
+
+namespace ui {
+
+KalmanFilter::KalmanFilter()
+ : state_estimation_(gfx::Vector3dF()),
+ error_covariance_matrix_(gfx::Matrix3F::Identity()),
+ state_transition_matrix_(GetStateTransition(1.0)),
+ process_noise_covariance_matrix_(GetProcessNoise(1.0)),
+ measurement_vector_(gfx::Vector3dF(1.0, 0.0, 0.0)),
+ measurement_noise_variance_(kSigmaMeasurement),
+ iteration_count_(0) {}
+
+KalmanFilter::~KalmanFilter() = default;
+
+const gfx::Vector3dF& KalmanFilter::GetStateEstimation() const {
+ return state_estimation_;
+}
+
+bool KalmanFilter::Stable() const {
+ return iteration_count_ >= kStableIterNum;
+}
+
+void KalmanFilter::Update(double observation, double dt) {
+ if (iteration_count_++ == 0) {
+ // We only update the state estimation in the first iteration.
+ state_estimation_ = gfx::Vector3dF(observation, 0, 0);
+ return;
+ }
+ Predict(dt);
+ // Y = z - H * X
+ double y =
+ observation - gfx::DotProduct(measurement_vector_, state_estimation_);
+ // S = H * P * H' + R
+ double S = gfx::DotProduct(measurement_vector_,
+ gfx::MatrixProduct(error_covariance_matrix_,
+ measurement_vector_)) +
+ measurement_noise_variance_;
+ // K = P * H' * inv(S)
+ gfx::Vector3dF kalman_gain = gfx::ScaleVector3d(
+ gfx::MatrixProduct(error_covariance_matrix_, measurement_vector_),
+ 1.0 / S);
+ // X = X + K * Y
+ state_estimation_ += gfx::ScaleVector3d(kalman_gain, y);
+ // I_HK = eye(P) - K * H
+ gfx::Matrix3F I_KH =
+ gfx::Matrix3F::Identity() -
+ gfx::Matrix3F::FromOuterProduct(kalman_gain, measurement_vector_);
+
+ // P = I_KH * P * I_KH' + K * R * K'
+ error_covariance_matrix_ =
+ gfx::MatrixProduct(gfx::MatrixProduct(I_KH, error_covariance_matrix_),
+ I_KH.Transpose()) +
+ gfx::Matrix3F::FromOuterProduct(
+ gfx::ScaleVector3d(kalman_gain, measurement_noise_variance_),
+ kalman_gain);
+}
+
+void KalmanFilter::Reset() {
+ state_estimation_ = gfx::Vector3dF();
+ error_covariance_matrix_ = gfx::Matrix3F::Identity();
+ iteration_count_ = 0;
+}
+
+double KalmanFilter::GetPosition() const {
+ return GetStateEstimation().x();
+}
+
+double KalmanFilter::GetVelocity() const {
+ return GetStateEstimation().y();
+}
+
+double KalmanFilter::GetAcceleration() const {
+ return GetStateEstimation().z();
+}
+
+void KalmanFilter::Predict(double dt) {
+ DCHECK_GT(iteration_count_, 0u);
+ state_transition_matrix_ = GetStateTransition(dt);
+ // X = F * X
+ state_estimation_ =
+ gfx::MatrixProduct(state_transition_matrix_, state_estimation_);
+ // P = F * P * F' + Q
+ error_covariance_matrix_ =
+ gfx::MatrixProduct(gfx::MatrixProduct(state_transition_matrix_,
+ error_covariance_matrix_),
+ state_transition_matrix_.Transpose()) +
+ GetProcessNoise(dt);
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/kalman_filter.h b/chromium/ui/base/prediction/kalman_filter.h
new file mode 100644
index 00000000000..0571cdb4ef6
--- /dev/null
+++ b/chromium/ui/base/prediction/kalman_filter.h
@@ -0,0 +1,86 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_KALMAN_FILTER_H_
+#define UI_BASE_PREDICTION_KALMAN_FILTER_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "ui/gfx/geometry/matrix3_f.h"
+
+namespace ui {
+
+// This Kalman filter is used to predict state in one axles.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) KalmanFilter {
+ public:
+ KalmanFilter();
+ ~KalmanFilter();
+
+ // Get the estimation of current state.
+ const gfx::Vector3dF& GetStateEstimation() const;
+
+ // Will return true only if the kalman filter has seen enough data and is
+ // considered as stable.
+ bool Stable() const;
+
+ // Update the observation of the system.
+ void Update(double observation, double dt);
+
+ void Reset();
+
+ // Get the predicted values from the kalman filter.
+ double GetPosition() const;
+ double GetVelocity() const;
+ double GetAcceleration() const;
+
+ private:
+ void Predict(double dt);
+
+ // Estimate of the latent state
+ // Symbol: X
+ // Dimension: state_vector_dim_
+ gfx::Vector3dF state_estimation_;
+
+ // The covariance of the difference between prior predicted latent
+ // state and posterior estimated latent state (the so-called "innovation".
+ // Symbol: P
+ // Dimension: state_vector_dim_, state_vector_dim_
+ gfx::Matrix3F error_covariance_matrix_;
+
+ // For position, state transition matrix is derived from basic physics:
+ // new_x = x + v * dt + 1/2 * a * dt^2
+ // new_v = v + a * dt
+ // ...
+ // Matrix that transforms current state to next state
+ // Symbol: F
+ // Dimension: state_vector_dim_, state_vector_dim_
+ gfx::Matrix3F state_transition_matrix_;
+
+ // A time-varying noise parameter that will be estimated as part of the
+ // kalman filter process.
+ // Symbol: Q
+ // Dimension: state_vector_dim_, state_vector_dim_
+ gfx::Matrix3F process_noise_covariance_matrix_;
+
+ // Vector to transform estimate to measurement.
+ // Symbol: H
+ // Dimension: state_vector_dim_
+ const gfx::Vector3dF measurement_vector_;
+
+ // A time-varying noise parameter that will be estimated as part of the
+ // kalman filter process.
+ // Symbol: R
+ double measurement_noise_variance_;
+
+ // Tracks number of update iteration happened at this kalman filter. At the
+ // 1st iteration, the state estimate will be updated to the measured value.
+ // After a few iterations, the KalmanFilter is considered stable.
+ uint32_t iteration_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(KalmanFilter);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_KALMAN_FILTER_H_
diff --git a/chromium/ui/base/prediction/kalman_predictor.cc b/chromium/ui/base/prediction/kalman_predictor.cc
new file mode 100644
index 00000000000..df359f5da65
--- /dev/null
+++ b/chromium/ui/base/prediction/kalman_predictor.cc
@@ -0,0 +1,139 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/kalman_predictor.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/numerics/math_constants.h"
+#include "ui/base/ui_base_features.h"
+
+namespace {
+
+// Influence of acceleration during each prediction sample
+constexpr float kAccelerationInfluence = 0.5f;
+// Influence of velocity during each prediction sample
+constexpr float kVelocityInfluence = 1.0f;
+
+} // namespace
+
+namespace ui {
+
+constexpr base::TimeDelta InputPredictor::kMaxTimeDelta;
+constexpr base::TimeDelta InputPredictor::kMaxResampleTime;
+constexpr base::TimeDelta InputPredictor::kMaxPredictionTime;
+constexpr base::TimeDelta InputPredictor::kTimeInterval;
+constexpr base::TimeDelta InputPredictor::kMinTimeInterval;
+constexpr base::TimeDelta KalmanPredictor::kMaxTimeInQueue;
+
+KalmanPredictor::KalmanPredictor(unsigned int prediction_options)
+ : prediction_options_(prediction_options) {}
+
+KalmanPredictor::~KalmanPredictor() = default;
+
+const char* KalmanPredictor::GetName() const {
+ return features::kPredictorNameKalman;
+}
+
+void KalmanPredictor::Reset() {
+ x_predictor_.Reset();
+ y_predictor_.Reset();
+ last_points_.clear();
+ time_filter_.Reset();
+}
+
+void KalmanPredictor::Update(const InputData& cur_input) {
+ base::TimeDelta dt;
+ if (last_points_.size()) {
+ // When last point is kMaxTimeDelta away, consider it is incontinuous.
+ dt = cur_input.time_stamp - last_points_.back().time_stamp;
+ if (dt > kMaxTimeDelta)
+ Reset();
+ else
+ time_filter_.Update(dt.InMillisecondsF(), 0);
+ }
+
+ double dt_ms = time_filter_.GetPosition();
+ last_points_.push_back(cur_input);
+ x_predictor_.Update(cur_input.pos.x(), dt_ms);
+ y_predictor_.Update(cur_input.pos.y(), dt_ms);
+
+ while (last_points_.back().time_stamp - last_points_.front().time_stamp >
+ kMaxTimeInQueue) {
+ last_points_.pop_front();
+ }
+}
+
+bool KalmanPredictor::HasPrediction() const {
+ return x_predictor_.Stable() && y_predictor_.Stable();
+}
+
+std::unique_ptr<InputPredictor::InputData> KalmanPredictor::GeneratePrediction(
+ base::TimeTicks predict_time) const {
+ if (!HasPrediction())
+ return nullptr;
+
+ DCHECK(last_points_.size());
+ float pred_dt =
+ (predict_time - last_points_.back().time_stamp).InMillisecondsF();
+
+ gfx::PointF position(last_points_.back().pos.x(),
+ last_points_.back().pos.y());
+ gfx::Vector2dF velocity = PredictVelocity();
+ gfx::Vector2dF acceleration = PredictAcceleration();
+
+ if (prediction_options_ & kDirectionCutOffEnabled) {
+ gfx::Vector2dF future_velocity =
+ velocity + ScaleVector2d(acceleration, pred_dt);
+ if (gfx::DotProduct(velocity, future_velocity) <= 0)
+ return nullptr;
+ }
+
+ position += ScaleVector2d(velocity, kVelocityInfluence * pred_dt);
+
+ if (prediction_options_ & kHeuristicsEnabled) {
+ float points_angle = 0.0f;
+ for (size_t i = 2; i < last_points_.size(); i++) {
+ gfx::Vector2dF first_dir =
+ last_points_[i - 1].pos - last_points_[i - 2].pos;
+ gfx::Vector2dF second_dir = last_points_[i].pos - last_points_[i - 1].pos;
+ if (first_dir.Length() && second_dir.Length()) {
+ points_angle += atan2(first_dir.x(), first_dir.y()) -
+ atan2(second_dir.x(), second_dir.y());
+ }
+ }
+ if (fabsf(points_angle) * 180 / base::kPiDouble > 15) {
+ position += ScaleVector2d(acceleration,
+ kAccelerationInfluence * pred_dt * pred_dt);
+ }
+ } else {
+ position +=
+ ScaleVector2d(acceleration, kAccelerationInfluence * pred_dt * pred_dt);
+ }
+
+ return std::make_unique<InputData>(position, predict_time);
+}
+
+base::TimeDelta KalmanPredictor::TimeInterval() const {
+ return time_filter_.GetPosition()
+ ? std::max(kMinTimeInterval, base::TimeDelta::FromMilliseconds(
+ time_filter_.GetPosition()))
+ : kTimeInterval;
+}
+
+gfx::Vector2dF KalmanPredictor::PredictPosition() const {
+ return gfx::Vector2dF(x_predictor_.GetPosition(), y_predictor_.GetPosition());
+}
+
+gfx::Vector2dF KalmanPredictor::PredictVelocity() const {
+ return gfx::Vector2dF(x_predictor_.GetVelocity(), y_predictor_.GetVelocity());
+}
+
+gfx::Vector2dF KalmanPredictor::PredictAcceleration() const {
+ return gfx::Vector2dF(x_predictor_.GetAcceleration(),
+ y_predictor_.GetAcceleration());
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/kalman_predictor.h b/chromium/ui/base/prediction/kalman_predictor.h
new file mode 100644
index 00000000000..89d7b466b21
--- /dev/null
+++ b/chromium/ui/base/prediction/kalman_predictor.h
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_KALMAN_PREDICTOR_H_
+#define UI_BASE_PREDICTION_KALMAN_PREDICTOR_H_
+
+#include <deque>
+#include <vector>
+
+#include "base/component_export.h"
+#include "ui/base/prediction/input_predictor.h"
+#include "ui/base/prediction/kalman_filter.h"
+
+namespace ui {
+
+// Class to perform kalman filter prediction inherited from InputPredictor.
+// This predictor uses kalman filters to predict the current status of the
+// motion. Then it predict the future points using <current_position,
+// predicted_velocity, predicted_acceleration>. Each kalman_filter will only
+// be used to predict one dimension (x, y).
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) KalmanPredictor
+ : public InputPredictor {
+ public:
+ // Heuristic option enables changing the influence of acceleration based on
+ // change of direction. Direction cut off enables discarding the prediction if
+ // the predicted direction is opposite from the current direction.
+ enum PredictionOptions {
+ kNone = 0x0,
+ kHeuristicsEnabled = 0x1,
+ kDirectionCutOffEnabled = 0x2
+ };
+
+ explicit KalmanPredictor(unsigned int prediction_options);
+ ~KalmanPredictor() override;
+
+ const char* GetName() const override;
+
+ // Reset the predictor to initial state.
+ void Reset() override;
+
+ // Store current input in queue.
+ void Update(const InputData& cur_input) override;
+
+ // Return if there is enough data in the queue to generate prediction.
+ bool HasPrediction() const override;
+
+ // Generate the prediction based on stored points and given time_stamp.
+ // Return false if no prediction available.
+ std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks predict_time) const override;
+
+ // Return the filtered value of time intervals.
+ base::TimeDelta TimeInterval() const override;
+
+ private:
+ // The following functions get the predicted values from kalman filters.
+ gfx::Vector2dF PredictPosition() const;
+ gfx::Vector2dF PredictVelocity() const;
+ gfx::Vector2dF PredictAcceleration() const;
+
+ // Predictor for each axis.
+ KalmanFilter x_predictor_;
+ KalmanFilter y_predictor_;
+
+ // Filter to smooth time intervals.
+ KalmanFilter time_filter_;
+
+ // Most recent input data.
+ std::deque<InputData> last_points_;
+
+ // Maximum time interval between first and last events in last points queue.
+ static constexpr base::TimeDelta kMaxTimeInQueue =
+ base::TimeDelta::FromMilliseconds(40);
+
+ // Flags to determine the enabled prediction options.
+ const unsigned int prediction_options_;
+
+ DISALLOW_COPY_AND_ASSIGN(KalmanPredictor);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_KALMAN_PREDICTOR_H_
diff --git a/chromium/ui/base/prediction/kalman_predictor_unittest.cc b/chromium/ui/base/prediction/kalman_predictor_unittest.cc
new file mode 100644
index 00000000000..9ae1a762e10
--- /dev/null
+++ b/chromium/ui/base/prediction/kalman_predictor_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+#include "ui/base/prediction/kalman_predictor.h"
+
+namespace ui {
+namespace test {
+namespace {
+
+constexpr uint32_t kExpectedStableIterNum = 4;
+
+struct DataSet {
+ double initial_observation;
+ std::vector<double> observation;
+ std::vector<double> position;
+ std::vector<double> velocity;
+ std::vector<double> acceleration;
+};
+
+void ValidateSingleKalmanFilter(const DataSet& data) {
+ std::unique_ptr<KalmanFilter> kalman_filter =
+ std::make_unique<KalmanFilter>();
+ constexpr double kEpsilon = 0.001;
+ constexpr double kDtMillisecond = 8;
+ kalman_filter->Update(data.initial_observation, kDtMillisecond);
+ for (size_t i = 0; i < data.observation.size(); i++) {
+ kalman_filter->Update(data.observation[i], kDtMillisecond);
+ EXPECT_NEAR(data.position[i], kalman_filter->GetPosition(), kEpsilon);
+ EXPECT_NEAR(data.velocity[i], kalman_filter->GetVelocity(), kEpsilon);
+ EXPECT_NEAR(data.acceleration[i], kalman_filter->GetAcceleration(),
+ kEpsilon);
+ }
+}
+
+} // namespace
+
+class KalmanPredictorTest : public InputPredictorTest {
+ public:
+ explicit KalmanPredictorTest() {}
+
+ void SetUp() override {
+ predictor_ = std::make_unique<KalmanPredictor>(
+ KalmanPredictor::PredictionOptions::kNone);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(KalmanPredictorTest);
+};
+
+// Test the a single axle kalman filter behavior with preset datas.
+TEST_F(KalmanPredictorTest, KalmanFilterPredictedValue) {
+ DataSet data;
+ data.initial_observation = 0;
+ data.observation = {1, 2, 3, 4, 5, 6};
+ data.position = {0.999, 2.007, 3.001, 3.999, 5.000, 6.000};
+ data.velocity = {0.242, 0.130, 0.122, 0.124, 0.125, 0.125};
+ data.acceleration = {0.029, 0.000, 0.000, 0.000, 0.000, 0.000};
+ ValidateSingleKalmanFilter(data);
+
+ data.initial_observation = 0;
+ data.observation = {1, 2, 4, 8, 16, 32};
+ data.position = {0.999, 2.007, 3.976, 7.970, 15.950, 31.896};
+ data.velocity = {0.242, 0.130, 0.298, 0.623, 1.240, 2.475};
+ data.acceleration = {0.029, 0.000, 0.015, 0.034, 0.065, 0.130};
+ ValidateSingleKalmanFilter(data);
+}
+
+TEST_F(KalmanPredictorTest, ShouldHasPrediction) {
+ for (uint32_t i = 0; i < kExpectedStableIterNum; i++) {
+ EXPECT_FALSE(predictor_->HasPrediction());
+ InputPredictor::InputData data = {gfx::PointF(1, 1),
+ FromMilliseconds(8 * i)};
+ predictor_->Update(data);
+ }
+ EXPECT_TRUE(predictor_->HasPrediction());
+
+ predictor_->Reset();
+ EXPECT_FALSE(predictor_->HasPrediction());
+}
+
+// Tests the kalman predictor constant position.
+TEST_F(KalmanPredictorTest, PredictConstantValue) {
+ std::vector<double> x = {50, 50, 50, 50, 50, 50};
+ std::vector<double> y = {-50, -50, -50, -50, -50, -50};
+ std::vector<double> t = {8, 16, 24, 32, 40, 48};
+ ValidatePredictor(x, y, t);
+}
+
+// Tests the kalman predictor predict constant velocity.
+TEST_F(KalmanPredictorTest, PredictLinearValue) {
+ // The kalman filter is initialized with a velocity of zero. The change of
+ // velocity from zero to the constant value will be attributed to
+ // acceleration. Given how the filter works, it will take a few updates for it
+ // to get accustomed to a constant velocity.
+ std::vector<double> x_stabilizer = {-40, -32, -24, -16, -8};
+ std::vector<double> y_stabilizer = {-10, -2, 6, 14, 22};
+ std::vector<double> t_stabilizer = {-40, -32, -24, -16, -8};
+ for (size_t i = 0; i < t_stabilizer.size(); i++) {
+ InputPredictor::InputData data = {
+ gfx::PointF(x_stabilizer[i], y_stabilizer[i]),
+ FromMilliseconds(t_stabilizer[i])};
+ predictor_->Update(data);
+ }
+
+ std::vector<double> x = {0, 8, 16, 24, 32, 40, 48, 60};
+ std::vector<double> y = {30, 38, 46, 54, 62, 70, 78, 90};
+ std::vector<double> t = {0, 8, 16, 24, 32, 40, 48, 60};
+ for (size_t i = 0; i < t.size(); i++) {
+ if (predictor_->HasPrediction()) {
+ auto result = predictor_->GeneratePrediction(FromMilliseconds(t[i]));
+ EXPECT_TRUE(result);
+ EXPECT_NEAR(result->pos.x(), x[i], kEpsilon);
+ EXPECT_NEAR(result->pos.y(), y[i], kEpsilon);
+ }
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+}
+
+// Tests the kalman predictor predict constant acceleration.
+TEST_F(KalmanPredictorTest, PredictQuadraticValue) {
+ std::vector<double> x = {0, 2, 8, 18, 32, 50, 72, 98};
+ std::vector<double> y = {10, 11, 14, 19, 26, 35, 46, 59};
+ std::vector<double> t = {8, 16, 24, 32, 40, 48, 56, 64};
+ ValidatePredictor(x, y, t);
+}
+
+// Tests the kalman predictor time interval filter.
+TEST_F(KalmanPredictorTest, TimeInterval) {
+ EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
+ std::vector<double> x = {0, 2, 8, 18};
+ std::vector<double> y = {10, 11, 14, 19};
+ std::vector<double> t = {7, 14, 21, 28};
+ for (size_t i = 0; i < t.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+ EXPECT_EQ(predictor_->TimeInterval().InMillisecondsF(),
+ base::TimeDelta::FromMilliseconds(7).InMillisecondsF());
+}
+
+// Test the benefit from the heuristic approach on noisy data.
+TEST_F(KalmanPredictorTest, HeuristicApproach) {
+ std::unique_ptr<InputPredictor> heuristic_predictor =
+ std::make_unique<KalmanPredictor>(
+ KalmanPredictor::PredictionOptions::kHeuristicsEnabled);
+ std::vector<double> x_stabilizer = {-40, -32, -24, -16, -8, 0};
+ std::vector<double> y_stabilizer = {-40, -32, -24, -16, -8, 0};
+ std::vector<double> t_stabilizer = {-40, -32, -24, -16, -8, 0};
+ for (size_t i = 0; i < t_stabilizer.size(); i++) {
+ InputPredictor::InputData data = {
+ gfx::PointF(x_stabilizer[i], y_stabilizer[i]),
+ FromMilliseconds(t_stabilizer[i])};
+ predictor_->Update(data);
+ heuristic_predictor->Update(data);
+ }
+
+ std::vector<double> x = {7, 17, 23, 33, 39, 49, 60};
+ std::vector<double> y = {9, 15, 25, 31, 41, 47, 60};
+ std::vector<double> t = {8, 16, 24, 32, 40, 48, 60};
+ for (size_t i = 0; i < t.size(); i++) {
+ gfx::PointF point(x[i], y[i]);
+ if (heuristic_predictor->HasPrediction() && predictor_->HasPrediction()) {
+ auto heuristic_result =
+ heuristic_predictor->GeneratePrediction(FromMilliseconds(t[i]));
+ auto result = predictor_->GeneratePrediction(FromMilliseconds(t[i]));
+ EXPECT_TRUE(heuristic_result);
+ EXPECT_TRUE(result);
+ EXPECT_LE((heuristic_result->pos - point).Length(),
+ (result->pos - point).Length());
+ }
+ InputPredictor::InputData data = {point, FromMilliseconds(t[i])};
+ heuristic_predictor->Update(data);
+ predictor_->Update(data);
+ }
+}
+
+// Test the kalman predictor prevention of rubber-banding.
+TEST_F(KalmanPredictorTest, DirectionalCutOff) {
+ predictor_ = std::make_unique<KalmanPredictor>(
+ KalmanPredictor::PredictionOptions::kDirectionCutOffEnabled);
+ std::vector<double> x = {98, 72, 50, 32, 18, 8, 2};
+ std::vector<double> y = {49, 36, 25, 16, 9, 4, 1};
+ std::vector<double> t = {8, 16, 24, 32, 40, 48, 56};
+ for (size_t i = 0; i < t.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+ // On t=64, position is (0,0), and in t=72 it is (2,1) again which means that
+ // direction has shifted in the opposite direction and prediction should be
+ // cut off.
+ auto result = predictor_->GeneratePrediction(FromMilliseconds(72));
+ EXPECT_FALSE(result);
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/least_squares_predictor.cc b/chromium/ui/base/prediction/least_squares_predictor.cc
new file mode 100644
index 00000000000..ab3c37c1246
--- /dev/null
+++ b/chromium/ui/base/prediction/least_squares_predictor.cc
@@ -0,0 +1,113 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/least_squares_predictor.h"
+
+#include <algorithm>
+
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+namespace {
+
+// Solve XB = y.
+static bool SolveLeastSquares(const gfx::Matrix3F& x,
+ const std::deque<double>& y,
+ gfx::Vector3dF& result) {
+ constexpr double kEpsilon = std::numeric_limits<double>::epsilon();
+
+ // return last point if y didn't change.
+ if (std::abs(y[0] - y[1]) < kEpsilon && std::abs(y[1] - y[2]) < kEpsilon) {
+ result = gfx::Vector3dF(y[2], 0, 0);
+ return true;
+ }
+
+ gfx::Matrix3F x_transpose = x.Transpose();
+ gfx::Matrix3F temp = gfx::MatrixProduct(x_transpose, x).Inverse();
+
+ // Return false if x is singular.
+ if (temp == gfx::Matrix3F::Zeros())
+ return false;
+
+ result = gfx::MatrixProduct(gfx::MatrixProduct(temp, x_transpose),
+ gfx::Vector3dF(y[0], y[1], y[2]));
+ return true;
+}
+
+} // namespace
+
+LeastSquaresPredictor::LeastSquaresPredictor() {}
+
+LeastSquaresPredictor::~LeastSquaresPredictor() {}
+
+const char* LeastSquaresPredictor::GetName() const {
+ return features::kPredictorNameLsq;
+}
+
+void LeastSquaresPredictor::Reset() {
+ x_queue_.clear();
+ y_queue_.clear();
+ time_.clear();
+}
+
+void LeastSquaresPredictor::Update(const InputData& cur_input) {
+ if (!time_.empty()) {
+ // When last point is kMaxTimeDelta away, consider it is incontinuous.
+ if (cur_input.time_stamp - time_.back() > kMaxTimeDelta)
+ Reset();
+ }
+
+ x_queue_.push_back(cur_input.pos.x());
+ y_queue_.push_back(cur_input.pos.y());
+ time_.push_back(cur_input.time_stamp);
+ if (time_.size() > kSize) {
+ x_queue_.pop_front();
+ y_queue_.pop_front();
+ time_.pop_front();
+ }
+}
+
+bool LeastSquaresPredictor::HasPrediction() const {
+ return time_.size() >= kSize;
+}
+
+gfx::Matrix3F LeastSquaresPredictor::GetXMatrix() const {
+ gfx::Matrix3F x = gfx::Matrix3F::Zeros();
+ double t1 = (time_[1] - time_[0]).InMillisecondsF();
+ double t2 = (time_[2] - time_[0]).InMillisecondsF();
+ x.set(1, 0, 0, 1, t1, t1 * t1, 1, t2, t2 * t2);
+ return x;
+}
+
+std::unique_ptr<InputPredictor::InputData>
+LeastSquaresPredictor::GeneratePrediction(base::TimeTicks predict_time) const {
+ if (!HasPrediction())
+ return nullptr;
+
+ float pred_dt = (predict_time - time_[0]).InMillisecondsF();
+
+ gfx::Vector3dF b1, b2;
+ gfx::Matrix3F time_matrix = GetXMatrix();
+ if (SolveLeastSquares(time_matrix, x_queue_, b1) &&
+ SolveLeastSquares(time_matrix, y_queue_, b2)) {
+ gfx::Vector3dF prediction_time(1, pred_dt, pred_dt * pred_dt);
+
+ return std::make_unique<InputData>(
+ gfx::PointF(gfx::DotProduct(prediction_time, b1),
+ gfx::DotProduct(prediction_time, b2)),
+ predict_time);
+ }
+ return nullptr;
+}
+
+base::TimeDelta LeastSquaresPredictor::TimeInterval() const {
+ if (time_.size() > 1) {
+ return std::max(kMinTimeInterval,
+ (time_.back() - time_.front()) / (time_.size() - 1));
+ }
+ return kTimeInterval;
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/least_squares_predictor.h b/chromium/ui/base/prediction/least_squares_predictor.h
new file mode 100644
index 00000000000..12b9f1ce9fe
--- /dev/null
+++ b/chromium/ui/base/prediction/least_squares_predictor.h
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_PREDICTION_LEAST_SQUARES_PREDICTOR_H_
+#define UI_BASE_PREDICTION_LEAST_SQUARES_PREDICTOR_H_
+
+#include <deque>
+
+#include "base/component_export.h"
+#include "ui/base/prediction/input_predictor.h"
+#include "ui/gfx/geometry/matrix3_f.h"
+
+namespace ui {
+
+// This class use a quadratic least square regression model:
+// y = b0 + b1 * x + b2 * x ^ 2.
+// See https://en.wikipedia.org/wiki/Linear_least_squares_(mathematics)
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) LeastSquaresPredictor
+ : public InputPredictor {
+ public:
+ static constexpr size_t kSize = 3;
+
+ explicit LeastSquaresPredictor();
+ ~LeastSquaresPredictor() override;
+
+ const char* GetName() const override;
+
+ // Reset the predictor to initial state.
+ void Reset() override;
+
+ // Store current input in queue.
+ void Update(const InputData& cur_input) override;
+
+ // Return if there is enough data in the queue to generate prediction.
+ bool HasPrediction() const override;
+
+ // Generate the prediction based on stored points and given time_stamp.
+ // Return false if no prediction available.
+ std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks predict_time) const override;
+
+ // Return the averaged value of time intervals.
+ base::TimeDelta TimeInterval() const override;
+
+ private:
+ // Generate X matrix from time_ queue.
+ gfx::Matrix3F GetXMatrix() const;
+
+ std::deque<double> x_queue_;
+ std::deque<double> y_queue_;
+ std::deque<base::TimeTicks> time_;
+
+ DISALLOW_COPY_AND_ASSIGN(LeastSquaresPredictor);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_LEAST_SQUARES_PREDICTOR_H_
diff --git a/chromium/ui/base/prediction/least_squares_predictor_unittest.cc b/chromium/ui/base/prediction/least_squares_predictor_unittest.cc
new file mode 100644
index 00000000000..5d1749b0b73
--- /dev/null
+++ b/chromium/ui/base/prediction/least_squares_predictor_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/prediction/least_squares_predictor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+class LSQPredictorTest : public InputPredictorTest {
+ public:
+ explicit LSQPredictorTest() {}
+
+ void SetUp() override {
+ predictor_ = std::make_unique<LeastSquaresPredictor>();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LSQPredictorTest);
+};
+
+TEST_F(LSQPredictorTest, ShouldHasPrediction) {
+ LeastSquaresPredictor predictor;
+ for (size_t i = 0; i < LeastSquaresPredictor::kSize; i++) {
+ // First |kSize| point do not have prediction available.
+ EXPECT_FALSE(predictor.HasPrediction());
+ InputPredictor::InputData data = {gfx::PointF(1, 1),
+ FromMilliseconds(8 * i)};
+ predictor.Update(data);
+ }
+ EXPECT_TRUE(predictor.HasPrediction());
+}
+
+// Tests the lest squares filter behavior.
+// The data set is generated by a "known to work" quadratic fit.
+TEST_F(LSQPredictorTest, PredictedValue) {
+ std::vector<double> x = {22, 58, 102, 108.094};
+ std::vector<double> y = {100, 100, 100, 100};
+ std::vector<double> t = {13, 21, 37, 42};
+ ValidatePredictor(x, y, t);
+
+ x = {100, 100, 101, 104.126};
+ y = {120, 280, 600, 1364.93};
+ t = {101, 126, 148, 180};
+ ValidatePredictor(x, y, t);
+}
+
+// Tests the LSQ predictor predict constant velocity.
+TEST_F(LSQPredictorTest, PredictLinearValue) {
+ std::vector<double> x = {0, 4, 10, 15, 20, 28, 30, 38};
+ std::vector<double> y = {30, 34, 40, 45, 50, 58, 60, 68};
+ std::vector<double> t = {0, 4, 10, 15, 20, 28, 30, 38};
+ ValidatePredictor(x, y, t);
+}
+
+// Tests the LSQ predictor predict quadratic value correctly.
+TEST_F(LSQPredictorTest, PredictQuadraticValue) {
+ std::vector<double> x = {2, 8, 18, 32, 50};
+ std::vector<double> y = {100, 400, 900, 1600, 2500};
+ std::vector<double> t = {8, 16, 24, 32, 40};
+ ValidatePredictor(x, y, t);
+}
+
+// Tests that lsq predictor will not crash when given constant time stamp.
+TEST_F(LSQPredictorTest, ConstantTimeStampNotCrash) {
+ std::vector<double> x = {100, 101, 102};
+ std::vector<double> y = {101, 102, 103};
+ std::vector<double> t = {0, 0, 0};
+ for (size_t i = 0; i < t.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+ // Expect false because the matrix is singular
+ // and the predictor cannot compute a prediction
+ EXPECT_FALSE(predictor_->GeneratePrediction(FromMilliseconds(42)));
+
+ x = {100, 100, 100};
+ y = {100, 100, 100};
+ t = {100, 100, 100};
+ for (size_t i = 0; i < t.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+ EXPECT_TRUE(predictor_->GeneratePrediction(FromMilliseconds(142)));
+}
+
+// Tests the LSQ predictor produce the time interval correctly.
+TEST_F(LSQPredictorTest, TimeInterval) {
+ EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
+ std::vector<double> x = {0, 4, 10};
+ std::vector<double> y = {30, 34, 40};
+ std::vector<double> t = {0, 4, 10};
+ for (size_t i = 0; i < t.size(); i++) {
+ InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
+ FromMilliseconds(t[i])};
+ predictor_->Update(data);
+ }
+ EXPECT_EQ(predictor_->TimeInterval(),
+ base::TimeDelta::FromMillisecondsD((t[2] - t[0]) / 2));
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/linear_predictor.cc b/chromium/ui/base/prediction/linear_predictor.cc
new file mode 100644
index 00000000000..9f85488335b
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_predictor.cc
@@ -0,0 +1,128 @@
+// 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 "ui/base/prediction/linear_predictor.h"
+
+#include <algorithm>
+
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+LinearPredictor::LinearPredictor(EquationOrder order) {
+ equation_order_ = order;
+}
+
+LinearPredictor::~LinearPredictor() {}
+
+const char* LinearPredictor::GetName() const {
+ return equation_order_ == EquationOrder::kFirstOrder
+ ? features::kPredictorNameLinearFirst
+ : features::kPredictorNameLinearSecond;
+}
+
+void LinearPredictor::Reset() {
+ events_queue_.clear();
+}
+
+size_t LinearPredictor::NumberOfEventsNeeded() {
+ return static_cast<size_t>(equation_order_);
+}
+
+void LinearPredictor::Update(const InputData& new_input) {
+ // The last input received is at least kMaxDeltaTime away, we consider it
+ // is a new trajectory
+ if (!events_queue_.empty() &&
+ new_input.time_stamp - events_queue_.back().time_stamp > kMaxTimeDelta) {
+ Reset();
+ }
+
+ // Queue the new event and keep only the number of last events needed
+ events_queue_.push_back(new_input);
+ if (events_queue_.size() > static_cast<size_t>(equation_order_)) {
+ events_queue_.pop_front();
+ }
+
+ // Compute the current velocity
+ if (events_queue_.size() >= static_cast<size_t>(EquationOrder::kFirstOrder)) {
+ // Even if cur_velocity is empty the first time, last_velocity is only
+ // used when 3 events are in the queue, so it will be initialized
+ last_velocity_.set_x(cur_velocity_.x());
+ last_velocity_.set_y(cur_velocity_.y());
+
+ // We have at least 2 events to compute the current velocity
+ // Get delta time between the last 2 events
+ // Note: this delta time is also used to compute the acceleration term
+ events_dt_ = (events_queue_.at(events_queue_.size() - 1).time_stamp -
+ events_queue_.at(events_queue_.size() - 2).time_stamp)
+ .InMillisecondsF();
+
+ // Get delta pos between the last 2 events
+ gfx::Vector2dF delta_pos = events_queue_.at(events_queue_.size() - 1).pos -
+ events_queue_.at(events_queue_.size() - 2).pos;
+
+ // Get the velocity
+ if (events_dt_ > 0) {
+ cur_velocity_.set_x(ScaleVector2d(delta_pos, 1.0 / events_dt_).x());
+ cur_velocity_.set_y(ScaleVector2d(delta_pos, 1.0 / events_dt_).y());
+ } else {
+ cur_velocity_.set_x(0);
+ cur_velocity_.set_y(0);
+ }
+ }
+}
+
+bool LinearPredictor::HasPrediction() const {
+ // Even if the current equation is second order, we still can predict a
+ // first order
+ return events_queue_.size() >=
+ static_cast<size_t>(EquationOrder::kFirstOrder);
+}
+
+std::unique_ptr<InputPredictor::InputData> LinearPredictor::GeneratePrediction(
+ base::TimeTicks predict_time) const {
+ if (!HasPrediction())
+ return nullptr;
+
+ float pred_dt =
+ (predict_time - events_queue_.back().time_stamp).InMillisecondsF();
+
+ // Compute the prediction
+ // Note : a first order prediction is computed when only 2 events are
+ // available in the second order predictor
+ if (equation_order_ == EquationOrder::kSecondOrder && events_dt_ > 0 &&
+ events_queue_.size() ==
+ static_cast<size_t>(EquationOrder::kSecondOrder)) {
+ // Add the acceleration term to the current result
+ return std::make_unique<InputData>(GeneratePredictionSecondOrder(pred_dt),
+ predict_time);
+ }
+ return std::make_unique<InputData>(GeneratePredictionFirstOrder(pred_dt),
+ predict_time);
+}
+
+gfx::PointF LinearPredictor::GeneratePredictionFirstOrder(float pred_dt) const {
+ return events_queue_.back().pos + ScaleVector2d(cur_velocity_, pred_dt);
+}
+
+gfx::PointF LinearPredictor::GeneratePredictionSecondOrder(
+ float pred_dt) const {
+ DCHECK(equation_order_ == EquationOrder::kSecondOrder);
+
+ gfx::Vector2dF acceleration =
+ ScaleVector2d(cur_velocity_ - last_velocity_, 1.0 / events_dt_);
+ return events_queue_.back().pos + ScaleVector2d(cur_velocity_, pred_dt) +
+ ScaleVector2d(acceleration, 0.5 * pred_dt * pred_dt);
+}
+
+base::TimeDelta LinearPredictor::TimeInterval() const {
+ if (events_queue_.size() > 1) {
+ return std::max(kMinTimeInterval, (events_queue_.back().time_stamp -
+ events_queue_.front().time_stamp) /
+ (events_queue_.size() - 1));
+ }
+ return kTimeInterval;
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/linear_predictor.h b/chromium/ui/base/prediction/linear_predictor.h
new file mode 100644
index 00000000000..0f3bac523d0
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_predictor.h
@@ -0,0 +1,80 @@
+// 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 UI_BASE_PREDICTION_LINEAR_PREDICTOR_H_
+#define UI_BASE_PREDICTION_LINEAR_PREDICTOR_H_
+
+#include <deque>
+
+#include "base/component_export.h"
+#include "ui/base/prediction/input_predictor.h"
+
+namespace ui {
+
+// This class use a linear model for prediction
+// You can choose between a first order equation:
+// pred_p = last_p + velocity*pred_time
+// and a second order equation:
+// pred_p = last_p + velocity*pred_dt + 0.5*acceleration*pred_dt^2
+
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) LinearPredictor
+ : public InputPredictor {
+ public:
+ // Used to dissociate the order of the equation used but also used to
+ // define the number of events needed by each model
+ enum class EquationOrder : size_t { kFirstOrder = 2, kSecondOrder = 3 };
+
+ explicit LinearPredictor(EquationOrder order);
+ ~LinearPredictor() override;
+
+ const char* GetName() const override;
+
+ // Reset the predictor to initial state.
+ void Reset() override;
+
+ // Store current input in queue.
+ void Update(const InputData& new_input) override;
+
+ // Return if there is enough data in the queue to generate prediction.
+ bool HasPrediction() const override;
+
+ // Generate the prediction based on stored points and given time_stamp.
+ // Return false if no prediction available.
+ std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks predict_time) const override;
+
+ // Return the average time delta in the event queue.
+ base::TimeDelta TimeInterval() const override;
+
+ // Return the number of events needed to compute a prediction
+ size_t NumberOfEventsNeeded();
+
+ private:
+ gfx::PointF GeneratePredictionFirstOrder(float pred_dt) const;
+
+ gfx::PointF GeneratePredictionSecondOrder(float pred_dt) const;
+
+ // Store the last events received
+ std::deque<InputData> events_queue_;
+
+ // Store the equation order of the predictor
+ // The enum value also represents the number of events needed to compute the
+ // prediction
+ EquationOrder equation_order_;
+
+ // Store the current velocity of the 2 last events
+ gfx::Vector2dF cur_velocity_;
+
+ // Store the last velocity of the 2 last past events
+ gfx::Vector2dF last_velocity_;
+
+ // Store the current delta time between the last 2 events
+ float events_dt_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinearPredictor);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_LINEAR_PREDICTOR_H_
diff --git a/chromium/ui/base/prediction/linear_predictor_unittest.cc b/chromium/ui/base/prediction/linear_predictor_unittest.cc
new file mode 100644
index 00000000000..7d85d8323e8
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_predictor_unittest.cc
@@ -0,0 +1,149 @@
+// 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 "ui/base/prediction/linear_predictor.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+namespace test {
+
+class LinearPredictorFirstOrderTest : public InputPredictorTest {
+ public:
+ explicit LinearPredictorFirstOrderTest() {}
+
+ void SetUp() override {
+ predictor_ = std::make_unique<LinearPredictor>(
+ LinearPredictor::EquationOrder::kFirstOrder);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LinearPredictorFirstOrderTest);
+};
+
+class LinearPredictorSecondOrderTest : public InputPredictorTest {
+ public:
+ explicit LinearPredictorSecondOrderTest() {}
+
+ void SetUp() override {
+ predictor_ = std::make_unique<LinearPredictor>(
+ LinearPredictor::EquationOrder::kSecondOrder);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LinearPredictorSecondOrderTest);
+};
+
+// Test if the output name of the predictor is taking account of the
+// equation order
+TEST_F(LinearPredictorFirstOrderTest, GetName) {
+ EXPECT_EQ(predictor_->GetName(), features::kPredictorNameLinearFirst);
+}
+
+// Test if the output name of the predictor is taking account of the
+// equation order
+TEST_F(LinearPredictorSecondOrderTest, GetName) {
+ EXPECT_EQ(predictor_->GetName(), features::kPredictorNameLinearSecond);
+}
+
+// Test that the number of events required to compute a prediction is correct
+TEST_F(LinearPredictorFirstOrderTest, ShouldHavePrediction) {
+ LinearPredictor predictor(LinearPredictor::EquationOrder::kFirstOrder);
+ size_t n = static_cast<size_t>(LinearPredictor::EquationOrder::kFirstOrder);
+ for (size_t i = 0; i < n; i++) {
+ EXPECT_FALSE(predictor.HasPrediction());
+ predictor.Update(InputPredictor::InputData());
+ }
+ EXPECT_TRUE(predictor.HasPrediction());
+ predictor.Reset();
+ EXPECT_FALSE(predictor.HasPrediction());
+}
+
+// Test that the number of events required to compute a prediction is correct
+TEST_F(LinearPredictorSecondOrderTest, ShouldHavePrediction) {
+ LinearPredictor predictor(LinearPredictor::EquationOrder::kSecondOrder);
+ size_t n1 = static_cast<size_t>(LinearPredictor::EquationOrder::kFirstOrder);
+ size_t n2 = static_cast<size_t>(LinearPredictor::EquationOrder::kSecondOrder);
+ for (size_t i = 0; i < n2; i++) {
+ if (i < n1)
+ EXPECT_FALSE(predictor.HasPrediction());
+ else
+ EXPECT_TRUE(predictor.HasPrediction());
+ predictor.Update(InputPredictor::InputData());
+ }
+ EXPECT_TRUE(predictor.HasPrediction());
+ predictor.Reset();
+ EXPECT_FALSE(predictor.HasPrediction());
+}
+
+TEST_F(LinearPredictorFirstOrderTest, PredictedValue) {
+ std::vector<double> x = {10, 20};
+ std::vector<double> y = {5, 25};
+ std::vector<double> t = {17, 33};
+ // Compensating 23 ms
+ // 1st order prediction at 33 + 23 = 56 ms
+
+ std::vector<double> pred_ts = {56};
+ std::vector<double> pred_x = {34.37};
+ std::vector<double> pred_y = {53.75};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+TEST_F(LinearPredictorSecondOrderTest, PredictedValue) {
+ std::vector<double> x = {0, 10, 20};
+ std::vector<double> y = {0, 5, 25};
+ std::vector<double> t = {0, 17, 33};
+ // Compensating 23 ms in both results
+ // 1st order prediction at 17 + 23 = 40 ms
+ // 2nd order prediction at 33 + 23 = 56 ms
+ std::vector<double> pred_ts = {40, 56};
+ std::vector<double> pred_x = {23.52, 34.98};
+ std::vector<double> pred_y = {11.76, 69.55};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+// Test constant position and constant velocity
+TEST_F(LinearPredictorSecondOrderTest, ConstantPositionAndVelocity) {
+ std::vector<double> x = {10, 10, 10, 10, 10}; // constant position
+ std::vector<double> y = {0, 5, 10, 15, 20}; // constant velocity
+ std::vector<double> t = {0, 7, 14, 21, 28}; // regular interval
+ // since velocity is constant, acceleration should be 0 which simplifies
+ // computations
+ // Compensating 10 ms in all results
+ std::vector<double> pred_ts = {17, 24, 31, 38};
+ std::vector<double> pred_x = {10, 10, 10, 10};
+ std::vector<double> pred_y = {12.14, 17.14, 22.14, 27.14};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+// Test time interval in first order
+TEST_F(LinearPredictorFirstOrderTest, TimeInterval) {
+ EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
+ std::vector<double> x = {10, 20};
+ std::vector<double> y = {5, 25};
+ std::vector<double> t = {17, 33};
+ size_t n = static_cast<size_t>(LinearPredictor::EquationOrder::kFirstOrder);
+ for (size_t i = 0; i < n; i++) {
+ predictor_->Update({gfx::PointF(x[i], y[i]), FromMilliseconds(t[i])});
+ }
+ EXPECT_EQ(predictor_->TimeInterval(),
+ base::TimeDelta::FromMilliseconds(t[1] - t[0]));
+}
+
+// Test time interval in second order
+TEST_F(LinearPredictorSecondOrderTest, TimeInterval) {
+ EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
+ std::vector<double> x = {0, 10, 20};
+ std::vector<double> y = {0, 5, 25};
+ std::vector<double> t = {0, 17, 33};
+ size_t n = static_cast<size_t>(LinearPredictor::EquationOrder::kSecondOrder);
+ for (size_t i = 0; i < n; i++) {
+ predictor_->Update({gfx::PointF(x[i], y[i]), FromMilliseconds(t[i])});
+ }
+ EXPECT_EQ(predictor_->TimeInterval(),
+ base::TimeDelta::FromMillisecondsD((t[2] - t[0]) / 2));
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/linear_resampling.cc b/chromium/ui/base/prediction/linear_resampling.cc
new file mode 100644
index 00000000000..850e99c0c3b
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_resampling.cc
@@ -0,0 +1,97 @@
+// 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 "ui/base/prediction/linear_resampling.h"
+
+#include <algorithm>
+
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+namespace {
+// Minimum time difference between last two consecutive events before attempting
+// to resample.
+constexpr auto kResampleMinDelta = base::TimeDelta::FromMilliseconds(2);
+// Maximum time to predict forward from the last event, to avoid predicting too
+// far into the future. This time is further bounded by 50% of the last time
+// delta.
+constexpr auto kResampleMaxPrediction = base::TimeDelta::FromMilliseconds(8);
+// Align events to a few milliseconds before frame_time. This is to make the
+// resampling either doing interpolation or extrapolating a closer future time
+// so that resampled result is more accurate and has less noise. This adds some
+// latency during resampling but a few ms should be fine.
+constexpr auto kResampleLatency = base::TimeDelta::FromMilliseconds(5);
+
+// Get position at |sample_time| by linear interpolate/extrapolate a and b.
+inline gfx::PointF lerp(const InputPredictor::InputData& a,
+ const InputPredictor::InputData& b,
+ base::TimeTicks sample_time) {
+ const float alpha =
+ (sample_time - a.time_stamp) / (a.time_stamp - b.time_stamp);
+ return a.pos + gfx::ScaleVector2d(a.pos - b.pos, alpha);
+}
+
+} // namespace
+
+LinearResampling::LinearResampling() {}
+
+LinearResampling::~LinearResampling() {}
+
+const char* LinearResampling::GetName() const {
+ return features::kPredictorNameLinearResampling;
+}
+
+void LinearResampling::Reset() {
+ events_queue_.clear();
+}
+
+void LinearResampling::Update(const InputData& new_input) {
+ // The last input received is at least kMaxDeltaTime away, we consider it
+ // is a new trajectory
+ if (!events_queue_.empty() &&
+ new_input.time_stamp - events_queue_.front().time_stamp > kMaxTimeDelta) {
+ Reset();
+ }
+
+ // Queue the new event.
+ events_queue_.push_front(new_input);
+ if (events_queue_.size() > kNumEventsForResampling)
+ events_queue_.pop_back();
+ DCHECK(events_queue_.size() <= kNumEventsForResampling);
+
+ if (events_queue_.size() == kNumEventsForResampling)
+ events_dt_ = events_queue_[0].time_stamp - events_queue_[1].time_stamp;
+}
+
+bool LinearResampling::HasPrediction() const {
+ return events_queue_.size() == kNumEventsForResampling &&
+ events_dt_ >= kResampleMinDelta;
+}
+
+std::unique_ptr<InputPredictor::InputData> LinearResampling::GeneratePrediction(
+ base::TimeTicks frame_time) const {
+ if (!HasPrediction())
+ return nullptr;
+
+ base::TimeTicks sample_time = frame_time - kResampleLatency;
+
+ base::TimeDelta max_prediction =
+ std::min(kResampleMaxPrediction, events_dt_ / 2.0);
+
+ sample_time =
+ std::min(sample_time, events_queue_[0].time_stamp + max_prediction);
+
+ return std::make_unique<InputData>(
+ lerp(events_queue_[0], events_queue_[1], sample_time), sample_time);
+}
+
+base::TimeDelta LinearResampling::TimeInterval() const {
+ if (events_queue_.size() == kNumEventsForResampling) {
+ return events_dt_;
+ }
+ return kTimeInterval;
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/linear_resampling.h b/chromium/ui/base/prediction/linear_resampling.h
new file mode 100644
index 00000000000..4e44d44d352
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_resampling.h
@@ -0,0 +1,60 @@
+// 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 UI_BASE_PREDICTION_LINEAR_RESAMPLING_H_
+#define UI_BASE_PREDICTION_LINEAR_RESAMPLING_H_
+
+#include <deque>
+
+#include "base/component_export.h"
+#include "ui/base/prediction/input_predictor.h"
+
+namespace ui {
+
+// This class use linear extrapolates / interpolates to resample events to
+// frame_time - kResampleLatency. This resampling logic is to match the
+// resampling behavior on Android. It's not designed for pointerevent's
+// PredictedEvent and should not be used for that purpose.
+// Resampling on Android see:
+// https://android.googlesource.com/platform/frameworks/native/+/master/libs/input/InputTransport.cpp
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) LinearResampling
+ : public InputPredictor {
+ public:
+ explicit LinearResampling();
+ ~LinearResampling() override;
+
+ const char* GetName() const override;
+
+ // Reset the predictor to initial state.
+ void Reset() override;
+
+ // Store current input in queue.
+ void Update(const InputData& new_input) override;
+
+ // Return if there is enough data in the queue to generate prediction.
+ bool HasPrediction() const override;
+
+ // Generate the prediction based on stored points and given frame_time.
+ // Return false if no prediction available.
+ std::unique_ptr<InputData> GeneratePrediction(
+ base::TimeTicks frame_time) const override;
+
+ // Return the average time delta in the event queue.
+ base::TimeDelta TimeInterval() const override;
+
+ private:
+ static constexpr size_t kNumEventsForResampling = 2;
+
+ // Store the last events received
+ std::deque<InputData> events_queue_;
+
+ // Store the current delta time between the last 2 events
+ base::TimeDelta events_dt_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinearResampling);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_LINEAR_RESAMPLING_H_
diff --git a/chromium/ui/base/prediction/linear_resampling_unittest.cc b/chromium/ui/base/prediction/linear_resampling_unittest.cc
new file mode 100644
index 00000000000..9ae3bda367f
--- /dev/null
+++ b/chromium/ui/base/prediction/linear_resampling_unittest.cc
@@ -0,0 +1,123 @@
+// 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 "ui/base/prediction/linear_resampling.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+namespace test {
+
+class LinearResamplingTest : public InputPredictorTest {
+ public:
+ explicit LinearResamplingTest() {}
+
+ void SetUp() override { predictor_ = std::make_unique<LinearResampling>(); }
+
+ DISALLOW_COPY_AND_ASSIGN(LinearResamplingTest);
+};
+
+// Test if the output name of the predictor is taking account of the
+// equation order
+TEST_F(LinearResamplingTest, GetName) {
+ EXPECT_EQ(predictor_->GetName(), features::kPredictorNameLinearResampling);
+}
+
+// Test that the number of events required to compute a prediction is correct
+TEST_F(LinearResamplingTest, ShouldHavePrediction) {
+ LinearResampling predictor;
+ EXPECT_FALSE(predictor.HasPrediction());
+
+ // 1st event.
+ predictor.Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(0)}));
+ EXPECT_FALSE(predictor.HasPrediction());
+
+ // 2nd event.
+ predictor.Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(8)}));
+ EXPECT_TRUE(predictor.HasPrediction());
+
+ predictor.Reset();
+ EXPECT_FALSE(predictor.HasPrediction());
+}
+
+TEST_F(LinearResamplingTest, ResampleMinDelta) {
+ EXPECT_FALSE(predictor_->HasPrediction());
+ predictor_->Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(0)}));
+ predictor_->Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(1)}));
+ // No prediction when last_dt < kResampleMinDelta.
+ EXPECT_FALSE(predictor_->HasPrediction());
+
+ // Has prediction when last_dt >= kResampleMinDelta.
+ predictor_->Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(3)}));
+ EXPECT_TRUE(predictor_->HasPrediction());
+
+ predictor_->Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(15)}));
+ EXPECT_TRUE(predictor_->HasPrediction());
+
+ // Predictor is reset when dt > kMaxTimeDelta.
+ predictor_->Update(
+ InputPredictor::InputData({gfx::PointF(0, 0), FromMilliseconds(36)}));
+ EXPECT_FALSE(predictor_->HasPrediction());
+}
+
+TEST_F(LinearResamplingTest, ResamplingValue) {
+ std::vector<double> x = {10, 20, 30};
+ std::vector<double> y = {5, 25, 35};
+ std::vector<double> t = {15, 24, 32};
+
+ // Resample at frame_time = 33 ms, sample_time = 33-5 = 28ms.
+ // Resample at frame_time = 41 ms, sample_time = 41-5 = 36ms.
+ std::vector<double> pred_ts = {33, 41};
+ std::vector<double> pred_x = {24.44, 35};
+ std::vector<double> pred_y = {33.89, 40};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+TEST_F(LinearResamplingTest, ResamplingMaxPrediction) {
+ std::vector<double> x = {10, 20};
+ std::vector<double> y = {5, 10};
+ std::vector<double> t = {10, 30};
+ // Resample at frame_time = 45 ms, with max_prediction =
+ // kResampleMaxPrediction, sample_time = 30 + 8ms = 38ms.
+ std::vector<double> pred_ts = {45};
+ std::vector<double> pred_x = {24};
+ std::vector<double> pred_y = {12};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+TEST_F(LinearResamplingTest, ResamplingBoundLastDelta) {
+ std::vector<double> x = {10, 20};
+ std::vector<double> y = {5, 10};
+ std::vector<double> t = {10, 14};
+ // Resample at frame_time = 20 ms, sample time is bounded by 50% of the
+ // last time delta, result in 14 + 2ms = 16ms.
+ std::vector<double> pred_ts = {20};
+ std::vector<double> pred_x = {22.5};
+ std::vector<double> pred_y = {11.25};
+ ValidatePredictor(x, y, t, pred_ts, pred_x, pred_y);
+}
+
+// Test time interval in first order
+TEST_F(LinearResamplingTest, TimeInterval) {
+ EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
+ std::vector<double> x = {10, 20};
+ std::vector<double> y = {5, 25};
+ std::vector<double> t = {17, 33};
+ for (size_t i = 0; i < t.size(); i++) {
+ predictor_->Update({gfx::PointF(x[i], y[i]), FromMilliseconds(t[i])});
+ }
+ EXPECT_EQ(predictor_->TimeInterval(),
+ base::TimeDelta::FromMilliseconds(t[1] - t[0]));
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/one_euro_filter.cc b/chromium/ui/base/prediction/one_euro_filter.cc
new file mode 100644
index 00000000000..31a6de485b6
--- /dev/null
+++ b/chromium/ui/base/prediction/one_euro_filter.cc
@@ -0,0 +1,53 @@
+// 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 "ui/base/prediction/one_euro_filter.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+const double OneEuroFilter::kDefaultFrequency;
+const double OneEuroFilter::kDefaultMincutoff;
+const double OneEuroFilter::kDefaultBeta;
+const double OneEuroFilter::kDefaultDcutoff;
+
+const char OneEuroFilter::kParamBeta[];
+const char OneEuroFilter::kParamMincutoff[];
+
+OneEuroFilter::OneEuroFilter(double mincutoff, double beta) {
+ x_filter_ = std::make_unique<one_euro_filter::OneEuroFilter>(
+ kDefaultFrequency, mincutoff, beta, kDefaultDcutoff);
+ y_filter_ = std::make_unique<one_euro_filter::OneEuroFilter>(
+ kDefaultFrequency, mincutoff, beta, kDefaultDcutoff);
+}
+
+OneEuroFilter::~OneEuroFilter() {}
+
+bool OneEuroFilter::Filter(const base::TimeTicks& timestamp,
+ gfx::PointF* position) const {
+ if (!position)
+ return false;
+ one_euro_filter::TimeStamp ts = (timestamp - base::TimeTicks()).InSecondsF();
+ position->set_x(x_filter_->Filter(position->x(), ts));
+ position->set_y(y_filter_->Filter(position->y(), ts));
+ return true;
+}
+
+const char* OneEuroFilter::GetName() const {
+ return features::kFilterNameOneEuro;
+}
+
+InputFilter* OneEuroFilter::Clone() {
+ OneEuroFilter* new_filter = new OneEuroFilter();
+ new_filter->x_filter_.reset(x_filter_->Clone());
+ new_filter->y_filter_.reset(y_filter_->Clone());
+ return new_filter;
+}
+
+void OneEuroFilter::Reset() {
+ x_filter_->Reset();
+ y_filter_->Reset();
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/one_euro_filter.h b/chromium/ui/base/prediction/one_euro_filter.h
new file mode 100644
index 00000000000..fa3667f5515
--- /dev/null
+++ b/chromium/ui/base/prediction/one_euro_filter.h
@@ -0,0 +1,52 @@
+// 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 UI_BASE_PREDICTION_ONE_EURO_FILTER_H_
+#define UI_BASE_PREDICTION_ONE_EURO_FILTER_H_
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "third_party/one_euro_filter/src/one_euro_filter.h"
+#include "ui/base/prediction/input_filter.h"
+
+namespace ui {
+
+// This class uses the 1€ filter from third party.
+// See this page : http://cristal.univ-lille.fr/~casiez/1euro/
+// to know how the filter works and how to tune it
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) OneEuroFilter : public InputFilter {
+ public:
+ OneEuroFilter(double mincutoff = kDefaultMincutoff,
+ double beta = kDefaultBeta);
+ ~OneEuroFilter() override;
+
+ bool Filter(const base::TimeTicks& timestamp,
+ gfx::PointF* position) const override;
+
+ const char* GetName() const override;
+
+ InputFilter* Clone() override;
+
+ void Reset() override;
+
+ // Default parameters values for the filter
+ static constexpr double kDefaultFrequency = 60;
+ static constexpr double kDefaultMincutoff = 1.0;
+ static constexpr double kDefaultBeta = 0.001;
+ static constexpr double kDefaultDcutoff = 1.0;
+
+ // Names of the fieldtrials used to tune the filter
+ static constexpr char kParamBeta[] = "beta";
+ static constexpr char kParamMincutoff[] = "mincutoff";
+
+ private:
+ std::unique_ptr<one_euro_filter::OneEuroFilter> x_filter_;
+ std::unique_ptr<one_euro_filter::OneEuroFilter> y_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(OneEuroFilter);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_ONE_EURO_FILTER_H_
diff --git a/chromium/ui/base/prediction/one_euro_filter_unittests.cc b/chromium/ui/base/prediction/one_euro_filter_unittests.cc
new file mode 100644
index 00000000000..3e74f032fa0
--- /dev/null
+++ b/chromium/ui/base/prediction/one_euro_filter_unittests.cc
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_filter_unittest_helpers.h"
+#include "ui/base/prediction/one_euro_filter.h"
+#include "ui/base/prediction/prediction_unittest_helpers.h"
+
+namespace ui {
+namespace test {
+
+class OneEuroFilterTest : public InputFilterTest {
+ public:
+ explicit OneEuroFilterTest() {}
+
+ void SetUp() override { filter_ = std::make_unique<OneEuroFilter>(1, 1); }
+
+ DISALLOW_COPY_AND_ASSIGN(OneEuroFilterTest);
+};
+
+TEST_F(OneEuroFilterTest, TestClone) {
+ TestCloneFilter();
+}
+
+TEST_F(OneEuroFilterTest, TestReset) {
+ TestResetFilter();
+}
+
+// Check if sending values between 0 and 1 keeps filtered values between 0 and 1
+TEST_F(OneEuroFilterTest, filteringValues) {
+ base::TimeTicks ts = PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ gfx::PointF point;
+ for (int i = 0; i < 100; i++) {
+ point.SetPoint(base::RandDouble(), base::RandDouble());
+ EXPECT_TRUE(filter_->Filter(ts, &point));
+ EXPECT_LT(point.x(), 1.0);
+ EXPECT_LT(point.y(), 1.0);
+ EXPECT_GT(point.x(), 0.0);
+ EXPECT_GT(point.y(), 0.0);
+ }
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/prediction_metrics_handler.cc b/chromium/ui/base/prediction/prediction_metrics_handler.cc
new file mode 100644
index 00000000000..e782e6736e5
--- /dev/null
+++ b/chromium/ui/base/prediction/prediction_metrics_handler.cc
@@ -0,0 +1,186 @@
+// 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 "ui/base/prediction/prediction_metrics_handler.h"
+
+#include "base/metrics/histogram_functions.h"
+
+namespace ui {
+
+PredictionMetricsHandler::PredictionMetricsHandler() {}
+PredictionMetricsHandler::~PredictionMetricsHandler() {}
+
+void PredictionMetricsHandler::AddRealEvent(const gfx::PointF& pos,
+ const base::TimeTicks& time_stamp,
+ const base::TimeTicks& frame_time,
+ bool scrolling) {
+ // Be sure real events are ordered over time
+ DCHECK(events_queue_.empty() ||
+ time_stamp >= events_queue_.back().time_stamp);
+ EventData e;
+ if (scrolling)
+ e.pos = gfx::PointF(0, pos.y());
+ else
+ e.pos = pos;
+ e.time_stamp = time_stamp;
+ e.frame_time = frame_time;
+ events_queue_.push_back(e);
+}
+
+void PredictionMetricsHandler::AddPredictedEvent(
+ const gfx::PointF& pos,
+ const base::TimeTicks& time_stamp,
+ const base::TimeTicks& frame_time,
+ bool scrolling) {
+ DCHECK(!events_queue_.empty());
+ // If the predicted event is prior to the first real event, ignore it as we
+ // don't have enough data for interpolation.
+ if (time_stamp < events_queue_.front().time_stamp)
+ return;
+ // TODO(nzolghadr): The following DCHECK is commented out due to
+ // crbug.com/1017661. More investigation needs to be done as why this happens.
+ // DCHECK(predicted_events_queue_.empty() ||
+ // time_stamp >= predicted_events_queue_.back().time_stamp);
+ bool needs_sorting = false;
+ if (!predicted_events_queue_.empty() &&
+ time_stamp < predicted_events_queue_.back().time_stamp)
+ needs_sorting = true;
+
+ EventData e;
+ if (scrolling)
+ e.pos = gfx::PointF(0, pos.y());
+ else
+ e.pos = pos;
+ e.time_stamp = time_stamp;
+ e.frame_time = frame_time;
+ predicted_events_queue_.push_back(e);
+
+ // TODO(nzolghadr): This should never be needed. Something seems to be wrong
+ // in the tests. See crbug.com/1017661.
+ if (needs_sorting) {
+ std::sort(predicted_events_queue_.begin(), predicted_events_queue_.end(),
+ [](const EventData& a, const EventData& b) {
+ return a.time_stamp < b.time_stamp;
+ });
+ }
+}
+
+void PredictionMetricsHandler::EvaluatePrediction() {
+ while (!predicted_events_queue_.empty()) {
+ // Not enough events to compute the metrics, do not compute for now.
+ if (events_queue_.size() < 2 ||
+ events_queue_.back().time_stamp <=
+ predicted_events_queue_.front().time_stamp ||
+ events_queue_.back().time_stamp <=
+ predicted_events_queue_.front().frame_time) {
+ return;
+ }
+
+ ComputeMetrics();
+
+ last_predicted_ = predicted_events_queue_.front().pos;
+ last_interpolated_ = interpolated_;
+ last_frame_interpolated_ = frame_interpolated_;
+ predicted_events_queue_.pop_front();
+ }
+}
+
+void PredictionMetricsHandler::Reset() {
+ events_queue_.clear();
+ predicted_events_queue_.clear();
+ last_predicted_ = base::nullopt;
+}
+
+int PredictionMetricsHandler::GetInterpolatedEventForPredictedEvent(
+ const base::TimeTicks& interpolation_timestamp,
+ gfx::PointF* interpolated) {
+ size_t idx = 0;
+ while (idx < events_queue_.size() &&
+ interpolation_timestamp >= events_queue_[idx].time_stamp)
+ idx++;
+
+ if (idx == 0 || idx == events_queue_.size())
+ return -1;
+
+ const float alpha =
+ (interpolation_timestamp - events_queue_[idx - 1].time_stamp) /
+ (events_queue_[idx].time_stamp - events_queue_[idx - 1].time_stamp);
+ *interpolated =
+ events_queue_[idx - 1].pos +
+ ScaleVector2d(events_queue_[idx].pos - events_queue_[idx - 1].pos, alpha);
+ return idx - 1;
+}
+
+void PredictionMetricsHandler::ComputeMetrics() {
+ // Compute interpolations at predicted time and frame time.
+ int low_idx_interpolated = GetInterpolatedEventForPredictedEvent(
+ predicted_events_queue_.front().time_stamp, &interpolated_);
+ int low_idx_frame_interpolated = GetInterpolatedEventForPredictedEvent(
+ predicted_events_queue_.front().frame_time, &frame_interpolated_);
+
+ next_real_ = events_queue_[low_idx_interpolated + 1].pos;
+
+ int first_needed_event =
+ std::min(low_idx_interpolated, low_idx_frame_interpolated);
+ // Return if any of the interpolation is not found.
+ if (first_needed_event == -1)
+ return;
+ // Clean real events queue.
+ for (int i = 0; i < first_needed_event - 1; i++)
+ events_queue_.pop_front();
+
+ std::string kPredictionMetrics = "Event.InputEventPrediction.Scroll.";
+
+ double score = ComputeOverUnderPredictionMetric();
+ if (score >= 0) {
+ base::UmaHistogramCounts1000(kPredictionMetrics + "OverPrediction", score);
+ } else {
+ base::UmaHistogramCounts1000(kPredictionMetrics + "UnderPrediction",
+ -score);
+ }
+
+ // Need |last_predicted_| to compute WrongDirection and Jitter metrics.
+ if (!last_predicted_.has_value())
+ return;
+
+ base::UmaHistogramBoolean(kPredictionMetrics + "WrongDirection",
+ ComputeWrongDirectionMetric());
+ base::UmaHistogramCounts1000(kPredictionMetrics + "PredictionJitter",
+ ComputePredictionJitterMetric());
+ base::UmaHistogramCounts1000(kPredictionMetrics + "VisualJitter",
+ ComputeVisualJitterMetric());
+}
+
+double PredictionMetricsHandler::ComputeOverUnderPredictionMetric() {
+ gfx::Vector2dF real_direction = next_real_ - interpolated_;
+ gfx::Vector2dF relative_direction =
+ predicted_events_queue_.front().pos - interpolated_;
+ if (gfx::DotProduct(real_direction, relative_direction) >= 0)
+ return relative_direction.Length();
+ else
+ return -relative_direction.Length();
+}
+
+bool PredictionMetricsHandler::ComputeWrongDirectionMetric() {
+ gfx::Vector2dF real_direction = next_real_ - interpolated_;
+ gfx::Vector2dF predicted_direction =
+ predicted_events_queue_.front().pos - last_predicted_.value();
+ return gfx::DotProduct(real_direction, predicted_direction) < 0;
+}
+
+double PredictionMetricsHandler::ComputePredictionJitterMetric() {
+ gfx::Vector2dF delta = interpolated_ - predicted_events_queue_.front().pos;
+ gfx::Vector2dF last_delta = last_interpolated_ - last_predicted_.value();
+ return (delta - last_delta).Length();
+}
+
+double PredictionMetricsHandler::ComputeVisualJitterMetric() {
+ gfx::Vector2dF delta =
+ frame_interpolated_ - predicted_events_queue_.front().pos;
+ gfx::Vector2dF last_delta =
+ last_frame_interpolated_ - last_predicted_.value();
+ return (delta - last_delta).Length();
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/prediction/prediction_metrics_handler.h b/chromium/ui/base/prediction/prediction_metrics_handler.h
new file mode 100644
index 00000000000..648ccdd9998
--- /dev/null
+++ b/chromium/ui/base/prediction/prediction_metrics_handler.h
@@ -0,0 +1,110 @@
+// 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 UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_
+#define UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_
+
+#include <deque>
+#include <unordered_map>
+
+#include "base/component_export.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace ui {
+
+namespace test {
+class PredictionMetricsHandlerTest;
+}
+
+// Class used for evaluating input prediction.
+// The basic idea is to buffer predicted and real events to be able to compare
+// a predicted position to its corresponding interpolated real position with
+// few metrics.
+class COMPONENT_EXPORT(UI_BASE_PREDICTION) PredictionMetricsHandler {
+ public:
+ explicit PredictionMetricsHandler();
+ ~PredictionMetricsHandler();
+
+ // Struct used to store predicted and real event information.
+ struct EventData {
+ // Position of the event
+ gfx::PointF pos;
+ // Timestamp of the event
+ base::TimeTicks time_stamp;
+ // frame_time of the event
+ base::TimeTicks frame_time;
+ };
+
+ // Buffers a real event.
+ void AddRealEvent(const gfx::PointF& pos,
+ const base::TimeTicks& time_stamp,
+ const base::TimeTicks& frame_time,
+ bool scrolling = false);
+
+ // Buffers a predicted event.
+ void AddPredictedEvent(const gfx::PointF& pos,
+ const base::TimeTicks& time_stamp,
+ const base::TimeTicks& frame_time,
+ bool scrolling = false);
+
+ void EvaluatePrediction();
+
+ // Cleans all events buffers
+ void Reset();
+
+ private:
+ friend class test::PredictionMetricsHandlerTest;
+
+ // Computes necessary interpolations used for computing the metrics
+ void ComputeMetrics();
+
+ // Compute the OverUnderPredictionMetric score.
+ // The score is the amount of pixels the predicted point is ahead of
+ // the real point. If the score is positive, the prediction is OverPredicting,
+ // otherwise UnderPredicting.
+ double ComputeOverUnderPredictionMetric();
+
+ // Compute the PredictionJitterMetric score.
+ // The score is the euclidean distance between 2 successive variation of
+ // prediction and the corresponding real events at the same timestamp. It is
+ // an indicator of smoothness.
+ double ComputePredictionJitterMetric();
+
+ // Compute the WrongDirectionMetric score.
+ // The score is a boolean (as double) indicating whether the prediction is
+ // in the same direction as the real trajectory..
+ bool ComputeWrongDirectionMetric();
+
+ // Compute the VisualJitterMetric score.
+ // The score is the euclidean distance between 2 successive variation of
+ // prediction and the corresponding real events at frame time. It is
+ // an indicator of smoothness.
+ double ComputeVisualJitterMetric();
+
+ // Get the interpolated position from the real events at a given timestamp.
+ // Returns the index of the last real event which timestamp is smaller than
+ // the |interpolation_timestamp|. Returns -1 if not found.
+ int GetInterpolatedEventForPredictedEvent(
+ const base::TimeTicks& interpolation_timestamp,
+ gfx::PointF* interpolated);
+
+ // Queues used for buffering real and predicted events.
+ std::deque<EventData> events_queue_;
+ std::deque<EventData> predicted_events_queue_;
+
+ // Interpolated points of real events.
+ gfx::PointF interpolated_, frame_interpolated_;
+ gfx::PointF last_interpolated_, last_frame_interpolated_;
+ // Last predicted point that pop from predicted_event_queue_. Use for
+ // computing Jitter metrics.
+ base::Optional<gfx::PointF> last_predicted_ = base::nullopt;
+ // The first real event position which time is later than the predicted time.
+ gfx::PointF next_real_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_
diff --git a/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc b/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc
new file mode 100644
index 00000000000..f44df0d01dd
--- /dev/null
+++ b/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc
@@ -0,0 +1,233 @@
+// 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 "ui/base/prediction/prediction_metrics_handler.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/prediction/input_predictor_unittest_helpers.h"
+#include "ui/base/prediction/prediction_unittest_helpers.h"
+
+using base::Bucket;
+using testing::ElementsAre;
+
+namespace ui {
+namespace test {
+namespace {
+
+base::TimeTicks MillisecondsToTestTimeTicks(int64_t ms) {
+ return PredictionUnittestHelpers::GetStaticTimeStampForTests() +
+ base::TimeDelta::FromMilliseconds(ms);
+}
+
+} // namespace
+
+class PredictionMetricsHandlerTest : public testing::Test {
+ public:
+ explicit PredictionMetricsHandlerTest() {}
+
+ void SetUp() override {
+ metrics_handler_ = std::make_unique<PredictionMetricsHandler>();
+ histogram_tester_ = std::make_unique<base::HistogramTester>();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(PredictionMetricsHandlerTest);
+
+ int GetInterpolatedEventForPredictedEvent(const base::TimeTicks& timestamp,
+ gfx::PointF* interpolated) {
+ return metrics_handler_->GetInterpolatedEventForPredictedEvent(
+ timestamp, interpolated);
+ }
+
+ ::testing::AssertionResult HistogramSizeEq(const char* histogram_name,
+ int size) {
+ uint64_t histogram_size =
+ histogram_tester_->GetAllSamples(histogram_name).size();
+ if (static_cast<uint64_t>(size) == histogram_size) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure()
+ << histogram_name << " expected " << size << " entries, but had "
+ << histogram_size;
+ }
+ }
+
+ bool HasPredictionHistograms() {
+ uint64_t histogram_size =
+ histogram_tester_
+ ->GetAllSamples("Event.InputEventPrediction.Scroll.WrongDirection")
+ .size();
+ return histogram_size > 0u;
+ }
+
+ void Reset() {
+ histogram_tester_.reset(new base::HistogramTester());
+ metrics_handler_->Reset();
+ }
+
+ const base::HistogramTester& histogram_tester() { return *histogram_tester_; }
+
+ protected:
+ std::unique_ptr<PredictionMetricsHandler> metrics_handler_;
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+TEST_F(PredictionMetricsHandlerTest, CanComputeMetricsTest) {
+ base::TimeTicks start_time =
+ PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ base::TimeDelta dt = base::TimeDelta::FromMilliseconds(8);
+
+ // Need at least 2 real events to start comput metrics.
+ {
+ metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3 * dt,
+ start_time);
+ metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 3 * dt,
+ start_time);
+ metrics_handler_->EvaluatePrediction();
+ EXPECT_FALSE(HasPredictionHistograms());
+ }
+
+ // Need at least a real event strictly after the second predicted event.
+ Reset();
+ {
+ metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time, start_time);
+ metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + dt,
+ start_time);
+ metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 2 * dt,
+ start_time);
+ metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 3 * dt,
+ start_time);
+ metrics_handler_->EvaluatePrediction();
+ EXPECT_FALSE(HasPredictionHistograms());
+
+ metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3 * dt,
+ start_time);
+ metrics_handler_->EvaluatePrediction();
+ EXPECT_FALSE(HasPredictionHistograms());
+
+ metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3.1 * dt,
+ start_time);
+ metrics_handler_->EvaluatePrediction();
+ EXPECT_TRUE(HasPredictionHistograms());
+ }
+}
+
+TEST_F(PredictionMetricsHandlerTest, InterpolationTest) {
+ base::TimeTicks start_time =
+ PredictionUnittestHelpers::GetStaticTimeStampForTests();
+ base::TimeDelta dt = base::TimeDelta::FromMilliseconds(8);
+ gfx::PointF interpolated;
+
+ metrics_handler_->AddRealEvent(gfx::PointF(2, 2), start_time + 1 * dt,
+ start_time);
+ metrics_handler_->AddRealEvent(gfx::PointF(3, 3), start_time + 2 * dt,
+ start_time);
+ metrics_handler_->AddRealEvent(gfx::PointF(5, 5), start_time + 3 * dt,
+ start_time);
+ metrics_handler_->AddRealEvent(gfx::PointF(8, 8), start_time + 4 * dt,
+ start_time);
+
+ EXPECT_EQ(0, GetInterpolatedEventForPredictedEvent(start_time + 1.5 * dt,
+ &interpolated));
+ EXPECT_EQ(interpolated, gfx::PointF(2.5, 2.5));
+
+ EXPECT_EQ(2, GetInterpolatedEventForPredictedEvent(start_time + 3.5 * dt,
+ &interpolated));
+ EXPECT_EQ(interpolated, gfx::PointF(6.5, 6.5));
+}
+// For test purpose and simplify, we are predicted in the middle of 2 real
+// events, which is also the frame time (i.e. a prediction of 4 ms)
+void AddEvents(PredictionMetricsHandler* metrics_handler) {
+ metrics_handler->AddRealEvent(gfx::PointF(1, 1),
+ MillisecondsToTestTimeTicks(8),
+ MillisecondsToTestTimeTicks(12)); // R0
+ metrics_handler->AddRealEvent(gfx::PointF(2, 2),
+ MillisecondsToTestTimeTicks(16),
+ MillisecondsToTestTimeTicks(20)); // R1
+ metrics_handler->AddRealEvent(gfx::PointF(4, 4),
+ MillisecondsToTestTimeTicks(24),
+ MillisecondsToTestTimeTicks(28)); // R2
+ metrics_handler->AddRealEvent(gfx::PointF(7, 7),
+ MillisecondsToTestTimeTicks(32),
+ MillisecondsToTestTimeTicks(36)); // R3
+ metrics_handler->AddRealEvent(gfx::PointF(5, 5),
+ MillisecondsToTestTimeTicks(40),
+ MillisecondsToTestTimeTicks(44)); // R4
+ metrics_handler->AddRealEvent(gfx::PointF(3, 3),
+ MillisecondsToTestTimeTicks(48),
+ MillisecondsToTestTimeTicks(54)); // R5
+
+ // P0 | Interpolation from R0-R1 is (1.5,1.5)
+ // UnderPrediction
+ metrics_handler->AddPredictedEvent(gfx::PointF(1, 1),
+ MillisecondsToTestTimeTicks(12),
+ MillisecondsToTestTimeTicks(12));
+ // P1 | Interpolation from R1-R2 is (3,3)
+ // OverPrediction | RightDirection
+ metrics_handler->AddPredictedEvent(gfx::PointF(3.5, 3.5),
+ MillisecondsToTestTimeTicks(20),
+ MillisecondsToTestTimeTicks(20));
+ // P2 | Interpolation from R2-R3 is (5.5,5.5)
+ // UnderPrediction | RightDirection
+ metrics_handler->AddPredictedEvent(gfx::PointF(5, 5),
+ MillisecondsToTestTimeTicks(28),
+ MillisecondsToTestTimeTicks(28));
+ // P3 | Interpolation from R3-R4 is (6,6)
+ // UnderPrediction | WrongDirection
+ metrics_handler->AddPredictedEvent(gfx::PointF(7, 7),
+ MillisecondsToTestTimeTicks(36),
+ MillisecondsToTestTimeTicks(36));
+ // P4 | Interpolation from R4-R5 is (4,4)
+ // OverPrediction | RightDirection
+ metrics_handler->AddPredictedEvent(gfx::PointF(3, 3),
+ MillisecondsToTestTimeTicks(44),
+ MillisecondsToTestTimeTicks(44));
+}
+
+TEST_F(PredictionMetricsHandlerTest, PredictionMetricTest) {
+ AddEvents(metrics_handler_.get());
+ metrics_handler_->EvaluatePrediction();
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.InputEventPrediction.Scroll.OverPrediction"),
+ ElementsAre(Bucket(0, 1), Bucket(1, 1)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.InputEventPrediction.Scroll.UnderPrediction"),
+ ElementsAre(Bucket(0, 2), Bucket(1, 1)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.InputEventPrediction.Scroll.WrongDirection"),
+ ElementsAre(Bucket(0, 3), Bucket(1, 1)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.InputEventPrediction.Scroll.PredictionJitter"),
+ ElementsAre(Bucket(1, 2), Bucket(2, 2)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.InputEventPrediction.Scroll.VisualJitter"),
+ ElementsAre(Bucket(1, 2), Bucket(2, 2)));
+}
+
+// Test that it doesn't crash when predicted event is prior to first real event.
+TEST_F(PredictionMetricsHandlerTest, PredictedTimePriorToReal) {
+ metrics_handler_->AddRealEvent(gfx::PointF(1, 1),
+ MillisecondsToTestTimeTicks(8),
+ MillisecondsToTestTimeTicks(12));
+ metrics_handler_->AddRealEvent(gfx::PointF(2, 2),
+ MillisecondsToTestTimeTicks(10),
+ MillisecondsToTestTimeTicks(12));
+
+ metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0),
+ MillisecondsToTestTimeTicks(7),
+ MillisecondsToTestTimeTicks(12));
+ metrics_handler_->EvaluatePrediction();
+ // No prediction metrics result.
+ EXPECT_TRUE(
+ HistogramSizeEq("Event.InputEventPrediction.Scroll.PredictionJitter", 0));
+}
+
+} // namespace test
+} // namespace ui
diff --git a/chromium/ui/base/prediction/prediction_unittest_helpers.h b/chromium/ui/base/prediction/prediction_unittest_helpers.h
new file mode 100644
index 00000000000..3a22cdd55bc
--- /dev/null
+++ b/chromium/ui/base/prediction/prediction_unittest_helpers.h
@@ -0,0 +1,32 @@
+// 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 UI_BASE_PREDICTION_PREDICTION_UNITTEST_HELPERS_H_
+#define UI_BASE_PREDICTION_PREDICTION_UNITTEST_HELPERS_H_
+
+#include "base/time/time.h"
+
+namespace ui {
+namespace test {
+
+class PredictionUnittestHelpers {
+ public:
+ // Copied from third_party\blink\public\common\input\web_input_event.h
+ static constexpr base::TimeTicks GetStaticTimeStampForTests() {
+ // Note: intentionally use a relatively large delta from base::TimeTicks ==
+ // 0. Otherwise, code that tracks the time ticks of the last event that
+ // happened and computes a delta might get confused when the testing
+ // timestamp is near 0, as the computed delta may very well be under the
+ // delta threshold.
+ //
+ // TODO(dcheng): This really shouldn't use FromInternalValue(), but
+ // constexpr support for time operations is a bit busted...
+ return base::TimeTicks::FromInternalValue(123'000'000);
+ }
+};
+
+} // namespace test
+} // namespace ui
+
+#endif // UI_BASE_PREDICTION_PREDICTION_UNITTEST_HELPERS_H_
diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc
index eeb09546b45..df4b0a7c9bb 100644
--- a/chromium/ui/base/resource/resource_bundle.cc
+++ b/chromium/ui/base/resource/resource_bundle.cc
@@ -18,6 +18,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
@@ -42,7 +43,6 @@
#include "ui/display/screen.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_source.h"
@@ -72,7 +72,7 @@ const size_t kPngChunkMetadataSize = 12; // length, type, crc32
const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' };
const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' };
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
const char kPakFileExtension[] = ".pak";
#endif
@@ -207,10 +207,9 @@ class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource {
if (fell_back_to_1x) {
// GRIT fell back to the 100% image, so rescale it to the correct size.
image = skia::ImageOperations::Resize(
- image,
- skia::ImageOperations::RESIZE_LANCZOS3,
- gfx::ToCeiledInt(image.width() * scale),
- gfx::ToCeiledInt(image.height() * scale));
+ image, skia::ImageOperations::RESIZE_LANCZOS3,
+ base::ClampCeil(image.width() * scale),
+ base::ClampCeil(image.height() * scale));
} else {
scale = GetScaleForScaleFactor(scale_factor);
}
@@ -376,7 +375,7 @@ void ResourceBundle::AddDataPackFromFileRegion(
}
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// static
base::FilePath ResourceBundle::GetLocaleFilePath(
const std::string& app_locale) {
@@ -457,18 +456,22 @@ void ResourceBundle::LoadTestResources(const base::FilePath& path,
const base::FilePath& locale_path) {
is_test_resources_ = true;
DCHECK(!ui::GetSupportedScaleFactors().empty());
- const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]);
// Use the given resource pak for both common and localized resources.
- std::unique_ptr<DataPack> data_pack(new DataPack(scale_factor));
- if (!path.empty() && data_pack->LoadFromPath(path))
+
+ if (!path.empty()) {
+ const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]);
+ auto data_pack = std::make_unique<DataPack>(scale_factor);
+ CHECK(data_pack->LoadFromPath(path));
AddDataPack(std::move(data_pack));
+ }
- data_pack = std::make_unique<DataPack>(ui::SCALE_FACTOR_NONE);
+ auto data_pack = std::make_unique<DataPack>(ui::SCALE_FACTOR_NONE);
if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
locale_resources_data_ = std::move(data_pack);
} else {
locale_resources_data_ = std::make_unique<DataPack>(ui::SCALE_FACTOR_NONE);
}
+
// This is necessary to initialize ICU since we won't be calling
// LoadLocaleResources in this case.
l10n_util::GetApplicationLocale(std::string());
@@ -841,7 +844,7 @@ void ResourceBundle::ReloadFonts() {
}
ScaleFactor ResourceBundle::GetMaxScaleFactor() const {
-#if defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
return max_scale_factor_;
#else
return GetSupportedScaleFactors().back();
@@ -894,7 +897,8 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) {
// On platforms other than iOS, 100P is always a supported scale factor.
// For Windows we have a separate case in this function.
supported_scale_factors.push_back(SCALE_FACTOR_100P);
-#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_WIN)
+#if defined(OS_APPLE) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
+ defined(OS_WIN)
supported_scale_factors.push_back(SCALE_FACTOR_200P);
#endif
#endif
diff --git a/chromium/ui/base/ui_base_features.cc b/chromium/ui/base/ui_base_features.cc
index 3a6ee833e13..a89d5f031e4 100644
--- a/chromium/ui/base/ui_base_features.cc
+++ b/chromium/ui/base/ui_base_features.cc
@@ -67,7 +67,7 @@ bool IsNotificationIndicatorEnabled() {
// Enables GPU rasterization for all UI drawing (where not blacklisted).
const base::Feature kUiGpuRasterization = {"UiGpuRasterization",
-#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+#if defined(OS_APPLE) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
@@ -82,7 +82,7 @@ bool IsUiGpuRasterizationEnabled() {
const base::Feature kUiCompositorScrollWithLayers = {
"UiCompositorScrollWithLayers",
// TODO(https://crbug.com/615948): Use composited scrolling on all platforms.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
@@ -130,7 +130,8 @@ const base::Feature kPrecisionTouchpadLogging{
"PrecisionTouchpadLogging", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // defined(OS_WIN)
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \
+ defined(OS_CHROMEOS)
// Enables stylus appearing as touch when in contact with digitizer.
const base::Feature kDirectManipulationStylus = {
"DirectManipulationStylus",
@@ -140,7 +141,8 @@ const base::Feature kDirectManipulationStylus = {
base::FEATURE_DISABLED_BY_DEFAULT
#endif
};
-#endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#endif // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) ||
+ // defined(OS_CHROMEOS)
// Enables forced colors mode for web content.
const base::Feature kForcedColors{"ForcedColors",
@@ -179,7 +181,7 @@ bool IsCSSColorSchemeUARenderingEnabled() {
// Mac launch bug.
const base::Feature kFormControlsRefresh = {"FormControlsRefresh",
#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \
- defined(OS_MACOSX)
+ defined(OS_APPLE)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
@@ -226,16 +228,29 @@ const base::Feature kUseOzonePlatform {
bool IsUsingOzonePlatform() {
// Only allow enabling and disabling the OzonePlatform on USE_X11 && USE_OZONE
// builds.
+ static const bool using_ozone_platform =
#if defined(USE_X11) && defined(USE_OZONE)
- return base::FeatureList::IsEnabled(kUseOzonePlatform);
+ base::FeatureList::IsEnabled(kUseOzonePlatform);
#elif defined(USE_X11) && !defined(USE_OZONE)
- // This shouldn't be switchable for pure X11 builds.
- return false;
+ // This shouldn't be switchable for pure X11 builds.
+ false;
#else
- // All the other platforms must use Ozone by default and can't disable that.
- return true;
+ // All the other platforms must use Ozone by default and can't disable
+ // that.
+ true;
#endif
+ return using_ozone_platform;
}
#endif // defined(USE_X11) || defined(USE_OZONE)
+const char kPredictorNameLsq[] = "lsq";
+const char kPredictorNameKalman[] = "kalman";
+const char kPredictorNameLinearFirst[] = "linear_first";
+const char kPredictorNameLinearSecond[] = "linear_second";
+const char kPredictorNameLinearResampling[] = "linear_resampling";
+const char kPredictorNameEmpty[] = "empty";
+
+const char kFilterNameEmpty[] = "empty_filter";
+const char kFilterNameOneEuro[] = "one_euro_filter";
+
} // namespace features
diff --git a/chromium/ui/base/ui_base_features.h b/chromium/ui/base/ui_base_features.h
index 61f75131c9f..5dfdf280812 100644
--- a/chromium/ui/base/ui_base_features.h
+++ b/chromium/ui/base/ui_base_features.h
@@ -60,10 +60,12 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kTSFImeSupport;
COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUsingWMPointerForTouch();
#endif // defined(OS_WIN)
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \
+ defined(OS_CHROMEOS)
COMPONENT_EXPORT(UI_BASE_FEATURES)
extern const base::Feature kDirectManipulationStylus;
-#endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#endif // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) ||
+ // defined(OS_CHROMEOS)
// Used to enable forced colors mode for web content.
COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kForcedColors;
@@ -122,6 +124,24 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kUseOzonePlatform;
COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUsingOzonePlatform();
#endif
+// The type of predictor to use for the resampling events. These values are
+// used as the 'predictor' feature param for
+// |blink::features::kResamplingScrollEvents|.
+COMPONENT_EXPORT(UI_BASE_FEATURES) extern const char kPredictorNameLsq[];
+COMPONENT_EXPORT(UI_BASE_FEATURES) extern const char kPredictorNameKalman[];
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const char kPredictorNameLinearFirst[];
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const char kPredictorNameLinearSecond[];
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const char kPredictorNameLinearResampling[];
+COMPONENT_EXPORT(UI_BASE_FEATURES) extern const char kPredictorNameEmpty[];
+
+// The type of filter to use for filtering events. These values are used as the
+// 'filter' feature param for |blink::features::kFilteringScrollPrediction|.
+COMPONENT_EXPORT(UI_BASE_FEATURES) extern const char kFilterNameEmpty[];
+COMPONENT_EXPORT(UI_BASE_FEATURES) extern const char kFilterNameOneEuro[];
+
} // namespace features
#endif // UI_BASE_UI_BASE_FEATURES_H_
diff --git a/chromium/ui/base/ui_base_paths.cc b/chromium/ui/base/ui_base_paths.cc
index 82448af8754..b63668fba03 100644
--- a/chromium/ui/base/ui_base_paths.cc
+++ b/chromium/ui/base/ui_base_paths.cc
@@ -26,7 +26,7 @@ bool PathProvider(int key, base::FilePath* result) {
case DIR_LOCALES:
if (!base::PathService::Get(base::DIR_MODULE, &cur))
return false;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, locale files are in Contents/Resources, a sibling of the
// App dir.
cur = cur.DirName();
diff --git a/chromium/ui/base/ui_base_switches.cc b/chromium/ui/base/ui_base_switches.cc
index 712aa67bf99..384150dcdaa 100644
--- a/chromium/ui/base/ui_base_switches.cc
+++ b/chromium/ui/base/ui_base_switches.cc
@@ -6,7 +6,7 @@
namespace switches {
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
// Disable use of AVFoundation to draw video content.
const char kDisableAVFoundationOverlays[] = "disable-avfoundation-overlays";
@@ -31,9 +31,6 @@ const char kDisableCompositedAntialiasing[] = "disable-composited-antialiasing";
// Disables use of DWM composition for top level windows.
const char kDisableDwmComposition[] = "disable-dwm-composition";
-// Disables touch adjustment.
-const char kDisableTouchAdjustment[] = "disable-touch-adjustment";
-
// Disables touch event based drag and drop.
const char kDisableTouchDragDrop[] = "disable-touch-drag-drop";
@@ -82,8 +79,8 @@ const char kShowOverdrawFeedback[] = "show-overdraw-feedback";
const char kSlowDownCompositingScaleFactor[] =
"slow-down-compositing-scale-factor";
-// Tint GL-composited color.
-const char kTintGlCompositedContent[] = "tint-gl-composited-content";
+// Tint composited color.
+const char kTintCompositedContent[] = "tint-composited-content";
// Controls touch-optimized UI layout for top chrome.
const char kTopChromeTouchUi[] = "top-chrome-touch-ui";
diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h
index 8e7fb9e0c93..07ce187cebd 100644
--- a/chromium/ui/base/ui_base_switches.h
+++ b/chromium/ui/base/ui_base_switches.h
@@ -12,7 +12,7 @@
namespace switches {
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
COMPONENT_EXPORT(UI_BASE) extern const char kDisableAVFoundationOverlays[];
COMPONENT_EXPORT(UI_BASE) extern const char kDisableMacOverlays[];
COMPONENT_EXPORT(UI_BASE) extern const char kDisableModalAnimations[];
@@ -22,7 +22,6 @@ COMPONENT_EXPORT(UI_BASE) extern const char kShowMacOverlayBorders[];
COMPONENT_EXPORT(UI_BASE) extern const char kDisableCompositedAntialiasing[];
COMPONENT_EXPORT(UI_BASE) extern const char kDisableDwmComposition[];
-COMPONENT_EXPORT(UI_BASE) extern const char kDisableTouchAdjustment[];
COMPONENT_EXPORT(UI_BASE) extern const char kDisableTouchDragDrop[];
COMPONENT_EXPORT(UI_BASE) extern const char kEnableTouchDragDrop[];
COMPONENT_EXPORT(UI_BASE) extern const char kForceCaptionStyle[];
@@ -31,7 +30,7 @@ COMPONENT_EXPORT(UI_BASE) extern const char kForceHighContrast[];
COMPONENT_EXPORT(UI_BASE) extern const char kLang[];
COMPONENT_EXPORT(UI_BASE) extern const char kShowOverdrawFeedback[];
COMPONENT_EXPORT(UI_BASE) extern const char kSlowDownCompositingScaleFactor[];
-COMPONENT_EXPORT(UI_BASE) extern const char kTintGlCompositedContent[];
+COMPONENT_EXPORT(UI_BASE) extern const char kTintCompositedContent[];
COMPONENT_EXPORT(UI_BASE) extern const char kTopChromeTouchUi[];
COMPONENT_EXPORT(UI_BASE) extern const char kTopChromeTouchUiAuto[];
COMPONENT_EXPORT(UI_BASE) extern const char kTopChromeTouchUiDisabled[];
diff --git a/chromium/ui/base/ui_base_types.h b/chromium/ui/base/ui_base_types.h
index f290701526d..a74baa1c36e 100644
--- a/chromium/ui/base/ui_base_types.h
+++ b/chromium/ui/base/ui_base_types.h
@@ -78,6 +78,7 @@ enum class ZOrderLevel {
// These are used in histograms, do not remove/renumber entries. Only add at the
// end just before MENU_SOURCE_TYPE_LAST. Also remember to update the
// MenuSourceType enum listing in tools/metrics/histograms/enums.xml.
+// Lastly, any new type here needs to be synced with ui_base_types.mojom.
enum MenuSourceType {
MENU_SOURCE_NONE = 0,
MENU_SOURCE_MOUSE = 1,
diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni
index 5ac2e9ce288..e5203cd56d3 100644
--- a/chromium/ui/base/ui_features.gni
+++ b/chromium/ui/base/ui_features.gni
@@ -6,13 +6,13 @@ import("//build/config/ui.gni")
declare_args() {
# Optional system library.
- use_xkbcommon = use_ozone && is_linux && !is_chromecast
+ use_xkbcommon = use_ozone && (is_linux || is_chromeos) && !is_chromecast
# Whether the platform provides a native accessibility toolkit.
has_native_accessibility = use_atk || is_win || is_mac
# Whether the message center should be included for displaying notifications.
- enable_message_center = is_win || is_mac || is_linux || is_fuchsia
+ enable_message_center = is_win || is_mac || is_linux || is_chromeos || is_fuchsia
}
-enable_hidpi = is_mac || is_win || is_linux || is_ios
+enable_hidpi = is_mac || is_win || is_linux || is_chromeos || is_ios
diff --git a/chromium/ui/base/webui/web_ui_util.cc b/chromium/ui/base/webui/web_ui_util.cc
index ffc30f61a7a..9b8ad27b170 100644
--- a/chromium/ui/base/webui/web_ui_util.cc
+++ b/chromium/ui/base/webui/web_ui_util.cc
@@ -34,6 +34,10 @@
#include "base/win/windows_version.h"
#endif
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "ui/base/ui_base_features.h"
+#endif
+
namespace webui {
namespace {
std::string GetWebUiCssTextDefaults(const std::string& css_template) {
@@ -228,9 +232,13 @@ std::string GetFontFamily() {
// TODO(dnicoara) Remove Ozone check when PlatformFont support is introduced
// into Ozone: crbug.com/320050
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(USE_OZONE)
- font_family = ui::ResourceBundle::GetSharedInstance().GetFont(
- ui::ResourceBundle::BaseFont).GetFontName() + ", " + font_family;
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ if (!features::IsUsingOzonePlatform()) {
+ font_family = ui::ResourceBundle::GetSharedInstance()
+ .GetFont(ui::ResourceBundle::BaseFont)
+ .GetFontName() +
+ ", " + font_family;
+ }
#endif
return font_family;
diff --git a/chromium/ui/base/win/event_creation_utils.cc b/chromium/ui/base/win/event_creation_utils.cc
index 29efc62b3cc..097f5d6b530 100644
--- a/chromium/ui/base/win/event_creation_utils.cc
+++ b/chromium/ui/base/win/event_creation_utils.cc
@@ -18,18 +18,24 @@ bool SendMouseEvent(const gfx::Point& point, int flags) {
INPUT input = {INPUT_MOUSE};
// Get the max screen coordinate for use in computing the normalized absolute
// coordinates required by SendInput.
- const int max_x = ::GetSystemMetrics(SM_CXSCREEN) - 1;
- const int max_y = ::GetSystemMetrics(SM_CYSCREEN) - 1;
- int screen_x = base::ClampToRange(point.x(), 0, max_x);
- int screen_y = base::ClampToRange(point.y(), 0, max_y);
+ const int screen_width = ::GetSystemMetrics(SM_CXSCREEN);
+ const int screen_height = ::GetSystemMetrics(SM_CYSCREEN);
+ int screen_x = base::ClampToRange(point.x(), 0, screen_width - 1);
+ int screen_y = base::ClampToRange(point.y(), 0, screen_height - 1);
+
+ // In normalized absolute coordinates, (0, 0) maps onto the upper-left corner
+ // of the display surface, while (65535, 65535) maps onto the lower-right
+ // corner.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event#remarks
+ static constexpr double kNormalizedScreenSize = 65536.0;
// Form the input data containing the normalized absolute coordinates. As of
// Windows 10 Fall Creators Update, moving to an absolute position of zero
// does not work. It seems that moving to 1,1 does, though.
- input.mi.dx =
- static_cast<LONG>(std::max(1.0, std::ceil(screen_x * (65535.0 / max_x))));
- input.mi.dy =
- static_cast<LONG>(std::max(1.0, std::ceil(screen_y * (65535.0 / max_y))));
+ input.mi.dx = static_cast<LONG>(std::max(
+ 1.0, std::ceil(screen_x * (kNormalizedScreenSize / screen_width))));
+ input.mi.dy = static_cast<LONG>(std::max(
+ 1.0, std::ceil(screen_y * (kNormalizedScreenSize / screen_height))));
input.mi.dwFlags = flags;
return ::SendInput(1, &input, sizeof(input)) == 1;
}
diff --git a/chromium/ui/base/win/shell.cc b/chromium/ui/base/win/shell.cc
index 54e5368a98d..d1ce18112b5 100644
--- a/chromium/ui/base/win/shell.cc
+++ b/chromium/ui/base/win/shell.cc
@@ -83,8 +83,7 @@ bool PreventWindowFromPinning(HWND hwnd) {
DCHECK(hwnd);
Microsoft::WRL::ComPtr<IPropertyStore> pps;
- if (FAILED(
- SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf()))))
+ if (FAILED(SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps))))
return false;
return base::win::SetBooleanValueForPropertyStore(
@@ -102,8 +101,7 @@ void SetAppDetailsForWindow(const base::string16& app_id,
DCHECK(hwnd);
Microsoft::WRL::ComPtr<IPropertyStore> pps;
- if (FAILED(
- SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf()))))
+ if (FAILED(SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps))))
return;
if (!app_id.empty())
@@ -152,8 +150,7 @@ void ClearWindowPropertyStore(HWND hwnd) {
DCHECK(hwnd);
Microsoft::WRL::ComPtr<IPropertyStore> pps;
- if (FAILED(
- SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf()))))
+ if (FAILED(SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps))))
return;
DWORD property_count;
diff --git a/chromium/ui/base/window_open_disposition.cc b/chromium/ui/base/window_open_disposition.cc
index e2e429bb13d..d90ce70031f 100644
--- a/chromium/ui/base/window_open_disposition.cc
+++ b/chromium/ui/base/window_open_disposition.cc
@@ -17,7 +17,7 @@ WindowOpenDisposition DispositionFromClick(
bool shift_key,
WindowOpenDisposition disposition_for_current_tab) {
// MacOS uses meta key (Command key) to spawn new tabs.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (middle_button || meta_key)
#else
if (middle_button || ctrl_key)
diff --git a/chromium/ui/base/x/BUILD.gn b/chromium/ui/base/x/BUILD.gn
index 2737a66ff79..69fc364223c 100644
--- a/chromium/ui/base/x/BUILD.gn
+++ b/chromium/ui/base/x/BUILD.gn
@@ -2,14 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/linux/gtk/gtk.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
assert(use_x11 || ozone_platform_x11)
-jumbo_component("x") {
+component("x") {
output_name = "ui_base_x"
sources = [
@@ -19,6 +18,8 @@ jumbo_component("x") {
"x11_cursor.h",
"x11_cursor_factory.cc",
"x11_cursor_factory.h",
+ "x11_cursor_loader.cc",
+ "x11_cursor_loader.h",
"x11_desktop_window_move_client.cc",
"x11_desktop_window_move_client.h",
"x11_display_manager.cc",
@@ -41,9 +42,10 @@ jumbo_component("x") {
"x11_software_bitmap_presenter.h",
"x11_topmost_window_finder.cc",
"x11_topmost_window_finder.h",
+ "x11_ui_thread.cc",
+ "x11_ui_thread.h",
"x11_util.cc",
"x11_util.h",
- "x11_util_internal.h",
"x11_whole_screen_move_loop.cc",
"x11_whole_screen_move_loop.h",
"x11_window.cc",
@@ -73,6 +75,7 @@ jumbo_component("x") {
"//net",
"//skia",
"//ui/base:data_exchange",
+ "//ui/base:features",
"//ui/base:hit_test",
"//ui/base:wm_role_names",
"//ui/base/clipboard:clipboard_types",
@@ -126,9 +129,13 @@ source_set("test_support") {
source_set("unittests") {
testonly = true
- sources = [ "x11_cursor_factory_unittest.cc" ]
+ sources = [
+ "x11_cursor_factory_unittest.cc",
+ "x11_cursor_loader_unittest.cc",
+ ]
deps = [
":x",
+ "//base",
"//skia",
"//testing/gtest",
"//ui/gfx/geometry",
diff --git a/chromium/ui/base/x/selection_owner.cc b/chromium/ui/base/x/selection_owner.cc
index 755b502b20a..a19d57031d0 100644
--- a/chromium/ui/base/x/selection_owner.cc
+++ b/chromium/ui/base/x/selection_owner.cc
@@ -104,8 +104,7 @@ void SelectionOwner::RetrieveTargets(std::vector<x11::Atom>* targets) {
void SelectionOwner::TakeOwnershipOfSelection(const SelectionFormatMap& data) {
acquired_selection_timestamp_ = X11EventSource::GetInstance()->GetTimestamp();
- SetSelectionOwner(x_window_, selection_name_,
- static_cast<x11::Time>(acquired_selection_timestamp_));
+ SetSelectionOwner(x_window_, selection_name_, acquired_selection_timestamp_);
if (GetSelectionOwner(selection_name_) == x_window_) {
// The X server agrees that we are the selection owner. Commit our data.
diff --git a/chromium/ui/base/x/selection_owner.h b/chromium/ui/base/x/selection_owner.h
index 0c715bd53d0..1dad4df144d 100644
--- a/chromium/ui/base/x/selection_owner.h
+++ b/chromium/ui/base/x/selection_owner.h
@@ -132,7 +132,7 @@ class COMPONENT_EXPORT(UI_BASE_X) SelectionOwner {
x11::Atom selection_name_;
// The time that this instance took ownership of its selection.
- uint32_t acquired_selection_timestamp_;
+ x11::Time acquired_selection_timestamp_;
// The maximum size of data we can put in XChangeProperty().
size_t max_request_size_;
diff --git a/chromium/ui/base/x/selection_requestor.cc b/chromium/ui/base/x/selection_requestor.cc
index 3e2173b4498..7a83c814a75 100644
--- a/chromium/ui/base/x/selection_requestor.cc
+++ b/chromium/ui/base/x/selection_requestor.cc
@@ -35,17 +35,16 @@ static_assert(KSelectionRequestorTimerPeriodMs <= kRequestTimeoutMs,
// Combines |data| into a single std::vector<uint8_t>.
std::vector<uint8_t> CombineData(
- const std::vector<std::vector<uint8_t>>& data) {
- if (data.size() == 1u)
- return data[0];
-
+ const std::vector<scoped_refptr<base::RefCountedMemory>>& data) {
size_t bytes = 0;
for (const auto& datum : data)
- bytes += datum.size();
+ bytes += datum->size();
std::vector<uint8_t> combined;
combined.reserve(bytes);
- for (const auto& datum : data)
- std::copy(datum.begin(), datum.end(), std::back_inserter(combined));
+ for (const auto& datum : data) {
+ std::copy(datum->data(), datum->data() + datum->size(),
+ std::back_inserter(combined));
+ }
return combined;
}
@@ -134,7 +133,7 @@ void SelectionRequestor::OnSelectionNotify(
bool success = false;
if (event_property == x_property_) {
- std::vector<uint8_t> out_data;
+ scoped_refptr<base::RefCountedMemory> out_data;
success = ui::GetRawBytesOfProperty(x_window_, x_property_, &out_data,
&request->out_type);
if (success) {
@@ -167,7 +166,7 @@ void SelectionRequestor::OnPropertyEvent(const x11::Event& event) {
if (!request || !request->data_sent_incrementally)
return;
- std::vector<uint8_t> out_data;
+ scoped_refptr<base::RefCountedMemory> out_data;
x11::Atom out_type = x11::Atom::None;
bool success =
ui::GetRawBytesOfProperty(x_window_, x_property_, &out_data, &out_type);
@@ -190,7 +189,7 @@ void SelectionRequestor::OnPropertyEvent(const x11::Event& event) {
request->timeout = base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
- if (out_data.empty())
+ if (!out_data->size())
CompleteRequest(current_request_index_, true);
}
diff --git a/chromium/ui/base/x/selection_requestor.h b/chromium/ui/base/x/selection_requestor.h
index 9b97d24ff58..9a32c88986e 100644
--- a/chromium/ui/base/x/selection_requestor.h
+++ b/chromium/ui/base/x/selection_requestor.h
@@ -85,7 +85,7 @@ class COMPONENT_EXPORT(UI_BASE) SelectionRequestor {
bool data_sent_incrementally;
// The result data for the XConvertSelection() request.
- std::vector<std::vector<uint8_t>> out_data;
+ std::vector<scoped_refptr<base::RefCountedMemory>> out_data;
x11::Atom out_type;
// Whether the XConvertSelection() request was successful.
diff --git a/chromium/ui/base/x/selection_utils.cc b/chromium/ui/base/x/selection_utils.cc
index 34533b97d69..72e02bcd354 100644
--- a/chromium/ui/base/x/selection_utils.cc
+++ b/chromium/ui/base/x/selection_utils.cc
@@ -19,19 +19,13 @@
namespace ui {
-const char kString[] = "STRING";
-const char kText[] = "TEXT";
-const char kTextPlain[] = "text/plain";
-const char kTextPlainUtf8[] = "text/plain;charset=utf-8";
-const char kUtf8String[] = "UTF8_STRING";
-
std::vector<x11::Atom> GetTextAtomsFrom() {
std::vector<x11::Atom> atoms;
- atoms.push_back(gfx::GetAtom(kUtf8String));
- atoms.push_back(gfx::GetAtom(kString));
- atoms.push_back(gfx::GetAtom(kText));
- atoms.push_back(gfx::GetAtom(kTextPlain));
- atoms.push_back(gfx::GetAtom(kTextPlainUtf8));
+ atoms.push_back(gfx::GetAtom(kMimeTypeLinuxUtf8String));
+ atoms.push_back(gfx::GetAtom(kMimeTypeLinuxString));
+ atoms.push_back(gfx::GetAtom(kMimeTypeLinuxText));
+ atoms.push_back(gfx::GetAtom(kMimeTypeText));
+ atoms.push_back(gfx::GetAtom(kMimeTypeTextUtf8));
return atoms;
}
@@ -176,11 +170,12 @@ size_t SelectionData::GetSize() const {
}
std::string SelectionData::GetText() const {
- if (type_ == gfx::GetAtom(kUtf8String) || type_ == gfx::GetAtom(kText) ||
- type_ == gfx::GetAtom(kTextPlainUtf8)) {
+ if (type_ == gfx::GetAtom(kMimeTypeLinuxUtf8String) ||
+ type_ == gfx::GetAtom(kMimeTypeLinuxText) ||
+ type_ == gfx::GetAtom(kMimeTypeTextUtf8)) {
return RefCountedMemoryToString(memory_);
- } else if (type_ == gfx::GetAtom(kString) ||
- type_ == gfx::GetAtom(kTextPlain)) {
+ } else if (type_ == gfx::GetAtom(kMimeTypeLinuxString) ||
+ type_ == gfx::GetAtom(kMimeTypeText)) {
std::string result;
base::ConvertToUtf8AndNormalize(RefCountedMemoryToString(memory_),
base::kCodepageLatin1, &result);
diff --git a/chromium/ui/base/x/selection_utils.h b/chromium/ui/base/x/selection_utils.h
index 9ca25b373a0..3e90ed3f20b 100644
--- a/chromium/ui/base/x/selection_utils.h
+++ b/chromium/ui/base/x/selection_utils.h
@@ -15,10 +15,6 @@
namespace ui {
class SelectionData;
-COMPONENT_EXPORT(UI_BASE_X) extern const char kString[];
-COMPONENT_EXPORT(UI_BASE_X) extern const char kText[];
-COMPONENT_EXPORT(UI_BASE_X) extern const char kUtf8String[];
-
// Returns a list of all text atoms that we handle.
COMPONENT_EXPORT(UI_BASE_X) std::vector<x11::Atom> GetTextAtomsFrom();
diff --git a/chromium/ui/base/x/x11_cursor.cc b/chromium/ui/base/x/x11_cursor.cc
index 9d55c5db524..c483bd7f798 100644
--- a/chromium/ui/base/x/x11_cursor.cc
+++ b/chromium/ui/base/x/x11_cursor.cc
@@ -4,45 +4,39 @@
#include "ui/base/x/x11_cursor.h"
-#include "base/check_op.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/gfx/geometry/point.h"
+#include "ui/base/x/x11_cursor_loader.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
-X11Cursor::X11Cursor(const SkBitmap& bitmap, const gfx::Point& hotspot) {
- XcursorImage* image = SkBitmapToXcursorImage(bitmap, hotspot);
- xcursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
- XcursorImageDestroy(image);
-}
+X11Cursor::X11Cursor() = default;
+
+X11Cursor::X11Cursor(x11::Cursor cursor) : loaded_(true), xcursor_(cursor) {}
-X11Cursor::X11Cursor(const std::vector<SkBitmap>& bitmaps,
- const gfx::Point& hotspot,
- int frame_delay_ms) {
- // Initialize an XCursorImage for each frame, store all of them in
- // XCursorImages and load the cursor from that.
- XcursorImages* images = XcursorImagesCreate(bitmaps.size());
- images->nimage = bitmaps.size();
- for (size_t frame = 0; frame < bitmaps.size(); ++frame) {
- XcursorImage* x_image = SkBitmapToXcursorImage(bitmaps[frame], hotspot);
- x_image->delay = frame_delay_ms;
- images->images[frame] = x_image;
- }
-
- xcursor_ = XcursorImagesLoadCursor(gfx::GetXDisplay(), images);
- XcursorImagesDestroy(images);
+void X11Cursor::OnCursorLoaded(Callback callback) {
+ if (loaded_)
+ std::move(callback).Run(xcursor_);
+ else
+ callbacks_.push_back(std::move(callback));
}
-X11Cursor::X11Cursor(::Cursor xcursor) : xcursor_(xcursor) {}
+void X11Cursor::SetCursor(x11::Cursor cursor) {
+ DCHECK(!loaded_);
+ loaded_ = true;
+ xcursor_ = cursor;
+ for (auto& callback : callbacks_)
+ std::move(callback).Run(cursor);
+ callbacks_.clear();
+}
-// static
-scoped_refptr<X11Cursor> X11Cursor::CreateInvisible() {
- return base::MakeRefCounted<X11Cursor>(CreateInvisibleCursor());
+x11::Cursor X11Cursor::ReleaseCursor() {
+ return std::exchange(xcursor_, x11::Cursor::None);
}
X11Cursor::~X11Cursor() {
- XFreeCursor(gfx::GetXDisplay(), xcursor_);
+ if (xcursor_ != x11::Cursor::None)
+ x11::Connection::Get()->FreeCursor({xcursor_});
}
} // namespace ui
diff --git a/chromium/ui/base/x/x11_cursor.h b/chromium/ui/base/x/x11_cursor.h
index 1711b947970..dba2dbe88e8 100644
--- a/chromium/ui/base/x/x11_cursor.h
+++ b/chromium/ui/base/x/x11_cursor.h
@@ -9,14 +9,7 @@
#include "base/component_export.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "ui/gfx/x/x11.h"
-
-class SkBitmap;
-
-namespace gfx {
-class Point;
-}
+#include "ui/gfx/x/xproto.h"
namespace ui {
@@ -27,27 +20,34 @@ class COMPONENT_EXPORT(UI_BASE_X) X11Cursor
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
- // Handles creating X11 cursor resources from SkBitmap/hotspot.
- X11Cursor(const SkBitmap& bitmap, const gfx::Point& hotspot);
+ using Callback = base::OnceCallback<void(x11::Cursor)>;
+
+ X11Cursor();
+ explicit X11Cursor(x11::Cursor cursor);
+
X11Cursor(const X11Cursor&) = delete;
X11Cursor& operator=(const X11Cursor&) = delete;
- X11Cursor(const std::vector<SkBitmap>& bitmaps,
- const gfx::Point& hotspot,
- int frame_delay_ms);
- // Wraps an X11 cursor |xcursor|.
- explicit X11Cursor(::Cursor xcursor);
- // Creates a new cursor that is invisible.
- static scoped_refptr<X11Cursor> CreateInvisible();
+ void OnCursorLoaded(Callback callback);
- ::Cursor xcursor() const { return xcursor_; }
+ bool loaded() const { return loaded_; }
+ x11::Cursor xcursor() const { return xcursor_; }
private:
friend class base::RefCounted<X11Cursor>;
+ friend class XCursorLoader;
+
+ void SetCursor(x11::Cursor cursor);
+
+ // This cannot be named Release() since it conflicts with base::RefCounted.
+ x11::Cursor ReleaseCursor();
~X11Cursor();
- ::Cursor xcursor_ = x11::None;
+ bool loaded_ = false;
+ x11::Cursor xcursor_ = x11::Cursor::None;
+
+ std::vector<Callback> callbacks_;
};
} // namespace ui
diff --git a/chromium/ui/base/x/x11_cursor_factory.cc b/chromium/ui/base/x/x11_cursor_factory.cc
index 26624776290..32d7b3b1f90 100644
--- a/chromium/ui/base/x/x11_cursor_factory.cc
+++ b/chromium/ui/base/x/x11_cursor_factory.cc
@@ -7,8 +7,10 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/x/x11_cursor.h"
+#include "ui/base/x/x11_cursor_loader.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
namespace ui {
@@ -23,10 +25,130 @@ PlatformCursor ToPlatformCursor(X11Cursor* cursor) {
return static_cast<PlatformCursor>(cursor);
}
+scoped_refptr<X11Cursor> CreateInvisibleCursor(XCursorLoader* cursor_loader) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(1, 1);
+ return cursor_loader->CreateCursor(bitmap, gfx::Point(0, 0));
+}
+
+// Returns a cursor name, compatible with either X11 or the FreeDesktop.org
+// cursor spec
+// (https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#x_font_cursors
+// and https://www.freedesktop.org/wiki/Specifications/cursor-spec/), followed
+// by fallbacks that can work as replacements in some environments where the
+// original may not be available (e.g. desktop environments other than
+// GNOME and KDE).
+// TODO(hferreiro): each list starts with the FreeDesktop.org icon name but
+// "ns-resize", "ew-resize", "nesw-resize", "nwse-resize", "grab", "grabbing",
+// which were not available in older versions of Breeze, the default KDE theme.
+std::vector<std::string> CursorNamesFromType(mojom::CursorType type) {
+ switch (type) {
+ case mojom::CursorType::kMove:
+ // Returning "move" is the correct thing here, but Blink doesn't make a
+ // distinction between move and all-scroll. Other platforms use a cursor
+ // more consistent with all-scroll, so use that.
+ case mojom::CursorType::kMiddlePanning:
+ case mojom::CursorType::kMiddlePanningVertical:
+ case mojom::CursorType::kMiddlePanningHorizontal:
+ return {"all-scroll", "fleur"};
+ case mojom::CursorType::kEastPanning:
+ case mojom::CursorType::kEastResize:
+ return {"e-resize", "right_side"};
+ case mojom::CursorType::kNorthPanning:
+ case mojom::CursorType::kNorthResize:
+ return {"n-resize", "top_side"};
+ case mojom::CursorType::kNorthEastPanning:
+ case mojom::CursorType::kNorthEastResize:
+ return {"ne-resize", "top_right_corner"};
+ case mojom::CursorType::kNorthWestPanning:
+ case mojom::CursorType::kNorthWestResize:
+ return {"nw-resize", "top_left_corner"};
+ case mojom::CursorType::kSouthPanning:
+ case mojom::CursorType::kSouthResize:
+ return {"s-resize", "bottom_side"};
+ case mojom::CursorType::kSouthEastPanning:
+ case mojom::CursorType::kSouthEastResize:
+ return {"se-resize", "bottom_right_corner"};
+ case mojom::CursorType::kSouthWestPanning:
+ case mojom::CursorType::kSouthWestResize:
+ return {"sw-resize", "bottom_left_corner"};
+ case mojom::CursorType::kWestPanning:
+ case mojom::CursorType::kWestResize:
+ return {"w-resize", "left_side"};
+ case mojom::CursorType::kNone:
+ return {"none"};
+ case mojom::CursorType::kGrab:
+ return {"openhand", "grab"};
+ case mojom::CursorType::kGrabbing:
+ return {"closedhand", "grabbing", "hand2"};
+ case mojom::CursorType::kCross:
+ return {"crosshair", "cross"};
+ case mojom::CursorType::kHand:
+ return {"pointer", "hand", "hand2"};
+ case mojom::CursorType::kIBeam:
+ return {"text", "xterm"};
+ case mojom::CursorType::kProgress:
+ return {"progress", "left_ptr_watch", "watch"};
+ case mojom::CursorType::kWait:
+ return {"wait", "watch"};
+ case mojom::CursorType::kHelp:
+ return {"help"};
+ case mojom::CursorType::kNorthSouthResize:
+ return {"sb_v_double_arrow", "ns-resize"};
+ case mojom::CursorType::kEastWestResize:
+ return {"sb_h_double_arrow", "ew-resize"};
+ case mojom::CursorType::kColumnResize:
+ return {"col-resize", "sb_h_double_arrow"};
+ case mojom::CursorType::kRowResize:
+ return {"row-resize", "sb_v_double_arrow"};
+ case mojom::CursorType::kNorthEastSouthWestResize:
+ return {"size_bdiag", "nesw-resize", "fd_double_arrow"};
+ case mojom::CursorType::kNorthWestSouthEastResize:
+ return {"size_fdiag", "nwse-resize", "bd_double_arrow"};
+ case mojom::CursorType::kVerticalText:
+ return {"vertical-text"};
+ case mojom::CursorType::kZoomIn:
+ return {"zoom-in"};
+ case mojom::CursorType::kZoomOut:
+ return {"zoom-out"};
+ case mojom::CursorType::kCell:
+ return {"cell", "plus"};
+ case mojom::CursorType::kContextMenu:
+ return {"context-menu"};
+ case mojom::CursorType::kAlias:
+ return {"alias"};
+ case mojom::CursorType::kNoDrop:
+ return {"no-drop"};
+ case mojom::CursorType::kCopy:
+ return {"copy"};
+ case mojom::CursorType::kNotAllowed:
+ return {"not-allowed", "crossed_circle"};
+ case mojom::CursorType::kDndNone:
+ return {"dnd-none", "hand2"};
+ case mojom::CursorType::kDndMove:
+ return {"dnd-move", "hand2"};
+ case mojom::CursorType::kDndCopy:
+ return {"dnd-copy", "hand2"};
+ case mojom::CursorType::kDndLink:
+ return {"dnd-link", "hand2"};
+ case mojom::CursorType::kCustom:
+ // kCustom is for custom image cursors. The platform cursor will be set
+ // at WebCursor::GetPlatformCursor().
+ NOTREACHED();
+ FALLTHROUGH;
+ case mojom::CursorType::kNull:
+ case mojom::CursorType::kPointer:
+ return {"left_ptr"};
+ }
+ NOTREACHED();
+ return {"left_ptr"};
+}
+
} // namespace
X11CursorFactory::X11CursorFactory()
- : invisible_cursor_(X11Cursor::CreateInvisible()) {}
+ : cursor_loader_(std::make_unique<XCursorLoader>(x11::Connection::Get())),
+ invisible_cursor_(CreateInvisibleCursor(cursor_loader_.get())) {}
X11CursorFactory::~X11CursorFactory() = default;
@@ -53,7 +175,7 @@ PlatformCursor X11CursorFactory::CreateImageCursor(const SkBitmap& bitmap,
return ToPlatformCursor(invisible_cursor_.get());
}
- auto cursor = base::MakeRefCounted<X11Cursor>(bitmap, hotspot);
+ auto cursor = cursor_loader_->CreateCursor(bitmap, hotspot);
cursor->AddRef();
return ToPlatformCursor(cursor.get());
}
@@ -62,8 +184,11 @@ PlatformCursor X11CursorFactory::CreateAnimatedCursor(
const std::vector<SkBitmap>& bitmaps,
const gfx::Point& hotspot,
int frame_delay_ms) {
- auto cursor =
- base::MakeRefCounted<X11Cursor>(bitmaps, hotspot, frame_delay_ms);
+ std::vector<XCursorLoader::Image> images;
+ images.reserve(bitmaps.size());
+ for (const auto& bitmap : bitmaps)
+ images.push_back(XCursorLoader::Image{bitmap, hotspot, frame_delay_ms});
+ auto cursor = cursor_loader_->CreateCursor(images);
cursor->AddRef();
return ToPlatformCursor(cursor.get());
}
@@ -84,12 +209,10 @@ void X11CursorFactory::ObserveThemeChanges() {
void X11CursorFactory::OnCursorThemeNameChanged(
const std::string& cursor_theme_name) {
- XcursorSetTheme(gfx::GetXDisplay(), cursor_theme_name.c_str());
ClearThemeCursors();
}
void X11CursorFactory::OnCursorThemeSizeChanged(int cursor_theme_size) {
- XcursorSetDefaultSize(gfx::GetXDisplay(), cursor_theme_size);
ClearThemeCursors();
}
@@ -100,11 +223,8 @@ scoped_refptr<X11Cursor> X11CursorFactory::GetDefaultCursorInternal(
if (!default_cursors_.count(type)) {
// Try to load a predefined X11 cursor.
- ::Cursor xcursor = LoadCursorFromType(type);
- if (xcursor == x11::None)
- return nullptr;
- auto cursor = base::MakeRefCounted<X11Cursor>(xcursor);
- default_cursors_[type] = cursor;
+ default_cursors_[type] =
+ cursor_loader_->LoadCursor(CursorNamesFromType(type));
}
// Returns owned default cursor for this type.
diff --git a/chromium/ui/base/x/x11_cursor_factory.h b/chromium/ui/base/x/x11_cursor_factory.h
index 424511854ef..464c75f2306 100644
--- a/chromium/ui/base/x/x11_cursor_factory.h
+++ b/chromium/ui/base/x/x11_cursor_factory.h
@@ -19,6 +19,7 @@
namespace ui {
class X11Cursor;
+class XCursorLoader;
// CursorFactoryOzone implementation for X11 cursors.
class COMPONENT_EXPORT(UI_BASE_X) X11CursorFactory
@@ -49,6 +50,8 @@ class COMPONENT_EXPORT(UI_BASE_X) X11CursorFactory
void ClearThemeCursors();
+ std::unique_ptr<XCursorLoader> cursor_loader_;
+
// Loads/caches default cursor or returns cached version.
scoped_refptr<X11Cursor> GetDefaultCursorInternal(mojom::CursorType type);
diff --git a/chromium/ui/base/x/x11_cursor_loader.cc b/chromium/ui/base/x/x11_cursor_loader.cc
new file mode 100644
index 00000000000..0fd30ace93c
--- /dev/null
+++ b/chromium/ui/base/x/x11_cursor_loader.cc
@@ -0,0 +1,549 @@
+// 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 "ui/base/x/x11_cursor_loader.h"
+
+#include <limits>
+#include <string>
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/task_runner_util.h"
+#include "ui/base/cursor/cursor_theme_manager.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace ui {
+
+namespace {
+
+// These cursor names are indexed by their ID in a cursor font.
+constexpr const char* cursor_names[] = {
+ "X_cursor",
+ "arrow",
+ "based_arrow_down",
+ "based_arrow_up",
+ "boat",
+ "bogosity",
+ "bottom_left_corner",
+ "bottom_right_corner",
+ "bottom_side",
+ "bottom_tee",
+ "box_spiral",
+ "center_ptr",
+ "circle",
+ "clock",
+ "coffee_mug",
+ "cross",
+ "cross_reverse",
+ "crosshair",
+ "diamond_cross",
+ "dot",
+ "dotbox",
+ "double_arrow",
+ "draft_large",
+ "draft_small",
+ "draped_box",
+ "exchange",
+ "fleur",
+ "gobbler",
+ "gumby",
+ "hand1",
+ "hand2",
+ "heart",
+ "icon",
+ "iron_cross",
+ "left_ptr",
+ "left_side",
+ "left_tee",
+ "leftbutton",
+ "ll_angle",
+ "lr_angle",
+ "man",
+ "middlebutton",
+ "mouse",
+ "pencil",
+ "pirate",
+ "plus",
+ "question_arrow",
+ "right_ptr",
+ "right_side",
+ "right_tee",
+ "rightbutton",
+ "rtl_logo",
+ "sailboat",
+ "sb_down_arrow",
+ "sb_h_double_arrow",
+ "sb_left_arrow",
+ "sb_right_arrow",
+ "sb_up_arrow",
+ "sb_v_double_arrow",
+ "shuttle",
+ "sizing",
+ "spider",
+ "spraycan",
+ "star",
+ "target",
+ "tcross",
+ "top_left_arrow",
+ "top_left_corner",
+ "top_right_corner",
+ "top_side",
+ "top_tee",
+ "trek",
+ "ul_angle",
+ "umbrella",
+ "ur_angle",
+ "watch",
+ "xterm",
+};
+
+std::string GetEnv(const std::string& var) {
+ auto env = base::Environment::Create();
+ std::string value;
+ env->GetVar(var, &value);
+ return value;
+}
+
+std::string CursorPath() {
+ constexpr const char kDefaultPath[] =
+ "~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons";
+ std::string path = GetEnv("XCURSOR_PATH");
+ return path.empty() ? kDefaultPath : path;
+}
+
+x11::Render::PictFormat GetRenderARGBFormat(
+ const x11::Render::QueryPictFormatsReply& formats) {
+ for (const auto& format : formats.formats) {
+ if (format.type == x11::Render::PictType::Direct && format.depth == 32 &&
+ format.direct.alpha_shift == 24 && format.direct.alpha_mask == 0xff &&
+ format.direct.red_shift == 16 && format.direct.red_mask == 0xff &&
+ format.direct.green_shift == 8 && format.direct.green_mask == 0xff &&
+ format.direct.blue_shift == 0 && format.direct.blue_mask == 0xff) {
+ return format.id;
+ }
+ }
+ return {};
+}
+
+std::vector<std::string> GetBaseThemes(const base::FilePath& abspath) {
+ DCHECK(abspath.IsAbsolute());
+ constexpr const char kKeyInherits[] = "Inherits";
+ std::string contents;
+ base::ReadFileToString(abspath, &contents);
+ base::StringPairs pairs;
+ base::SplitStringIntoKeyValuePairs(contents, '=', '\n', &pairs);
+ for (const auto& pair : pairs) {
+ if (base::TrimWhitespaceASCII(pair.first, base::TRIM_ALL) == kKeyInherits) {
+ return base::SplitString(pair.second, ",;", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ }
+ }
+ return {};
+}
+
+base::FilePath CanonicalizePath(base::FilePath path) {
+ std::vector<std::string> components;
+ path.GetComponents(&components);
+ if (components[0] == "~") {
+ path = base::GetHomeDir();
+ for (size_t i = 1; i < components.size(); i++)
+ path = path.Append(components[i]);
+ } else {
+ path = base::MakeAbsoluteFilePath(path);
+ }
+ return path;
+}
+
+// Reads the cursor called |name| for the theme named |theme|. Searches all
+// paths in the XCursor path and parent themes.
+scoped_refptr<base::RefCountedMemory> ReadCursorFromTheme(
+ const std::string& theme,
+ const std::string& name) {
+ constexpr const char kCursorDir[] = "cursors";
+ constexpr const char kThemeInfo[] = "index.theme";
+ std::vector<std::string> base_themes;
+
+ auto paths = base::SplitString(CursorPath(), ":", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ for (const auto& path : paths) {
+ auto dir = CanonicalizePath(base::FilePath(path));
+ if (dir.empty())
+ continue;
+ base::FilePath theme_dir = dir.Append(theme);
+ base::FilePath cursor_dir = theme_dir.Append(kCursorDir);
+
+ std::string contents;
+ if (base::ReadFileToString(cursor_dir.Append(name), &contents))
+ return base::RefCountedString::TakeString(&contents);
+
+ if (base_themes.empty())
+ base_themes = GetBaseThemes(theme_dir.Append(kThemeInfo));
+ }
+
+ for (const auto& path : base_themes) {
+ if (auto contents = ReadCursorFromTheme(path, name))
+ return contents;
+ }
+
+ return nullptr;
+}
+
+scoped_refptr<base::RefCountedMemory> ReadCursorFile(
+ const std::string& name,
+ const std::string& rm_xcursor_theme) {
+ constexpr const char kDefaultTheme[] = "default";
+ std::string themes[] = {
+ // The toolkit theme has the highest priority.
+ CursorThemeManager::GetInstance()
+ ? CursorThemeManager::GetInstance()->GetCursorThemeName()
+ : std::string(),
+
+ // Next try Xcursor.theme.
+ rm_xcursor_theme,
+
+ // As a last resort, use the default theme.
+ kDefaultTheme,
+ };
+
+ for (const std::string& theme : themes) {
+ if (theme.empty())
+ continue;
+ if (auto file = ReadCursorFromTheme(theme, name))
+ return file;
+ }
+ return nullptr;
+}
+
+std::vector<XCursorLoader::Image> ReadCursorImages(
+ const std::vector<std::string>& names,
+ const std::string& rm_xcursor_theme,
+ uint32_t preferred_size) {
+ // Fallback on a left pointer if possible.
+ auto names_copy = names;
+ names_copy.push_back("left_ptr");
+ for (const auto& name : names_copy) {
+ if (auto contents = ReadCursorFile(name, rm_xcursor_theme)) {
+ auto images = ParseCursorFile(contents, preferred_size);
+ if (!images.empty())
+ return images;
+ }
+ }
+ return {};
+}
+
+} // namespace
+
+XCursorLoader::XCursorLoader(x11::Connection* connection)
+ : connection_(connection) {
+ auto ver_cookie = connection_->render().QueryVersion(
+ {x11::Render::major_version, x11::Render::minor_version});
+ auto pf_cookie = connection_->render().QueryPictFormats({});
+ cursor_font_ = connection_->GenerateId<x11::Font>();
+ connection_->OpenFont({cursor_font_, "cursor"});
+
+ std::string resource_manager;
+ if (ui::GetStringProperty(connection_->default_root(), "RESOURCE_MANAGER",
+ &resource_manager)) {
+ ParseXResources(resource_manager);
+ }
+
+ if (auto reply = ver_cookie.Sync()) {
+ render_version_ =
+ base::Version({reply->major_version, reply->minor_version});
+ }
+
+ if (auto pf_reply = pf_cookie.Sync())
+ pict_format_ = GetRenderARGBFormat(*pf_reply.reply);
+
+ for (uint16_t i = 0; i < base::size(cursor_names); i++)
+ cursor_name_to_char_[cursor_names[i]] = i;
+}
+
+XCursorLoader::~XCursorLoader() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::LoadCursor(
+ const std::vector<std::string>& names) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto cursor = base::MakeRefCounted<X11Cursor>();
+ if (SupportsCreateCursor()) {
+ base::PostTaskAndReplyWithResult(
+ FROM_HERE,
+ {base::ThreadPool(), base::MayBlock(),
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::BindOnce(ReadCursorImages, names, rm_xcursor_theme_,
+ GetPreferredCursorSize()),
+ base::BindOnce(&XCursorLoader::LoadCursorImpl,
+ weak_factory_.GetWeakPtr(), cursor, names));
+ } else {
+ LoadCursorImpl(cursor, names, {});
+ }
+ return cursor;
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::CreateCursor(
+ const std::vector<Image>& images) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ std::vector<scoped_refptr<X11Cursor>> cursors;
+ std::vector<x11::Render::AnimationCursorElement> elements;
+ cursors.reserve(images.size());
+ elements.reserve(images.size());
+
+ for (const Image& image : images) {
+ auto cursor = CreateCursor(image.bitmap, image.hotspot);
+ cursors.push_back(cursor);
+ elements.push_back(x11::Render::AnimationCursorElement{
+ cursor->xcursor_, image.frame_delay_ms});
+ }
+
+ if (elements.empty())
+ return nullptr;
+ if (elements.size() == 1 || !SupportsCreateAnimCursor())
+ return cursors[0];
+
+ auto cursor = connection_->GenerateId<x11::Cursor>();
+ connection_->render().CreateAnimCursor({cursor, elements});
+ return base::MakeRefCounted<X11Cursor>(cursor);
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::CreateCursor(
+ const SkBitmap& bitmap,
+ const gfx::Point& hotspot) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto pixmap = connection_->GenerateId<x11::Pixmap>();
+ auto gc = connection_->GenerateId<x11::GraphicsContext>();
+ int width = bitmap.width();
+ int height = bitmap.height();
+ connection_->CreatePixmap(
+ {32, pixmap, connection_->default_root(), width, height});
+ connection_->CreateGC({gc, pixmap});
+
+ size_t size = bitmap.computeByteSize();
+ std::vector<uint8_t> vec(size);
+ memcpy(vec.data(), bitmap.getPixels(), size);
+ auto* connection = x11::Connection::Get();
+ x11::PutImageRequest put_image_request{
+ .format = x11::ImageFormat::ZPixmap,
+ .drawable = static_cast<x11::Pixmap>(pixmap),
+ .gc = static_cast<x11::GraphicsContext>(gc),
+ .width = width,
+ .height = height,
+ .depth = 32,
+ .data = base::RefCountedBytes::TakeVector(&vec),
+ };
+ connection->PutImage(put_image_request);
+
+ x11::Render::Picture pic = connection_->GenerateId<x11::Render::Picture>();
+ connection_->render().CreatePicture({pic, pixmap, pict_format_});
+
+ auto cursor = connection_->GenerateId<x11::Cursor>();
+ connection_->render().CreateCursor({cursor, pic, hotspot.x(), hotspot.y()});
+
+ connection_->render().FreePicture({pic});
+ connection_->FreePixmap({pixmap});
+ connection_->FreeGC({gc});
+
+ return base::MakeRefCounted<X11Cursor>(cursor);
+}
+
+void XCursorLoader::LoadCursorImpl(
+ scoped_refptr<X11Cursor> cursor,
+ const std::vector<std::string>& names,
+ const std::vector<XCursorLoader::Image>& images) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto xcursor = connection_->GenerateId<x11::Cursor>();
+ if (!images.empty()) {
+ xcursor = CreateCursor(images)->ReleaseCursor();
+ } else {
+ // Fallback to using a font cursor.
+ auto core_char = CursorNamesToChar(names);
+ connection_->CreateGlyphCursor({xcursor, cursor_font_, cursor_font_,
+ 2 * core_char, 2 * core_char + 1, 0, 0, 0,
+ 65535, 65535, 65535});
+ }
+ cursor->SetCursor(xcursor);
+}
+
+uint32_t XCursorLoader::GetPreferredCursorSize() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ constexpr const char kXcursorSizeEnv[] = "XCURSOR_SIZE";
+ constexpr unsigned int kCursorSizeInchNum = 16;
+ constexpr unsigned int kCursorSizeInchDen = 72;
+ constexpr unsigned int kScreenCursorRatio = 48;
+
+ // Allow the XCURSOR_SIZE environment variable to override GTK settings.
+ int size;
+ if (base::StringToInt(GetEnv(kXcursorSizeEnv), &size) && size > 0)
+ return size;
+
+ // Let the toolkit have the next say.
+ auto* manager = CursorThemeManager::GetInstance();
+ size = manager ? manager->GetCursorThemeSize() : 0;
+ if (size > 0)
+ return size;
+
+ // Use Xcursor.size from RESOURCE_MANAGER if available.
+ if (rm_xcursor_size_)
+ return rm_xcursor_size_;
+
+ // Guess the cursor size based on the DPI.
+ if (rm_xft_dpi_)
+ return rm_xft_dpi_ * kCursorSizeInchNum / kCursorSizeInchDen;
+
+ // As a last resort, guess the cursor size based on the screen size.
+ const auto& screen = connection_->default_screen();
+ return std::min(screen.width_in_pixels, screen.height_in_pixels) /
+ kScreenCursorRatio;
+}
+
+void XCursorLoader::ParseXResources(const std::string& resources) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ base::StringPairs pairs;
+ base::SplitStringIntoKeyValuePairs(resources, ':', '\n', &pairs);
+ for (const auto& pair : pairs) {
+ auto key = base::TrimWhitespaceASCII(pair.first, base::TRIM_ALL);
+ auto value = base::TrimWhitespaceASCII(pair.second, base::TRIM_ALL);
+
+ if (key == "Xcursor.theme")
+ rm_xcursor_theme_ = std::string(value);
+ else if (key == "Xcursor.size")
+ base::StringToUint(value, &rm_xcursor_size_);
+ else if (key == "Xft.dpi")
+ base::StringToUint(value, &rm_xft_dpi_);
+ }
+}
+
+uint16_t XCursorLoader::CursorNamesToChar(
+ const std::vector<std::string>& names) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (const auto& name : names) {
+ auto it = cursor_name_to_char_.find(name);
+ if (it != cursor_name_to_char_.end())
+ return it->second;
+ }
+ // Use a left pointer as a fallback.
+ return 0;
+}
+
+bool XCursorLoader::SupportsCreateCursor() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return render_version_.IsValid() && render_version_ >= base::Version("0.5");
+}
+
+bool XCursorLoader::SupportsCreateAnimCursor() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return render_version_.IsValid() && render_version_ >= base::Version("0.8");
+}
+
+// This is ported from libxcb-cursor's parse_cursor_file.c:
+// https://gitlab.freedesktop.org/xorg/lib/libxcb-cursor/-/blob/master/cursor/parse_cursor_file.c
+std::vector<XCursorLoader::Image> ParseCursorFile(
+ scoped_refptr<base::RefCountedMemory> file,
+ uint32_t preferred_size) {
+ constexpr uint32_t kMagic = 0x72756358;
+ constexpr uint32_t kImageType = 0xfffd0002;
+
+ const uint8_t* mem = file->data();
+ size_t offset = 0;
+
+ auto ReadU32s = [&](void* dest, size_t len) {
+ DCHECK_EQ(len % 4, 0u);
+ if (offset + len > file->size())
+ return false;
+ const auto* src32 = reinterpret_cast<const uint32_t*>(mem + offset);
+ auto* dest32 = reinterpret_cast<uint32_t*>(dest);
+ for (size_t i = 0; i < len / 4; i++)
+ dest32[i] = base::ByteSwapToLE32(src32[i]);
+ offset += len;
+ return true;
+ };
+
+ struct FileHeader {
+ uint32_t magic;
+ uint32_t header;
+ uint32_t version;
+ uint32_t ntoc;
+ } header;
+ if (!ReadU32s(&header, sizeof(FileHeader)) || header.magic != kMagic)
+ return {};
+
+ struct TableOfContentsEntry {
+ uint32_t type;
+ uint32_t subtype;
+ uint32_t position;
+ };
+ std::vector<TableOfContentsEntry> toc;
+ toc.reserve(header.ntoc);
+ for (uint32_t i = 0; i < header.ntoc; i++) {
+ TableOfContentsEntry entry;
+ if (!ReadU32s(&entry, sizeof(TableOfContentsEntry)))
+ return {};
+ toc.push_back(entry);
+ }
+
+ uint32_t best_size = std::numeric_limits<uint32_t>::max();
+ for (const auto& entry : toc) {
+ auto delta = [](uint32_t x, uint32_t y) {
+ return std::max(x, y) - std::min(x, y);
+ };
+ if (entry.type != kImageType)
+ continue;
+ if (delta(entry.subtype, preferred_size) < delta(best_size, preferred_size))
+ best_size = entry.subtype;
+ }
+
+ std::vector<XCursorLoader::Image> images;
+ for (const auto& entry : toc) {
+ if (entry.type != kImageType || entry.subtype != best_size)
+ continue;
+ offset = entry.position;
+ struct ChunkHeader {
+ uint32_t header;
+ uint32_t type;
+ uint32_t subtype;
+ uint32_t version;
+ } chunk_header;
+ if (!ReadU32s(&chunk_header, sizeof(ChunkHeader)) ||
+ chunk_header.type != entry.type ||
+ chunk_header.subtype != entry.subtype) {
+ continue;
+ }
+
+ struct ImageHeader {
+ uint32_t width;
+ uint32_t height;
+ uint32_t xhot;
+ uint32_t yhot;
+ uint32_t delay;
+ } image;
+ if (!ReadU32s(&image, sizeof(ImageHeader)))
+ continue;
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(image.width, image.height);
+ if (!ReadU32s(bitmap.getPixels(), bitmap.computeByteSize()))
+ continue;
+ images.push_back(XCursorLoader::Image{
+ bitmap, gfx::Point(image.xhot, image.yhot), image.delay});
+ }
+ return images;
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/x/x11_cursor_loader.h b/chromium/ui/base/x/x11_cursor_loader.h
new file mode 100644
index 00000000000..851f5245a90
--- /dev/null
+++ b/chromium/ui/base/x/x11_cursor_loader.h
@@ -0,0 +1,92 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_X_X11_CURSOR_LOADER_H_
+#define UI_BASE_X_X11_CURSOR_LOADER_H_
+
+#include <unordered_map>
+
+#include "base/component_export.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/version.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/x/x11_cursor.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/render.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace ui {
+
+// This is a port of libxcursor.
+class COMPONENT_EXPORT(UI_BASE_X) XCursorLoader {
+ public:
+ struct Image {
+ SkBitmap bitmap;
+ gfx::Point hotspot;
+ int frame_delay_ms;
+ };
+
+ explicit XCursorLoader(x11::Connection* connection);
+
+ ~XCursorLoader();
+
+ // Loads a system cursor for the given |names|. The cursor is loaded
+ // asynchronously, but some cursor (possibly a fallback) is always guaranteed
+ // to be loaded.
+ scoped_refptr<X11Cursor> LoadCursor(const std::vector<std::string>& names);
+
+ // Synchronously loads a cursor for the given |bitmap| or |images|. nullptr
+ // may be returned if image cursors are not supported.
+ scoped_refptr<X11Cursor> CreateCursor(const std::vector<Image>& images);
+ scoped_refptr<X11Cursor> CreateCursor(const SkBitmap& bitmap,
+ const gfx::Point& hotspot);
+
+ private:
+ friend class XCursorLoaderTest;
+
+ void LoadCursorImpl(scoped_refptr<X11Cursor> cursor,
+ const std::vector<std::string>& names,
+ const std::vector<XCursorLoader::Image>& images);
+
+ uint32_t GetPreferredCursorSize() const;
+
+ // Populate the |rm_*| variables from the value of the RESOURCE_MANAGER
+ // property on the root window.
+ void ParseXResources(const std::string& resources);
+
+ uint16_t CursorNamesToChar(const std::vector<std::string>& names) const;
+
+ bool SupportsCreateCursor() const;
+ bool SupportsCreateAnimCursor() const;
+
+ x11::Connection* connection_ = nullptr;
+
+ x11::Font cursor_font_ = x11::Font::None;
+
+ x11::Render::PictFormat pict_format_{};
+
+ // Values obtained from the RESOURCE_MANAGER property on the root window.
+ std::string rm_xcursor_theme_;
+ unsigned int rm_xcursor_size_ = 0;
+ unsigned int rm_xft_dpi_ = 0;
+
+ base::Version render_version_;
+
+ std::unordered_map<std::string, uint16_t> cursor_name_to_char_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ base::WeakPtrFactory<XCursorLoader> weak_factory_{this};
+};
+
+COMPONENT_EXPORT(UI_BASE_X)
+std::vector<XCursorLoader::Image> ParseCursorFile(
+ scoped_refptr<base::RefCountedMemory> file,
+ uint32_t preferred_size);
+
+} // namespace ui
+
+#endif // UI_BASE_X_X11_CURSOR_LOADER_H_
diff --git a/chromium/ui/base/x/x11_cursor_loader_unittest.cc b/chromium/ui/base/x/x11_cursor_loader_unittest.cc
new file mode 100644
index 00000000000..52a75c04de3
--- /dev/null
+++ b/chromium/ui/base/x/x11_cursor_loader_unittest.cc
@@ -0,0 +1,262 @@
+// 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 "ui/base/x/x11_cursor_loader.h"
+
+#undef Bool
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/sys_byteorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+namespace {
+std::vector<XCursorLoader::Image> ParseFile(std::vector<uint32_t>* data,
+ uint32_t preferred_size) {
+ for (uint32_t& i : *data)
+ i = base::ByteSwapToLE32(i);
+ std::vector<uint8_t> vec(data->size() * sizeof(uint32_t));
+ memcpy(vec.data(), data->data(), vec.size());
+ return ParseCursorFile(base::RefCountedBytes::TakeVector(&vec),
+ preferred_size);
+}
+
+} // namespace
+
+TEST(XCursorLoaderTest, Basic) {
+ std::vector<uint32_t> file{
+ // magic number
+ 0x72756358,
+ // bytes in header
+ 28,
+ // version
+ 1,
+ // number of TOC entries
+ 1,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 1,
+ // position
+ 28,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 1,
+ // chunk type version
+ 1,
+ // image width
+ 1,
+ // image height
+ 1,
+ // xhot
+ 1234,
+ // yhot
+ 5678,
+ // delay
+ 123,
+ // chunk data (ARGB image)
+ 0xff123456,
+ };
+ auto images = ParseFile(&file, 1);
+ ASSERT_EQ(images.size(), 1ul);
+ EXPECT_EQ(images[0].frame_delay_ms, 123);
+ EXPECT_EQ(images[0].bitmap.width(), 1);
+ EXPECT_EQ(images[0].bitmap.height(), 1);
+ EXPECT_EQ(images[0].hotspot.x(), 1234);
+ EXPECT_EQ(images[0].hotspot.y(), 5678);
+ EXPECT_EQ(images[0].bitmap.getColor(0, 0), 0xff123456);
+}
+
+TEST(XCursorLoaderTest, BestSize) {
+ std::vector<uint32_t> file{
+ // magic number
+ 0x72756358,
+ // bytes in header
+ 28,
+ // version
+ 1,
+ // number of TOC entries
+ 3,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 1,
+ // position
+ 52,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 2,
+ // position
+ 92,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 3,
+ // position
+ 144,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 1,
+ // chunk type version
+ 1,
+ // image width
+ 1,
+ // image height
+ 1,
+ // xhot
+ 0,
+ // yhot
+ 0,
+ // delay
+ 0,
+ // chunk data (ARGB image)
+ 0xffffffff,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 2,
+ // chunk type version
+ 1,
+ // image width
+ 2,
+ // image height
+ 2,
+ // xhot
+ 0,
+ // yhot
+ 0,
+ // delay
+ 0,
+ // chunk data (ARGB image)
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 3,
+ // chunk type version
+ 1,
+ // image width
+ 3,
+ // image height
+ 3,
+ // xhot
+ 0,
+ // yhot
+ 0,
+ // delay
+ 0,
+ // chunk data (ARGB image)
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ };
+ auto images = ParseFile(&file, 2);
+ ASSERT_EQ(images.size(), 1ul);
+ EXPECT_EQ(images[0].bitmap.width(), 2);
+ EXPECT_EQ(images[0].bitmap.height(), 2);
+}
+
+TEST(XCursorLoaderTest, Animated) {
+ std::vector<uint32_t> file{
+ // magic number
+ 0x72756358,
+ // bytes in header
+ 28,
+ // version
+ 1,
+ // number of TOC entries
+ 2,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 1,
+ // position
+ 40,
+
+ // type (image)
+ 0xfffd0002,
+ // subtype (image size)
+ 1,
+ // position
+ 80,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 1,
+ // chunk type version
+ 1,
+ // image width
+ 1,
+ // image height
+ 1,
+ // xhot
+ 0,
+ // yhot
+ 0,
+ // delay
+ 500,
+ // chunk data (ARGB image)
+ 0xff123456,
+
+ // bytes in header
+ 16,
+ // chunk type (image)
+ 0xfffd0002,
+ // chunk subtype (image size)
+ 1,
+ // chunk type version
+ 1,
+ // image width
+ 1,
+ // image height
+ 1,
+ // xhot
+ 0,
+ // yhot
+ 0,
+ // delay
+ 500,
+ // chunk data (ARGB image)
+ 0xff123456,
+ };
+ auto images = ParseFile(&file, 1);
+ ASSERT_EQ(images.size(), 2ul);
+ EXPECT_EQ(images[0].frame_delay_ms, 500);
+ EXPECT_EQ(images[1].frame_delay_ms, 500);
+}
+
+} // namespace ui
diff --git a/chromium/ui/base/x/x11_display_manager.cc b/chromium/ui/base/x/x11_display_manager.cc
index 623d809dd02..ac1e99da7e2 100644
--- a/chromium/ui/base/x/x11_display_manager.cc
+++ b/chromium/ui/base/x/x11_display_manager.cc
@@ -12,6 +12,7 @@
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
@@ -61,25 +62,11 @@ void XDisplayManager::RemoveObserver(display::DisplayObserver* observer) {
change_notifier_.RemoveObserver(observer);
}
-bool XDisplayManager::CanProcessEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- return xev.type - xrandr_event_base_ ==
- x11::RandR::ScreenChangeNotifyEvent::opcode ||
- xev.type - xrandr_event_base_ == x11::RandR::NotifyEvent::opcode ||
- (xev.type == PropertyNotify &&
- static_cast<x11::Window>(xev.xproperty.window) == x_root_window_ &&
- xev.xproperty.atom ==
- static_cast<uint32_t>(gfx::GetAtom("_NET_WORKAREA")));
-}
-
-bool XDisplayManager::ProcessEvent(x11::Event* x11_event) {
- DCHECK(x11_event);
- XEvent* xev = &x11_event->xlib_event();
- int ev_type = xev->type - xrandr_event_base_;
- if (ev_type == x11::RandR::NotifyEvent::opcode ||
- (xev->type == PropertyNotify &&
- xev->xproperty.atom ==
- static_cast<uint32_t>(gfx::GetAtom("_NET_WORKAREA")))) {
+bool XDisplayManager::ProcessEvent(x11::Event* xev) {
+ DCHECK(xev);
+ auto* prop = xev->As<x11::PropertyNotifyEvent>();
+ if (xev->As<x11::RandR::NotifyEvent>() ||
+ (prop && prop->atom == gfx::GetAtom("_NET_WORKAREA"))) {
DispatchDelayedDisplayListUpdate();
return true;
}
diff --git a/chromium/ui/base/x/x11_display_manager.h b/chromium/ui/base/x/x11_display_manager.h
index d638460ad1b..ec6c676249e 100644
--- a/chromium/ui/base/x/x11_display_manager.h
+++ b/chromium/ui/base/x/x11_display_manager.h
@@ -48,7 +48,6 @@ class COMPONENT_EXPORT(UI_BASE_X) XDisplayManager
void Init();
bool IsXrandrAvailable() const;
- bool CanProcessEvent(const x11::Event& xev);
bool ProcessEvent(x11::Event* xev);
void UpdateDisplayList();
void DispatchDelayedDisplayListUpdate();
diff --git a/chromium/ui/base/x/x11_display_util.cc b/chromium/ui/base/x/x11_display_util.cc
index 0376b5ef9b3..e8d433ad4b6 100644
--- a/chromium/ui/base/x/x11_display_util.cc
+++ b/chromium/ui/base/x/x11_display_util.cc
@@ -175,7 +175,7 @@ std::vector<display::Display> GetFallbackDisplayList(float scale) {
display::Display gfx_display(0, bounds_in_pixels);
if (!display::Display::HasForceDeviceScaleFactor() &&
- !display::IsDisplaySizeBlackListed(physical_size)) {
+ display::IsDisplaySizeValid(physical_size)) {
DCHECK_LE(1.0f, scale);
gfx_display.SetScaleAndBounds(scale, bounds_in_pixels);
gfx_display.set_work_area(
diff --git a/chromium/ui/base/x/x11_drag_context.cc b/chromium/ui/base/x/x11_drag_context.cc
index bdb6df1fd5c..d07e44dadb3 100644
--- a/chromium/ui/base/x/x11_drag_context.cc
+++ b/chromium/ui/base/x/x11_drag_context.cc
@@ -138,10 +138,10 @@ void XDragContext::OnSelectionNotify(const x11::SelectionNotifyEvent& event) {
if (event.property != x11::Atom::None) {
DCHECK_EQ(property, gfx::GetAtom(kChromiumDragReciever));
- std::vector<uint8_t> data;
+ scoped_refptr<base::RefCountedMemory> data;
x11::Atom type = x11::Atom::None;
if (GetRawBytesOfProperty(local_window_, property, &data, &type))
- fetched_targets_.Insert(target, base::RefCountedBytes::TakeVector(&data));
+ fetched_targets_.Insert(target, data);
} else {
// The source failed to convert the drop data to the format (target in X11
// parlance) that we asked for. This happens, even though we only ask for
diff --git a/chromium/ui/base/x/x11_drag_drop_client.h b/chromium/ui/base/x/x11_drag_drop_client.h
index 1d7e6a32caf..91916d3001d 100644
--- a/chromium/ui/base/x/x11_drag_drop_client.h
+++ b/chromium/ui/base/x/x11_drag_drop_client.h
@@ -25,10 +25,14 @@ class XOSExchangeDataProvider;
// Converts the current set of X masks into the set of ui::EventFlags.
COMPONENT_EXPORT(UI_BASE_X) int XGetMaskAsEventFlags();
-// Handles XDND (X11 drag and drop protocol) for the given window.
+// Works for both incoming and outgoing drags. For the incoming drags, receives
+// XDND events via HandleXdndEvent() and routes them to OnXdnd...() handlers;
+// outgoing drags are handled via Handle...() methods. Both ways end up in
+// SendXdnd...() calls that target the window that is currently at the drag
+// location. If the target is another Chrome window, the event is delivered
+// directly to its XdndHandler, bypassing the X server.
//
-// Doesn't fetch XDND events from the event source; those should be taken by
-// the client and fed to |OnXdnd...| and |OnSelectionNotify| methods.
+// The owner is notified about the ongoing drag through the Delegate interface.
class COMPONENT_EXPORT(UI_BASE_X) XDragDropClient {
public:
// Handlers and callbacks that should be implemented at the consumer side.
@@ -49,10 +53,11 @@ class COMPONENT_EXPORT(UI_BASE_X) XDragDropClient {
virtual void UpdateCursor(
DragDropTypes::DragOperation negotiated_operation) = 0;
- // Called when data from another application enters the window.
+ // Called when data from another application (not Chrome) enters the window.
virtual void OnBeginForeignDrag(x11::Window window) = 0;
- // Called when data from another application is about to leave the window.
+ // Called when data from another application (not Chrome) is about to leave
+ // the window.
virtual void OnEndForeignDrag() = 0;
// Called just before the drag leaves the window.
@@ -83,8 +88,8 @@ class COMPONENT_EXPORT(UI_BASE_X) XDragDropClient {
int current_modifier_state() const { return current_modifier_state_; }
// Handling XdndPosition can be paused while waiting for more data; this is
- // called either synchronously from OnXdndPosition, or asynchronously after
- // we've received data requested from the other window.
+ // called by XDragContext either synchronously or asynchronously, depending on
+ // whether the context has data requested from the other window.
void CompleteXdndPosition(x11::Window source_window,
const gfx::Point& screen_point);
diff --git a/chromium/ui/base/x/x11_error_handler.cc b/chromium/ui/base/x/x11_error_handler.cc
index d1babb0bcbd..e63bb8fb829 100644
--- a/chromium/ui/base/x/x11_error_handler.cc
+++ b/chromium/ui/base/x/x11_error_handler.cc
@@ -8,11 +8,10 @@
#include "base/compiler_specific.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/sequenced_task_runner.h"
+#include "base/task/current_thread.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_util_internal.h"
#include "ui/gfx/x/xproto_util.h"
namespace ui {
@@ -46,7 +45,7 @@ NOINLINE void WaitingForUIThreadToHandleIOError() {
}
int BrowserX11IOErrorHandler(Display* d) {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
// Wait for the UI thread (which has a different connection to the X server)
// to get the error. We can't call shutdown from this thread without
// tripping an error. Doing it through a function so that we'll be able
diff --git a/chromium/ui/base/x/x11_gl_egl_utility.cc b/chromium/ui/base/x/x11_gl_egl_utility.cc
index 197f8e4c553..46ad485c1b1 100644
--- a/chromium/ui/base/x/x11_gl_egl_utility.cc
+++ b/chromium/ui/base/x/x11_gl_egl_utility.cc
@@ -4,7 +4,7 @@
#include "ui/base/x/x11_gl_egl_utility.h"
-#include "ui/base/x/x11_util_internal.h"
+#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/x11.h"
#include "ui/gl/gl_surface_egl.h"
@@ -26,8 +26,8 @@ void GetPlatformExtraDisplayAttribs(EGLenum platform_type,
// get it anyway.
if (platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) {
x11::VisualId visual_id;
- ui::XVisualManager::GetInstance()->ChooseVisualForWindow(true, &visual_id,
- nullptr, nullptr);
+ ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
+ true, &visual_id, nullptr, nullptr, nullptr);
attributes->push_back(EGL_X11_VISUAL_ID_ANGLE);
attributes->push_back(static_cast<EGLAttrib>(visual_id));
}
@@ -39,11 +39,15 @@ void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
// can't use XVisualManager.
if (gl::GLSurfaceEGL::GetNativeDisplay() != EGL_DEFAULT_DISPLAY) {
uint8_t depth;
- ui::XVisualManager::GetInstance()->ChooseVisualForWindow(true, nullptr,
- &depth, nullptr);
+ ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
+ true, nullptr, &depth, nullptr, nullptr);
*buffer_size = depth;
*alpha_size = *buffer_size == 32 ? 8 : 0;
}
}
+bool IsTransparentBackgroundSupported() {
+ return ui::XVisualManager::GetInstance()->ArgbVisualAvailable();
+}
+
} // namespace ui
diff --git a/chromium/ui/base/x/x11_gl_egl_utility.h b/chromium/ui/base/x/x11_gl_egl_utility.h
index eb3ca99a784..8f559f28e28 100644
--- a/chromium/ui/base/x/x11_gl_egl_utility.h
+++ b/chromium/ui/base/x/x11_gl_egl_utility.h
@@ -19,6 +19,9 @@ void GetPlatformExtraDisplayAttribs(EGLenum platform_type,
void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size,
EGLint* buffer_size);
+// Returns whether transparent background is suppored.
+bool IsTransparentBackgroundSupported();
+
} // namespace ui
-#endif // UI_BASE_X_X11_GL_EGL_UTILITY_H_ \ No newline at end of file
+#endif // UI_BASE_X_X11_GL_EGL_UTILITY_H_
diff --git a/chromium/ui/base/x/x11_menu_registrar.cc b/chromium/ui/base/x/x11_menu_registrar.cc
index 2264b4b465b..924340bfd09 100644
--- a/chromium/ui/base/x/x11_menu_registrar.cc
+++ b/chromium/ui/base/x/x11_menu_registrar.cc
@@ -13,6 +13,7 @@
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_error_tracker.h"
+#include "ui/gfx/x/xproto.h"
namespace {
@@ -45,27 +46,15 @@ X11MenuRegistrar::~X11MenuRegistrar() {
ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
}
-bool X11MenuRegistrar::DispatchXEvent(x11::Event* x11_event) {
- XEvent* event = &x11_event->xlib_event();
- if (event->type != CreateNotify && event->type != DestroyNotify) {
- return false;
- }
- switch (event->type) {
- case CreateNotify:
- OnWindowCreatedOrDestroyed(
- event->type, x11_event->As<x11::CreateNotifyEvent>()->window);
- break;
- case DestroyNotify:
- OnWindowCreatedOrDestroyed(
- event->type, x11_event->As<x11::DestroyNotifyEvent>()->window);
- break;
- default:
- NOTREACHED();
- }
+bool X11MenuRegistrar::DispatchXEvent(x11::Event* xev) {
+ if (auto* create = xev->As<x11::CreateNotifyEvent>())
+ OnWindowCreatedOrDestroyed(true, create->window);
+ else if (auto* destroy = xev->As<x11::DestroyNotifyEvent>())
+ OnWindowCreatedOrDestroyed(false, destroy->window);
return false;
}
-void X11MenuRegistrar::OnWindowCreatedOrDestroyed(int event_type,
+void X11MenuRegistrar::OnWindowCreatedOrDestroyed(bool created,
x11::Window window) {
// Menus created by Chrome can be drag and drop targets. Since they are
// direct children of the screen root window and have override_redirect
@@ -74,7 +63,7 @@ void X11MenuRegistrar::OnWindowCreatedOrDestroyed(int event_type,
// TODO(varkha): Implement caching of all top level X windows and their
// coordinates and stacking order to eliminate repeated calls to the X server
// during mouse movement, drag and shaping events.
- if (event_type == CreateNotify) {
+ if (created) {
// The window might be destroyed if the message pump did not get a chance to
// run but we can safely ignore the X error.
gfx::X11ErrorTracker error_tracker;
diff --git a/chromium/ui/base/x/x11_menu_registrar.h b/chromium/ui/base/x/x11_menu_registrar.h
index 0fe0a83e727..901429612e2 100644
--- a/chromium/ui/base/x/x11_menu_registrar.h
+++ b/chromium/ui/base/x/x11_menu_registrar.h
@@ -38,7 +38,7 @@ class X11MenuRegistrar : public ui::XEventDispatcher {
// Called when |window| has been created or destroyed. |window| may not be
// managed by Chrome.
- void OnWindowCreatedOrDestroyed(int event_type, x11::Window window);
+ void OnWindowCreatedOrDestroyed(bool created, x11::Window window);
// The display and the native X window hosting the root window.
XDisplay* xdisplay_;
diff --git a/chromium/ui/base/x/x11_move_loop.h b/chromium/ui/base/x/x11_move_loop.h
index 24000a9a30b..9a00455409c 100644
--- a/chromium/ui/base/x/x11_move_loop.h
+++ b/chromium/ui/base/x/x11_move_loop.h
@@ -11,22 +11,24 @@
namespace ui {
+class X11Cursor;
+
// Runs a nested run loop and grabs the mouse. This is used to implement
// dragging.
class X11MoveLoop {
public:
- virtual ~X11MoveLoop() {}
+ virtual ~X11MoveLoop() = default;
// Runs the nested run loop. While the mouse is grabbed, use |cursor| as
// the mouse cursor. Returns true if the move-loop is completed successfully.
// If the pointer-grab fails, or the move-loop is canceled by the user (e.g.
// by pressing escape), then returns false.
virtual bool RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) = 0;
+ scoped_refptr<ui::X11Cursor> old_cursor,
+ scoped_refptr<ui::X11Cursor> new_cursor) = 0;
// Updates the cursor while the move loop is running.
- virtual void UpdateCursor(::Cursor cursor) = 0;
+ virtual void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) = 0;
// Ends the move loop that's currently in progress.
virtual void EndMoveLoop() = 0;
diff --git a/chromium/ui/base/x/x11_os_exchange_data_provider.cc b/chromium/ui/base/x/x11_os_exchange_data_provider.cc
index 5a38c3f841b..ef6463f78c6 100644
--- a/chromium/ui/base/x/x11_os_exchange_data_provider.cc
+++ b/chromium/ui/base/x/x11_os_exchange_data_provider.cc
@@ -71,13 +71,11 @@ SelectionFormatMap XOSExchangeDataProvider::GetFormatMap() const {
return selection_owner_.selection_format_map();
}
-#if defined(USE_OZONE)
std::unique_ptr<OSExchangeDataProvider> XOSExchangeDataProvider::Clone() const {
std::unique_ptr<XOSExchangeDataProvider> ret(new XOSExchangeDataProvider());
ret->set_format_map(format_map());
return std::move(ret);
}
-#endif
void XOSExchangeDataProvider::MarkOriginatedFromRenderer() {
std::string empty;
@@ -99,9 +97,9 @@ void XOSExchangeDataProvider::SetString(const base::string16& text_data) {
base::RefCountedString::TakeString(&utf8));
format_map_.Insert(gfx::GetAtom(kMimeTypeText), mem);
- format_map_.Insert(gfx::GetAtom(kText), mem);
- format_map_.Insert(gfx::GetAtom(kString), mem);
- format_map_.Insert(gfx::GetAtom(kUtf8String), mem);
+ format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxText), mem);
+ format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxString), mem);
+ format_map_.Insert(gfx::GetAtom(kMimeTypeLinuxUtf8String), mem);
}
void XOSExchangeDataProvider::SetURL(const GURL& url,
@@ -369,6 +367,39 @@ bool XOSExchangeDataProvider::HasCustomFormat(
return !requested_types.empty();
}
+#if defined(USE_X11)
+void XOSExchangeDataProvider::SetFileContents(
+ const base::FilePath& filename,
+ const std::string& file_contents) {
+ DCHECK(!filename.empty());
+ DCHECK(!base::Contains(format_map(), gfx::GetAtom(kMimeTypeMozillaURL)));
+ set_file_contents_name(filename);
+ // Direct save handling is a complicated juggling affair between this class,
+ // SelectionFormat, and XDragDropClient. The general idea behind
+ // the protocol is this:
+ // - The source window sets its XdndDirectSave0 window property to the
+ // proposed filename.
+ // - When a target window receives the drop, it updates the XdndDirectSave0
+ // property on the source window to the filename it would like the contents
+ // to be saved to and then requests the XdndDirectSave0 type from the
+ // source.
+ // - The source is supposed to copy the file here and return success (S),
+ // failure (F), or error (E).
+ // - In this case, failure means the destination should try to populate the
+ // file itself by copying the data from application/octet-stream. To make
+ // things simpler for Chrome, we always 'fail' and let the destination do
+ // the work.
+ std::string failure("F");
+ InsertData(gfx::GetAtom("XdndDirectSave0"),
+ scoped_refptr<base::RefCountedMemory>(
+ base::RefCountedString::TakeString(&failure)));
+ std::string file_contents_copy = file_contents;
+ InsertData(gfx::GetAtom("application/octet-stream"),
+ scoped_refptr<base::RefCountedMemory>(
+ base::RefCountedString::TakeString(&file_contents_copy)));
+}
+#endif
+
void XOSExchangeDataProvider::SetHtml(const base::string16& html,
const GURL& base_url) {
std::vector<unsigned char> bytes;
diff --git a/chromium/ui/base/x/x11_os_exchange_data_provider.h b/chromium/ui/base/x/x11_os_exchange_data_provider.h
index 214bf950ba1..24c0f42dbac 100644
--- a/chromium/ui/base/x/x11_os_exchange_data_provider.h
+++ b/chromium/ui/base/x/x11_os_exchange_data_provider.h
@@ -61,9 +61,7 @@ class COMPONENT_EXPORT(UI_BASE_X) XOSExchangeDataProvider
}
// Overridden from OSExchangeDataProvider:
-#if defined(USE_OZONE)
std::unique_ptr<OSExchangeDataProvider> Clone() const override;
-#endif
void MarkOriginatedFromRenderer() override;
bool DidOriginateFromRenderer() const override;
void SetString(const base::string16& data) override;
@@ -84,6 +82,10 @@ class COMPONENT_EXPORT(UI_BASE_X) XOSExchangeDataProvider
bool HasURL(FilenameToURLPolicy policy) const override;
bool HasFile() const override;
bool HasCustomFormat(const ClipboardFormatType& format) const override;
+#if defined(USE_X11)
+ void SetFileContents(const base::FilePath& filename,
+ const std::string& file_contents) override;
+#endif
void SetHtml(const base::string16& html, const GURL& base_url) override;
bool GetHtml(base::string16* html, GURL* base_url) const override;
diff --git a/chromium/ui/base/x/x11_pointer_grab.cc b/chromium/ui/base/x/x11_pointer_grab.cc
index b7a65240c02..e422d880c41 100644
--- a/chromium/ui/base/x/x11_pointer_grab.cc
+++ b/chromium/ui/base/x/x11_pointer_grab.cc
@@ -4,10 +4,17 @@
#include "ui/base/x/x11_pointer_grab.h"
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
#include "base/check.h"
+#include "base/no_destructor.h"
+#include "base/sys_byteorder.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xinput.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
@@ -19,71 +26,114 @@ x11::Window g_grab_window = x11::Window::None;
// The "owner events" parameter used to grab the pointer.
bool g_owner_events = false;
-} // namespace
+base::CancelableOnceCallback<void(x11::Cursor)>& GetGrabCallback() {
+ static base::NoDestructor<base::CancelableOnceCallback<void(x11::Cursor)>>
+ callback;
+ return *callback;
+}
-int GrabPointer(x11::Window window, bool owner_events, ::Cursor cursor) {
- int result = GrabInvalidTime;
+x11::GrabStatus GrabPointerImpl(x11::Window window,
+ bool owner_events,
+ x11::Cursor cursor) {
+ GetGrabCallback().Cancel();
+ auto result = x11::GrabStatus::InvalidTime;
+ auto* connection = x11::Connection::Get();
if (ui::IsXInput2Available()) {
- // Do an XInput2 pointer grab. If there is an active XInput2 pointer grab
- // as a result of normal button press, XGrabPointer() will fail.
- unsigned char mask[XIMaskLen(XI_LASTEVENT)];
- memset(mask, 0, sizeof(mask));
- XISetMask(mask, XI_ButtonPress);
- XISetMask(mask, XI_ButtonRelease);
- XISetMask(mask, XI_Motion);
- XISetMask(mask, XI_TouchBegin);
- XISetMask(mask, XI_TouchUpdate);
- XISetMask(mask, XI_TouchEnd);
- XIEventMask evmask;
- evmask.mask_len = sizeof(mask);
- evmask.mask = mask;
-
- const std::vector<int>& master_pointers =
- ui::DeviceDataManagerX11::GetInstance()->master_pointers();
- for (int master_pointer : master_pointers) {
- evmask.deviceid = master_pointer;
- result =
- XIGrabDevice(gfx::GetXDisplay(), master_pointer,
- static_cast<uint32_t>(window), x11::CurrentTime, cursor,
- GrabModeAsync, GrabModeAsync, owner_events, &evmask);
+ // Do an xinput pointer grab. If there is an active xinput pointer grab
+ // as a result of normal button press, GrabPointer() will fail.
+ auto mask = x11::Input::XIEventMask::ButtonPress |
+ x11::Input::XIEventMask::ButtonRelease |
+ x11::Input::XIEventMask::Motion |
+ x11::Input::XIEventMask::TouchBegin |
+ x11::Input::XIEventMask::TouchUpdate |
+ x11::Input::XIEventMask::TouchEnd;
+ static_assert(sizeof(mask) == 4, "");
+
+ for (auto master_pointer :
+ ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
+ x11::Input::XIGrabDeviceRequest req{
+ .window = window,
+ .time = x11::Time::CurrentTime,
+ .cursor = cursor,
+ .deviceid = master_pointer,
+ .mode = x11::GrabMode::Async,
+ .paired_device_mode = x11::GrabMode::Async,
+ .owner_events = owner_events ? x11::Input::GrabOwner::Owner
+ : x11::Input::GrabOwner::NoOwner,
+ .mask = {base::ByteSwapToLE32(static_cast<uint32_t>(mask))},
+ };
+ if (auto reply = connection->xinput().XIGrabDevice(req).Sync())
+ result = reply->status;
+
// Assume that the grab will succeed on either all or none of the master
// pointers.
- if (result != GrabSuccess) {
+ if (result != x11::GrabStatus::Success) {
// Try core pointer grab.
break;
}
}
}
- if (result != GrabSuccess) {
- int event_mask = PointerMotionMask | ButtonReleaseMask | ButtonPressMask;
- result = XGrabPointer(gfx::GetXDisplay(), static_cast<uint32_t>(window),
- owner_events, event_mask, GrabModeAsync,
- GrabModeAsync, x11::None, cursor, x11::CurrentTime);
+ if (result != x11::GrabStatus::Success) {
+ auto mask = x11::EventMask::PointerMotion | x11::EventMask::ButtonRelease |
+ x11::EventMask::ButtonPress;
+ x11::GrabPointerRequest req{
+ .owner_events = owner_events,
+ .grab_window = window,
+ .event_mask = mask,
+ .pointer_mode = x11::GrabMode::Async,
+ .keyboard_mode = x11::GrabMode::Async,
+ .confine_to = x11::Window::None,
+ .cursor = cursor,
+ .time = x11::Time::CurrentTime,
+ };
+ if (auto reply = connection->GrabPointer(req).Sync())
+ result = reply->status;
}
- if (result == GrabSuccess) {
+ if (result == x11::GrabStatus::Success) {
g_grab_window = window;
g_owner_events = owner_events;
}
return result;
}
-void ChangeActivePointerGrabCursor(::Cursor cursor) {
+} // namespace
+
+x11::GrabStatus GrabPointer(x11::Window window,
+ bool owner_events,
+ scoped_refptr<ui::X11Cursor> cursor) {
+ if (!cursor)
+ return GrabPointerImpl(window, owner_events, x11::Cursor::None);
+ if (cursor->loaded())
+ return GrabPointerImpl(window, owner_events, cursor->xcursor());
+
+ auto result = GrabPointerImpl(window, owner_events, x11::Cursor::None);
+ GetGrabCallback().Reset(base::BindOnce(base::IgnoreResult(GrabPointerImpl),
+ window, owner_events));
+ cursor->OnCursorLoaded(GetGrabCallback().callback());
+ return result;
+}
+
+void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor) {
DCHECK(g_grab_window != x11::Window::None);
GrabPointer(g_grab_window, g_owner_events, cursor);
}
void UngrabPointer() {
+ GetGrabCallback().Cancel();
g_grab_window = x11::Window::None;
+ auto* connection = x11::Connection::Get();
if (ui::IsXInput2Available()) {
- const std::vector<int>& master_pointers =
- ui::DeviceDataManagerX11::GetInstance()->master_pointers();
- for (int master_pointer : master_pointers)
- XIUngrabDevice(gfx::GetXDisplay(), master_pointer, x11::CurrentTime);
+ for (auto master_pointer :
+ ui::DeviceDataManagerX11::GetInstance()->master_pointers()) {
+ connection->xinput()
+ .XIUngrabDevice({x11::Time::CurrentTime, master_pointer})
+ .IgnoreError();
+ }
}
// Try core pointer ungrab in case the XInput2 pointer ungrab failed.
- XUngrabPointer(gfx::GetXDisplay(), x11::CurrentTime);
+ connection->UngrabPointer({}).IgnoreError();
}
} // namespace ui
diff --git a/chromium/ui/base/x/x11_pointer_grab.h b/chromium/ui/base/x/x11_pointer_grab.h
index 8df814098e3..534a9402247 100644
--- a/chromium/ui/base/x/x11_pointer_grab.h
+++ b/chromium/ui/base/x/x11_pointer_grab.h
@@ -7,18 +7,22 @@
#include "base/component_export.h"
#include "ui/gfx/x/x11_types.h"
-
-typedef unsigned long Cursor;
+#include "ui/gfx/x/xproto.h"
namespace ui {
+class X11Cursor;
+
// Grabs the pointer. It is unnecessary to ungrab the pointer prior to grabbing
// it.
COMPONENT_EXPORT(UI_BASE_X)
-int GrabPointer(x11::Window window, bool owner_events, ::Cursor cursor);
+x11::GrabStatus GrabPointer(x11::Window window,
+ bool owner_events,
+ scoped_refptr<ui::X11Cursor> cursor);
// Sets the cursor to use for the duration of the active pointer grab.
-COMPONENT_EXPORT(UI_BASE_X) void ChangeActivePointerGrabCursor(::Cursor cursor);
+COMPONENT_EXPORT(UI_BASE_X)
+void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor);
// Ungrabs the pointer.
COMPONENT_EXPORT(UI_BASE_X) void UngrabPointer();
diff --git a/chromium/ui/base/x/x11_shm_image_pool.cc b/chromium/ui/base/x/x11_shm_image_pool.cc
index 1824426e5bb..4b5ae612e13 100644
--- a/chromium/ui/base/x/x11_shm_image_pool.cc
+++ b/chromium/ui/base/x/x11_shm_image_pool.cc
@@ -22,6 +22,7 @@
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/x11_switches.h"
namespace ui {
@@ -112,26 +113,27 @@ XShmImagePool::SwapClosure::SwapClosure() = default;
XShmImagePool::SwapClosure::~SwapClosure() = default;
-XShmImagePool::XShmImagePool(
- scoped_refptr<base::SequencedTaskRunner> host_task_runner,
- scoped_refptr<base::SequencedTaskRunner> event_task_runner,
- XDisplay* display,
- x11::Drawable drawable,
- Visual* visual,
- int depth,
- std::size_t frames_pending)
- : host_task_runner_(std::move(host_task_runner)),
- event_task_runner_(std::move(event_task_runner)),
- display_(display),
+XShmImagePool::XShmImagePool(x11::Connection* connection,
+ x11::Drawable drawable,
+ Visual* visual,
+ int depth,
+ std::size_t frames_pending)
+ : connection_(connection),
+ display_(connection_->display()),
drawable_(drawable),
visual_(visual),
depth_(depth),
frame_states_(frames_pending) {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ X11EventSource::GetInstance()->AddXEventDispatcher(this);
+}
+
+XShmImagePool::~XShmImagePool() {
+ Cleanup();
+ X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
}
bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (pixel_size == pixel_size_)
return true;
@@ -140,9 +142,6 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
std::unique_ptr<XShmImagePool, decltype(cleanup_fn)> cleanup{this,
cleanup_fn};
- if (!event_task_runner_)
- return false;
-
#if !defined(OS_CHROMEOS)
if (!ShouldUseMitShm(display_))
return false;
@@ -156,7 +155,8 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
return false;
}
- SkColorType color_type = ColorTypeForVisual(visual_);
+ SkColorType color_type =
+ ColorTypeForVisual(static_cast<x11::VisualId>(visual_->visualid));
if (color_type == kUnknown_SkColorType)
return false;
@@ -201,7 +201,7 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
shmctl(state.shminfo_.shmid, IPC_RMID, nullptr);
return false;
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// On Linux, a shmid can still be attached after IPC_RMID if otherwise
// kept alive. Detach before XShmAttach to prevent a memory leak in case
// the process dies.
@@ -211,7 +211,7 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
if (!XShmAttach(display_, &state.shminfo_))
return false;
state.shmem_attached_to_server_ = true;
-#if !defined(OS_LINUX)
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// The Linux-specific shmctl behavior above may not be portable, so we're
// forced to do IPC_RMID after the server has attached to the segment.
// XShmAttach is asynchronous, so we must also sync.
@@ -244,31 +244,31 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) {
}
bool XShmImagePool::Ready() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ready_;
}
SkBitmap& XShmImagePool::CurrentBitmap() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return frame_states_[current_frame_index_].bitmap;
}
SkCanvas* XShmImagePool::CurrentCanvas() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return frame_states_[current_frame_index_].canvas.get();
}
XImage* XShmImagePool::CurrentImage() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return frame_states_[current_frame_index_].image.get();
}
void XShmImagePool::SwapBuffers(
base::OnceCallback<void(const gfx::Size&)> callback) {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
swap_closures_.emplace_back();
SwapClosure& swap_closure = swap_closures_.back();
@@ -278,33 +278,13 @@ void XShmImagePool::SwapBuffers(
current_frame_index_ = (current_frame_index_ + 1) % frame_states_.size();
}
-void XShmImagePool::Initialize() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
- if (event_task_runner_)
- event_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&XShmImagePool::InitializeOnGpu, this));
-}
-
-void XShmImagePool::Teardown() {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
- if (event_task_runner_)
- event_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&XShmImagePool::TeardownOnGpu, this));
-}
-
-XShmImagePool::~XShmImagePool() {
- Cleanup();
-#ifndef NDEBUG
- DCHECK(!dispatcher_registered_);
-#endif
-}
-
-void XShmImagePool::DispatchShmCompletionEvent(XShmCompletionEvent event) {
- DCHECK(host_task_runner_->RunsTasksInCurrentSequence());
+void XShmImagePool::DispatchShmCompletionEvent(
+ x11::Shm::CompletionEvent event) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(event.offset, 0UL);
for (auto it = swap_closures_.begin(); it != swap_closures_.end(); ++it) {
- if (event.shmseg == it->shmseg) {
+ if (static_cast<uint32_t>(event.shmseg) == it->shmseg) {
std::move(it->closure).Run();
swap_closures_.erase(it);
return;
@@ -312,45 +292,17 @@ void XShmImagePool::DispatchShmCompletionEvent(XShmCompletionEvent event) {
}
}
-bool XShmImagePool::CanDispatchXEvent(x11::Event* x11_event) {
- const XEvent* xev = &x11_event->xlib_event();
- DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
+bool XShmImagePool::DispatchXEvent(x11::Event* xev) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (xev->type != ui::ShmEventBase() + ShmCompletion)
+ auto* completion = xev->As<x11::Shm::CompletionEvent>();
+ if (!completion || completion->drawable.value != drawable_.value)
return false;
- const auto* shm_event = reinterpret_cast<const XShmCompletionEvent*>(xev);
- return shm_event->drawable == drawable_.value;
-}
-
-bool XShmImagePool::DispatchXEvent(x11::Event* x11_event) {
- XEvent* xev = &x11_event->xlib_event();
- if (!CanDispatchXEvent(x11_event))
- return false;
-
- XShmCompletionEvent* shm_event = reinterpret_cast<XShmCompletionEvent*>(xev);
- host_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&XShmImagePool::DispatchShmCompletionEvent,
- this, *shm_event));
+ DispatchShmCompletionEvent(*completion);
return true;
}
-void XShmImagePool::InitializeOnGpu() {
- DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
- X11EventSource::GetInstance()->AddXEventDispatcher(this);
-#ifndef NDEBUG
- dispatcher_registered_ = true;
-#endif
-}
-
-void XShmImagePool::TeardownOnGpu() {
- DCHECK(event_task_runner_->RunsTasksInCurrentSequence());
- X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
-#ifndef NDEBUG
- dispatcher_registered_ = false;
-#endif
-}
-
void XShmImagePool::Cleanup() {
for (FrameState& state : frame_states_) {
if (state.shminfo_.shmaddr)
diff --git a/chromium/ui/base/x/x11_shm_image_pool.h b/chromium/ui/base/x/x11_shm_image_pool.h
index b50331d5c9b..b898706e99e 100644
--- a/chromium/ui/base/x/x11_shm_image_pool.h
+++ b/chromium/ui/base/x/x11_shm_image_pool.h
@@ -13,33 +13,30 @@
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
+#include "base/sequence_checker.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/shm.h"
#include "ui/gfx/x/x11.h"
namespace ui {
-// Base class that creates XImages using shared memory that will be sent to
-// XServer for processing. As Ozone and non-Ozone X11 have different
-// PlatformEvent types, Ozone and non-Ozone provide own implementations that
-// handles events. See AddEventDispatcher and RemoveEventDispatcher below.
-class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool
- : public base::RefCountedThreadSafe<XShmImagePool>,
- public XEventDispatcher {
+// Creates XImages backed by shared memory that will be shared with the X11
+// server for processing.
+class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool : public XEventDispatcher {
public:
- XShmImagePool(scoped_refptr<base::SequencedTaskRunner> host_task_runner,
- scoped_refptr<base::SequencedTaskRunner> event_task_runner,
- XDisplay* display,
+ XShmImagePool(x11::Connection* connection,
x11::Drawable drawable,
Visual* visual,
int depth,
std::size_t max_frames_pending);
+ ~XShmImagePool() override;
+
bool Resize(const gfx::Size& pixel_size);
// Is XSHM supported by the server and are the shared buffers ready for use?
@@ -54,27 +51,8 @@ class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool
// change to reflect the new frame.
void SwapBuffers(base::OnceCallback<void(const gfx::Size&)> callback);
- // Part of setup and teardown must be done on the event task runner. Posting
- // the tasks cannot be done in the constructor/destructor because because this
- // would cause subtle problems with the reference count for this object. So
- // Initialize() must be called after constructing and Teardown() must be
- // called before destructing.
- void Initialize();
- void Teardown();
-
protected:
- ~XShmImagePool() override;
-
- void DispatchShmCompletionEvent(XShmCompletionEvent event);
-
- bool CanDispatchXEvent(x11::Event* xev);
-
- const scoped_refptr<base::SequencedTaskRunner> host_task_runner_;
- const scoped_refptr<base::SequencedTaskRunner> event_task_runner_;
-
-#ifndef NDEBUG
- bool dispatcher_registered_ = false;
-#endif
+ void DispatchShmCompletionEvent(x11::Shm::CompletionEvent event);
private:
friend class base::RefCountedThreadSafe<XShmImagePool>;
@@ -101,11 +79,9 @@ class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool
// XEventDispatcher:
bool DispatchXEvent(x11::Event* xev) override;
- void InitializeOnGpu();
- void TeardownOnGpu();
-
void Cleanup();
+ x11::Connection* const connection_;
XDisplay* const display_;
const x11::Drawable drawable_;
Visual* const visual_;
@@ -118,6 +94,8 @@ class COMPONENT_EXPORT(UI_BASE_X) XShmImagePool
std::size_t current_frame_index_ = 0;
std::list<SwapClosure> swap_closures_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(XShmImagePool);
};
diff --git a/chromium/ui/base/x/x11_software_bitmap_presenter.cc b/chromium/ui/base/x/x11_software_bitmap_presenter.cc
index 1974c5bfac8..03f35a34435 100644
--- a/chromium/ui/base/x/x11_software_bitmap_presenter.cc
+++ b/chromium/ui/base/x/x11_software_bitmap_presenter.cc
@@ -15,16 +15,19 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/task/current_thread.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/base/x/x11_shm_image_pool.h"
#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_util_internal.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_error_tracker.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xproto_types.h"
namespace ui {
@@ -120,15 +123,12 @@ bool X11SoftwareBitmapPresenter::CompositeBitmap(XDisplay* display,
}
X11SoftwareBitmapPresenter::X11SoftwareBitmapPresenter(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> host_task_runner,
- scoped_refptr<base::SequencedTaskRunner> event_task_runner)
+ gfx::AcceleratedWidget widget)
: widget_(static_cast<x11::Window>(widget)),
- display_(gfx::GetXDisplay()),
- gc_(nullptr),
- host_task_runner_(host_task_runner),
- event_task_runner_(event_task_runner) {
- DCHECK(widget_ != x11::Window::None);
+ connection_(x11::Connection::Get()),
+ display_(connection_->display()),
+ gc_(nullptr) {
+ DCHECK_NE(widget_, x11::Window::None);
gc_ = XCreateGC(display_, static_cast<uint32_t>(widget_), 0, nullptr);
memset(&attributes_, 0, sizeof(attributes_));
if (!XGetWindowAttributes(display_, static_cast<uint32_t>(widget_),
@@ -138,10 +138,9 @@ X11SoftwareBitmapPresenter::X11SoftwareBitmapPresenter(
return;
}
- shm_pool_ = base::MakeRefCounted<ui::XShmImagePool>(
- host_task_runner, event_task_runner, display_, widget_,
- attributes_.visual, attributes_.depth, kMaxFramesPending);
- shm_pool_->Initialize();
+ shm_pool_ = std::make_unique<ui::XShmImagePool>(
+ connection_, widget_, attributes_.visual, attributes_.depth,
+ kMaxFramesPending);
// TODO(thomasanderson): Avoid going through the X11 server to plumb this
// property in.
@@ -149,41 +148,17 @@ X11SoftwareBitmapPresenter::X11SoftwareBitmapPresenter(
}
X11SoftwareBitmapPresenter::~X11SoftwareBitmapPresenter() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (gc_)
XFreeGC(display_, gc_);
-
- if (shm_pool_)
- shm_pool_->Teardown();
}
bool X11SoftwareBitmapPresenter::ShmPoolReady() const {
return shm_pool_ && shm_pool_->Ready();
}
-void X11SoftwareBitmapPresenter::FlushAfterPutImage() {
- // Ensure the new window content appears immediately. On a TYPE_UI thread we
- // can rely on the message loop to flush for us so XFlush() isn't necessary.
- // However, this code can run on a different thread and would have to wait for
- // the TYPE_UI thread to no longer be idle before a flush happens.
- XFlush(display_);
-
- // Work around a race condition caused by XFlush above. Explanation: XFlush()
- // flushes all requests and *also* reads events. It's unclear why it does
- // this, but there's no alternative Xlib function that flushes the requests
- // and *doesn't* read any events, so this workaround is necessary. In
- // |event_task_runner_|'s message loop, poll() is called on the underlying
- // XDisplay's fd to dispatch toplevel events. When the fd is readable, poll()
- // exits and we (via Xlib) check for new events by read()ing from the fd. But
- // if the event dispatcher is currently dispatching an event, then our call to
- // XFlush() may read events into the event queue which will make the fd
- // blocking since there's no more data to read, so poll() won't wake up until
- // a new event comes, which may take a long time. Forcing the event loop to
- // wake up with a dummy event fixes the race condition.
- if (event_task_runner_)
- event_task_runner_->PostTask(FROM_HERE, base::BindOnce([] {}));
-}
-
void X11SoftwareBitmapPresenter::Resize(const gfx::Size& pixel_size) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (pixel_size == viewport_pixel_size_)
return;
viewport_pixel_size_ = pixel_size;
@@ -195,7 +170,8 @@ void X11SoftwareBitmapPresenter::Resize(const gfx::Size& pixel_size) {
needs_swap_ = false;
surface_ = nullptr;
} else {
- SkColorType color_type = ColorTypeForVisual(attributes_.visual);
+ SkColorType color_type = ColorTypeForVisual(
+ static_cast<x11::VisualId>(attributes_.visual->visualid));
if (color_type == kUnknown_SkColorType)
return;
SkImageInfo info = SkImageInfo::Make(viewport_pixel_size_.width(),
@@ -206,6 +182,7 @@ void X11SoftwareBitmapPresenter::Resize(const gfx::Size& pixel_size) {
}
SkCanvas* X11SoftwareBitmapPresenter::GetSkCanvas() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ShmPoolReady())
return shm_pool_->CurrentCanvas();
else if (surface_)
@@ -214,6 +191,7 @@ SkCanvas* X11SoftwareBitmapPresenter::GetSkCanvas() {
}
void X11SoftwareBitmapPresenter::EndPaint(const gfx::Rect& damage_rect) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gfx::Rect rect = damage_rect;
rect.Intersect(gfx::Rect(viewport_pixel_size_));
if (rect.IsEmpty())
@@ -228,7 +206,6 @@ void X11SoftwareBitmapPresenter::EndPaint(const gfx::Rect& damage_rect) {
shm_pool_->CurrentImage(), rect.x(), rect.y(), rect.x(),
rect.y(), rect.width(), rect.height(), x11::True)) {
needs_swap_ = true;
- FlushAfterPutImage();
return;
}
skia_pixmap = shm_pool_->CurrentBitmap().pixmap();
@@ -243,47 +220,28 @@ void X11SoftwareBitmapPresenter::EndPaint(const gfx::Rect& damage_rect) {
CompositeBitmap(display_, static_cast<uint32_t>(widget_), rect.x(),
rect.y(), rect.width(), rect.height(), attributes_.depth,
gc_, skia_pixmap.addr())) {
- FlushAfterPutImage();
return;
}
- XImage image = {};
- image.width = viewport_pixel_size_.width();
- image.height = viewport_pixel_size_.height();
- image.format = static_cast<int>(x11::ImageFormat::ZPixmap);
- image.byte_order = static_cast<int>(x11::ImageOrder::LSBFirst);
- image.bitmap_unit = 8;
- image.bitmap_bit_order = static_cast<int>(x11::ImageOrder::LSBFirst);
- image.depth = attributes_.depth;
-
- image.bits_per_pixel = attributes_.visual->bits_per_rgb;
- image.bits_per_pixel = skia_pixmap.info().bytesPerPixel() * 8;
-
- image.bytes_per_line = skia_pixmap.rowBytes();
- image.red_mask = attributes_.visual->red_mask;
- image.green_mask = attributes_.visual->green_mask;
- image.blue_mask = attributes_.visual->blue_mask;
+ auto* connection = x11::Connection::Get();
+ auto gc = static_cast<x11::GraphicsContext>(XGContextFromGC(gc_));
+ DrawPixmap(connection,
+ static_cast<x11::VisualId>(attributes_.visual->visualid), widget_,
+ gc, skia_pixmap, rect.x(), rect.y(), rect.x(), rect.y(),
+ rect.width(), rect.height());
- image.data = reinterpret_cast<char*>(const_cast<void*>(skia_pixmap.addr()));
- XPutImage(display_, static_cast<uint32_t>(widget_), gc_, &image, rect.x(),
- rect.y(), rect.x(), rect.y(), rect.width(), rect.height());
-
- FlushAfterPutImage();
+ // We must be running on a UI thread so that the connection will be flushed.
+ DCHECK(base::CurrentUIThread::IsSet());
}
void X11SoftwareBitmapPresenter::OnSwapBuffers(
SwapBuffersCallback swap_ack_callback) {
- if (ShmPoolReady()) {
- if (needs_swap_)
- shm_pool_->SwapBuffers(std::move(swap_ack_callback));
- else
- std::move(swap_ack_callback).Run(viewport_pixel_size_);
- needs_swap_ = false;
- } else {
- host_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_));
- }
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (ShmPoolReady() && needs_swap_)
+ shm_pool_->SwapBuffers(std::move(swap_ack_callback));
+ else
+ std::move(swap_ack_callback).Run(viewport_pixel_size_);
+ needs_swap_ = false;
}
int X11SoftwareBitmapPresenter::MaxFramesPending() const {
diff --git a/chromium/ui/base/x/x11_software_bitmap_presenter.h b/chromium/ui/base/x/x11_software_bitmap_presenter.h
index 4f405653b19..e04b7feb53f 100644
--- a/chromium/ui/base/x/x11_software_bitmap_presenter.h
+++ b/chromium/ui/base/x/x11_software_bitmap_presenter.h
@@ -8,6 +8,7 @@
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -28,10 +29,7 @@ class COMPONENT_EXPORT(UI_BASE_X) X11SoftwareBitmapPresenter {
// Corresponds to SwapBuffersCallback alias in SoftwareOutputDevice.
using SwapBuffersCallback = base::OnceCallback<void(const gfx::Size&)>;
- X11SoftwareBitmapPresenter(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> host_task_runner,
- scoped_refptr<base::SequencedTaskRunner> event_task_runner);
+ explicit X11SoftwareBitmapPresenter(gfx::AcceleratedWidget widget);
~X11SoftwareBitmapPresenter();
@@ -56,9 +54,8 @@ class COMPONENT_EXPORT(UI_BASE_X) X11SoftwareBitmapPresenter {
bool ShmPoolReady() const;
- void FlushAfterPutImage();
-
x11::Window widget_;
+ x11::Connection* connection_;
XDisplay* display_;
GC gc_;
XWindowAttributes attributes_;
@@ -67,15 +64,15 @@ class COMPONENT_EXPORT(UI_BASE_X) X11SoftwareBitmapPresenter {
// parent-relative background.
int composite_ = 0;
- scoped_refptr<ui::XShmImagePool> shm_pool_;
+ std::unique_ptr<ui::XShmImagePool> shm_pool_;
bool needs_swap_ = false;
- scoped_refptr<base::SequencedTaskRunner> host_task_runner_;
- scoped_refptr<base::SequencedTaskRunner> event_task_runner_;
sk_sp<SkSurface> surface_;
gfx::Size viewport_pixel_size_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(X11SoftwareBitmapPresenter);
};
diff --git a/chromium/ui/base/x/x11_ui_thread.cc b/chromium/ui/base/x/x11_ui_thread.cc
new file mode 100644
index 00000000000..86111b3fce3
--- /dev/null
+++ b/chromium/ui/base/x/x11_ui_thread.cc
@@ -0,0 +1,49 @@
+// 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 "ui/base/x/x11_ui_thread.h"
+
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/x/connection.h"
+
+namespace ui {
+
+namespace {
+
+x11::Connection* g_connection = nullptr;
+
+}
+
+X11UiThread::X11UiThread(const std::string& thread_name)
+ : base::Thread(thread_name) {
+ connection_.reset(g_connection);
+ g_connection = nullptr;
+ // When using in-process GPU, g_connection doesn't get set. But we can just
+ // open a new connection now since there's no GPU sandbox in place.
+ if (!connection_)
+ connection_ = x11::Connection::Get()->Clone();
+ connection_->DetachFromSequence();
+}
+
+X11UiThread::~X11UiThread() = default;
+
+void X11UiThread::SetConnection(x11::Connection* connection) {
+ DCHECK(!g_connection);
+ g_connection = connection;
+}
+
+void X11UiThread::Init() {
+ // Connection and X11EventSource make use of TLS, so these calls must be made
+ // on the thread, not in the constructor/destructor.
+ auto* connection = connection_.get();
+ x11::Connection::Set(std::move(connection_));
+ event_source_ = std::make_unique<X11EventSource>(connection);
+}
+
+void X11UiThread::CleanUp() {
+ event_source_.reset();
+ connection_.reset();
+}
+
+} // namespace ui \ No newline at end of file
diff --git a/chromium/ui/base/x/x11_ui_thread.h b/chromium/ui/base/x/x11_ui_thread.h
new file mode 100644
index 00000000000..8b9bcbc86ec
--- /dev/null
+++ b/chromium/ui/base/x/x11_ui_thread.h
@@ -0,0 +1,48 @@
+// 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 UI_BASE_X_X11_UI_THREAD_H_
+#define UI_BASE_X_X11_UI_THREAD_H_
+
+#include "base/component_export.h"
+#include "base/threading/thread.h"
+
+namespace x11 {
+class Connection;
+}
+
+namespace ui {
+
+class X11EventSource;
+
+// A thread-local x11::Connection may be used on this thread, stored in TLS and
+// obtained via x11::Connection::Get(). X11 events for the connection may also
+// dispatch events on this thread. This thread must be started as TYPE_UI.
+// TODO(thomasanderson): This can be removed once Linux switches to ozone.
+class COMPONENT_EXPORT(UI_BASE_X) X11UiThread : public base::Thread {
+ public:
+ explicit X11UiThread(const std::string& thread_name);
+
+ ~X11UiThread() override;
+
+ X11UiThread(const X11UiThread&) = delete;
+ X11UiThread& operator=(const X11UiThread&) = delete;
+
+ // Sets the global connection which will have its ownership transferred to the
+ // next X11UiThread created.
+ static void SetConnection(x11::Connection* connection);
+
+ protected:
+ // base::Thread:
+ void Init() override;
+ void CleanUp() override;
+
+ private:
+ std::unique_ptr<x11::Connection> connection_;
+ std::unique_ptr<X11EventSource> event_source_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_X11_UI_THREAD_H_
diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc
index e09f75b75c0..44526ec4218 100644
--- a/chromium/ui/base/x/x11_util.cc
+++ b/chromium/ui/base/x/x11_util.cc
@@ -22,10 +22,11 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
+#include "base/debug/stack_trace.h"
#include "base/location.h"
#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
@@ -35,6 +36,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_byteorder.h"
+#include "base/task/current_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_local_storage.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -45,8 +47,9 @@
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkTypes.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+#include "ui/base/x/x11_cursor.h"
+#include "ui/base/x/x11_cursor_loader.h"
#include "ui/base/x/x11_menu_list.h"
-#include "ui/base/x/x11_util_internal.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event_utils.h"
@@ -94,7 +97,7 @@ int DefaultX11ErrorHandler(XDisplay* d, XErrorEvent* e) {
if (TLSDestructionCheckerForX11::HasBeenDestroyed())
return 0;
- if (base::MessageLoopCurrent::Get()) {
+ if (base::CurrentThread::Get()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&x11::LogErrorEventDescription, *e));
} else {
@@ -163,219 +166,6 @@ bool GetWindowManagerName(std::string* wm_name) {
return !err_tracker.FoundNewError() && result;
}
-unsigned int GetMaxCursorSize() {
- constexpr unsigned int kQuerySize = std::numeric_limits<uint16_t>::max();
- auto* connection = x11::Connection::Get();
- x11::QueryBestSizeRequest request{
- x11::QueryShapeOf::LargestCursor,
- static_cast<x11::Window>(GetX11RootWindow()), kQuerySize, kQuerySize};
- if (auto response = connection->QueryBestSize(request).Sync())
- return std::min(response->width, response->height);
- // libXcursor defines MAX_BITMAP_CURSOR_SIZE to 64 in src/xcursorint.h, so use
- // this as a fallback in case the X server returns zero size, which can happen
- // on some buggy implementations of XWayland/XMir.
- return 64;
-}
-
-// A process wide singleton cache for custom X cursors.
-class XCustomCursorCache {
- public:
- static XCustomCursorCache* GetInstance() {
- return base::Singleton<XCustomCursorCache>::get();
- }
-
- ::Cursor InstallCustomCursor(XcursorImage* image) {
- XCustomCursor* custom_cursor = new XCustomCursor(image);
- ::Cursor xcursor = custom_cursor->cursor();
- cache_[xcursor] = custom_cursor;
- return xcursor;
- }
-
- void Ref(::Cursor cursor) { cache_[cursor]->Ref(); }
-
- void Unref(::Cursor cursor) {
- if (cache_[cursor]->Unref())
- cache_.erase(cursor);
- }
-
- void Clear() { cache_.clear(); }
-
- const XcursorImage* GetXcursorImage(::Cursor cursor) const {
- return cache_.find(cursor)->second->image();
- }
-
- private:
- friend struct base::DefaultSingletonTraits<XCustomCursorCache>;
-
- class XCustomCursor {
- public:
- // This takes ownership of the image.
- explicit XCustomCursor(XcursorImage* image) : image_(image), ref_(1) {
- cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
- }
-
- ~XCustomCursor() {
- XcursorImageDestroy(image_);
- XFreeCursor(gfx::GetXDisplay(), cursor_);
- }
-
- ::Cursor cursor() const { return cursor_; }
-
- void Ref() { ++ref_; }
-
- // Returns true if the cursor was destroyed because of the unref.
- bool Unref() {
- if (--ref_ == 0) {
- delete this;
- return true;
- }
- return false;
- }
-
- const XcursorImage* image() const { return image_; }
-
- private:
- XcursorImage* image_;
- int ref_;
- ::Cursor cursor_;
-
- DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
- };
-
- XCustomCursorCache() = default;
- ~XCustomCursorCache() { Clear(); }
-
- std::map<::Cursor, XCustomCursor*> cache_;
- DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
-};
-
-// Converts a SKBitmap to unpremul alpha.
-SkBitmap ConvertSkBitmapToUnpremul(const SkBitmap& bitmap) {
- DCHECK_NE(bitmap.alphaType(), kUnpremul_SkAlphaType);
-
- SkImageInfo image_info = SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
- kUnpremul_SkAlphaType);
- SkBitmap converted_bitmap;
- converted_bitmap.allocPixels(image_info);
- bitmap.readPixels(image_info, converted_bitmap.getPixels(),
- image_info.minRowBytes(), 0, 0);
-
- return converted_bitmap;
-}
-
-// Returns a cursor name, compatible with either X11 or the FreeDesktop.org
-// cursor spec
-// (https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#x_font_cursors
-// and https://www.freedesktop.org/wiki/Specifications/cursor-spec/), followed
-// by fallbacks that can work as replacements in some environments where the
-// original may not be available (e.g. desktop environments other than
-// GNOME and KDE).
-// TODO(hferreiro): each list starts with the FreeDesktop.org icon name but
-// "ns-resize", "ew-resize", "nesw-resize", "nwse-resize", "grab", "grabbing",
-// which were not available in older versions of Breeze, the default KDE theme.
-std::vector<const char*> CursorNamesFromType(mojom::CursorType type) {
- switch (type) {
- case mojom::CursorType::kMove:
- // Returning "move" is the correct thing here, but Blink doesn't make a
- // distinction between move and all-scroll. Other platforms use a cursor
- // more consistent with all-scroll, so use that.
- case mojom::CursorType::kMiddlePanning:
- case mojom::CursorType::kMiddlePanningVertical:
- case mojom::CursorType::kMiddlePanningHorizontal:
- return {"all-scroll", "fleur"};
- case mojom::CursorType::kEastPanning:
- case mojom::CursorType::kEastResize:
- return {"e-resize", "right_side"};
- case mojom::CursorType::kNorthPanning:
- case mojom::CursorType::kNorthResize:
- return {"n-resize", "top_side"};
- case mojom::CursorType::kNorthEastPanning:
- case mojom::CursorType::kNorthEastResize:
- return {"ne-resize", "top_right_corner"};
- case mojom::CursorType::kNorthWestPanning:
- case mojom::CursorType::kNorthWestResize:
- return {"nw-resize", "top_left_corner"};
- case mojom::CursorType::kSouthPanning:
- case mojom::CursorType::kSouthResize:
- return {"s-resize", "bottom_side"};
- case mojom::CursorType::kSouthEastPanning:
- case mojom::CursorType::kSouthEastResize:
- return {"se-resize", "bottom_right_corner"};
- case mojom::CursorType::kSouthWestPanning:
- case mojom::CursorType::kSouthWestResize:
- return {"sw-resize", "bottom_left_corner"};
- case mojom::CursorType::kWestPanning:
- case mojom::CursorType::kWestResize:
- return {"w-resize", "left_side"};
- case mojom::CursorType::kNone:
- return {"none"};
- case mojom::CursorType::kGrab:
- return {"openhand", "grab"};
- case mojom::CursorType::kGrabbing:
- return {"closedhand", "grabbing", "hand2"};
- case mojom::CursorType::kCross:
- return {"crosshair", "cross"};
- case mojom::CursorType::kHand:
- return {"pointer", "hand", "hand2"};
- case mojom::CursorType::kIBeam:
- return {"text", "xterm"};
- case mojom::CursorType::kProgress:
- return {"progress", "left_ptr_watch", "watch"};
- case mojom::CursorType::kWait:
- return {"wait", "watch"};
- case mojom::CursorType::kHelp:
- return {"help"};
- case mojom::CursorType::kNorthSouthResize:
- return {"sb_v_double_arrow", "ns-resize"};
- case mojom::CursorType::kEastWestResize:
- return {"sb_h_double_arrow", "ew-resize"};
- case mojom::CursorType::kColumnResize:
- return {"col-resize", "sb_h_double_arrow"};
- case mojom::CursorType::kRowResize:
- return {"row-resize", "sb_v_double_arrow"};
- case mojom::CursorType::kNorthEastSouthWestResize:
- return {"size_bdiag", "nesw-resize", "fd_double_arrow"};
- case mojom::CursorType::kNorthWestSouthEastResize:
- return {"size_fdiag", "nwse-resize", "bd_double_arrow"};
- case mojom::CursorType::kVerticalText:
- return {"vertical-text"};
- case mojom::CursorType::kZoomIn:
- return {"zoom-in"};
- case mojom::CursorType::kZoomOut:
- return {"zoom-out"};
- case mojom::CursorType::kCell:
- return {"cell", "plus"};
- case mojom::CursorType::kContextMenu:
- return {"context-menu"};
- case mojom::CursorType::kAlias:
- return {"alias"};
- case mojom::CursorType::kNoDrop:
- return {"no-drop"};
- case mojom::CursorType::kCopy:
- return {"copy"};
- case mojom::CursorType::kNotAllowed:
- return {"not-allowed", "crossed_circle"};
- case mojom::CursorType::kDndNone:
- return {"dnd-none", "hand2"};
- case mojom::CursorType::kDndMove:
- return {"dnd-move", "hand2"};
- case mojom::CursorType::kDndCopy:
- return {"dnd-copy", "hand2"};
- case mojom::CursorType::kDndLink:
- return {"dnd-link", "hand2"};
- case mojom::CursorType::kCustom:
- // kCustom is for custom image cursors. The platform cursor will be set
- // at WebCursor::GetPlatformCursor().
- NOTREACHED();
- FALLTHROUGH;
- case mojom::CursorType::kNull:
- case mojom::CursorType::kPointer:
- return {"left_ptr"};
- }
- NOTREACHED();
- return {"left_ptr"};
-}
-
} // namespace
void DeleteProperty(x11::Window window, x11::Atom name) {
@@ -425,14 +215,9 @@ void WithdrawWindow(x11::Window window) {
auto root = connection->default_root();
x11::UnmapNotifyEvent event{.event = root, .window = window};
- auto event_bytes = x11::Write(event);
- event_bytes.resize(32);
-
auto mask =
x11::EventMask::SubstructureNotify | x11::EventMask::SubstructureRedirect;
- x11::SendEventRequest request{false, root, mask};
- std::copy(event_bytes.begin(), event_bytes.end(), request.event.begin());
- connection->SendEvent(request);
+ SendEvent(event, root, mask);
}
void RaiseWindow(x11::Window window) {
@@ -473,37 +258,59 @@ x11::Window CreateDummyWindow(const std::string& name) {
return window;
}
-x11::KeyCode KeysymToKeycode(x11::Connection* connection, x11::KeySym keysym) {
- uint8_t min_keycode = static_cast<uint8_t>(connection->setup().min_keycode);
- uint8_t max_keycode = static_cast<uint8_t>(connection->setup().max_keycode);
- uint8_t count = max_keycode - min_keycode + 1;
- auto future =
- connection->GetKeyboardMapping({connection->setup().min_keycode, count});
- if (auto reply = future.Sync()) {
- DCHECK_EQ(count * reply->keysyms_per_keycode,
- static_cast<int>(reply->keysyms.size()));
- for (size_t i = 0; i < reply->keysyms.size(); i++) {
- if (reply->keysyms[i] == keysym) {
- return static_cast<x11::KeyCode>(min_keycode +
- i / reply->keysyms_per_keycode);
- }
- }
+void DrawPixmap(x11::Connection* connection,
+ x11::VisualId visual,
+ x11::Drawable drawable,
+ x11::GraphicsContext gc,
+ const SkPixmap& skia_pixmap,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ int width,
+ int height) {
+ const auto* visual_info = connection->GetVisualInfoFromId(visual);
+ if (!visual_info)
+ return;
+
+ auto bpp = visual_info->format->bits_per_pixel;
+ auto align = visual_info->format->scanline_pad;
+ size_t row_bits = bpp * width;
+ row_bits += (align - (row_bits % align)) % align;
+ size_t row_bytes = (row_bits + 7) / 8;
+
+ auto color_type = ColorTypeForVisual(visual);
+ if (color_type == kUnknown_SkColorType) {
+ // TODO(https://crbug.com/1066670): Add a fallback path in case any users
+ // are running a server that uses visual types for which Skia doesn't have
+ // a corresponding color format.
+ return;
}
- return {};
+ SkImageInfo image_info =
+ SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
+
+ std::vector<uint8_t> vec(row_bytes * height);
+ SkPixmap pixmap(image_info, vec.data(), row_bytes);
+ skia_pixmap.readPixels(pixmap, src_x, src_y);
+ x11::PutImageRequest put_image_request{
+ .format = x11::ImageFormat::ZPixmap,
+ .drawable = drawable,
+ .gc = gc,
+ .width = width,
+ .height = height,
+ .dst_x = dst_x,
+ .dst_y = dst_y,
+ .left_pad = 0,
+ .depth = visual_info->format->depth,
+ .data = base::RefCountedBytes::TakeVector(&vec),
+ };
+ connection->PutImage(put_image_request);
}
bool IsXInput2Available() {
return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
}
-bool QueryRenderSupport(Display* dpy) {
- int dummy;
- // We don't care about the version of Xrender since all the features which
- // we use are included in every version.
- static bool render_supported = XRenderQueryExtension(dpy, &dummy, &dummy);
- return render_supported;
-}
-
bool QueryShmSupport() {
int major;
int minor;
@@ -518,95 +325,24 @@ int ShmEventBase() {
return event_base;
}
-::Cursor CreateReffedCustomXCursor(XcursorImage* image) {
- return XCustomCursorCache::GetInstance()->InstallCustomCursor(image);
-}
-
-void RefCustomXCursor(::Cursor cursor) {
- XCustomCursorCache::GetInstance()->Ref(cursor);
-}
-
-void UnrefCustomXCursor(::Cursor cursor) {
- XCustomCursorCache::GetInstance()->Unref(cursor);
-}
-
-XcursorImage* SkBitmapToXcursorImage(const SkBitmap& cursor_image,
- const gfx::Point& hotspot) {
- // TODO(crbug.com/596782): It is possible for cursor_image to be zeroed out
- // at this point, which leads to benign debug errors. Once this is fixed, we
- // should DCHECK_EQ(cursor_image.colorType(), kN32_SkColorType).
-
- // X11 expects bitmap with unpremul alpha. If bitmap is premul then convert,
- // otherwise semi-transparent parts of cursor will look strange.
- const SkBitmap converted = (cursor_image.alphaType() != kUnpremul_SkAlphaType)
- ? ConvertSkBitmapToUnpremul(cursor_image)
- : cursor_image;
-
- gfx::Point hotspot_point = hotspot;
- SkBitmap scaled;
-
- // X11 seems to have issues with cursors when images get larger than 64
- // pixels. So rescale the image if necessary.
- static const float kMaxPixel = GetMaxCursorSize();
- bool needs_scale = false;
- if (converted.width() > kMaxPixel || converted.height() > kMaxPixel) {
- float scale = 1.f;
- if (converted.width() > converted.height())
- scale = kMaxPixel / converted.width();
- else
- scale = kMaxPixel / converted.height();
-
- scaled = skia::ImageOperations::Resize(
- converted, skia::ImageOperations::RESIZE_BETTER,
- static_cast<int>(converted.width() * scale),
- static_cast<int>(converted.height() * scale));
- hotspot_point = gfx::ScaleToFlooredPoint(hotspot, scale);
- needs_scale = true;
- }
-
- const SkBitmap& bitmap = needs_scale ? scaled : converted;
- XcursorImage* image = XcursorImageCreate(bitmap.width(), bitmap.height());
- image->xhot = std::min(bitmap.width() - 1, hotspot_point.x());
- image->yhot = std::min(bitmap.height() - 1, hotspot_point.y());
-
- if (bitmap.width() && bitmap.height()) {
- // The |bitmap| contains ARGB image, so just copy it.
- memcpy(image->pixels, bitmap.getPixels(),
- bitmap.width() * bitmap.height() * 4);
- }
-
- return image;
-}
-
-::Cursor LoadCursorFromType(mojom::CursorType type) {
- for (auto* name : CursorNamesFromType(type)) {
- ::Cursor cursor = XcursorLibraryLoadCursor(gfx::GetXDisplay(), name);
- if (cursor != x11::None)
- return cursor;
- }
- return x11::None;
-}
-
int CoalescePendingMotionEvents(const x11::Event* x11_event,
x11::Event* last_event) {
- const XEvent* xev = &x11_event->xlib_event();
- DCHECK(xev->type == x11::MotionNotifyEvent::opcode ||
- xev->type == x11::GeGenericEvent::opcode);
+ const auto* motion = x11_event->As<x11::MotionNotifyEvent>();
+ const auto* device = x11_event->As<x11::Input::DeviceEvent>();
+ DCHECK(motion || device);
auto* conn = x11::Connection::Get();
- bool is_motion = false;
int num_coalesced = 0;
conn->ReadResponses();
- if (xev->type == x11::MotionNotifyEvent::opcode) {
- is_motion = true;
+ if (motion) {
for (auto it = conn->events().begin(); it != conn->events().end();) {
- const auto& next_event = it->xlib_event();
+ const auto& next_event = *it;
// Discard all but the most recent motion event that targets the same
// window with unchanged state.
- if (next_event.type == x11::MotionNotifyEvent::opcode &&
- next_event.xmotion.window == xev->xmotion.window &&
- next_event.xmotion.subwindow == xev->xmotion.subwindow &&
- next_event.xmotion.state == xev->xmotion.state) {
+ const auto* next_motion = next_event.As<x11::MotionNotifyEvent>();
+ if (next_motion && next_motion->event == motion->event &&
+ next_motion->child == motion->child &&
+ next_motion->state == motion->state) {
*last_event = std::move(*it);
it = conn->events().erase(it);
} else {
@@ -614,48 +350,39 @@ int CoalescePendingMotionEvents(const x11::Event* x11_event,
}
}
} else {
- int event_type = xev->xgeneric.evtype;
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
- DCHECK(event_type == XI_Motion || event_type == XI_TouchUpdate);
- is_motion = event_type == XI_Motion;
+ DCHECK(device->opcode == x11::Input::DeviceEvent::Motion ||
+ device->opcode == x11::Input::DeviceEvent::TouchUpdate);
auto* ddmx11 = ui::DeviceDataManagerX11::GetInstance();
for (auto it = conn->events().begin(); it != conn->events().end();) {
- auto& next_event = it->xlib_event();
+ auto* next_device = it->As<x11::Input::DeviceEvent>();
- if (next_event.type != x11::GeGenericEvent::opcode ||
- !next_event.xcookie.data) {
+ if (!next_device)
break;
- }
// If this isn't from a valid device, throw the event away, as
// that's what the message pump would do. Device events come in pairs
// with one from the master and one from the slave so there will
// always be at least one pending.
- if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(
- &next_event)) {
+ if (!ui::TouchFactory::GetInstance()->ShouldProcessDeviceEvent(
+ *next_device)) {
it = conn->events().erase(it);
continue;
}
- if (next_event.type == x11::GeGenericEvent::opcode &&
- next_event.xgeneric.evtype == event_type &&
+ if (next_device->opcode == device->opcode &&
!ddmx11->IsCMTGestureEvent(*it) &&
ddmx11->GetScrollClassEventDetail(*it) == SCROLL_TYPE_NO_SCROLL) {
- XIDeviceEvent* next_xievent =
- static_cast<XIDeviceEvent*>(next_event.xcookie.data);
// Confirm that the motion event is targeted at the same window
// and that no buttons or modifiers have changed.
- if (xievent->event == next_xievent->event &&
- xievent->child == next_xievent->child &&
- xievent->detail == next_xievent->detail &&
- xievent->buttons.mask_len == next_xievent->buttons.mask_len &&
- (memcmp(xievent->buttons.mask, next_xievent->buttons.mask,
- xievent->buttons.mask_len) == 0) &&
- xievent->mods.base == next_xievent->mods.base &&
- xievent->mods.latched == next_xievent->mods.latched &&
- xievent->mods.locked == next_xievent->mods.locked &&
- xievent->mods.effective == next_xievent->mods.effective) {
+ if (device->event == next_device->event &&
+ device->child == next_device->child &&
+ device->detail == next_device->detail &&
+ device->button_mask == next_device->button_mask &&
+ device->mods.base == next_device->mods.base &&
+ device->mods.latched == next_device->mods.latched &&
+ device->mods.locked == next_device->mods.locked &&
+ device->mods.effective == next_device->mods.effective) {
*last_event = std::move(*it);
it = conn->events().erase(it);
num_coalesced++;
@@ -669,27 +396,6 @@ int CoalescePendingMotionEvents(const x11::Event* x11_event,
return num_coalesced;
}
-void HideHostCursor() {
- static base::NoDestructor<XScopedCursor> invisible_cursor(
- CreateInvisibleCursor(), gfx::GetXDisplay());
- XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
- invisible_cursor->get());
-}
-
-::Cursor CreateInvisibleCursor() {
- XDisplay* xdisplay = gfx::GetXDisplay();
- ::Cursor invisible_cursor;
- char nodata[] = {0, 0, 0, 0, 0, 0, 0, 0};
- XColor black;
- black.red = black.green = black.blue = 0;
- Pixmap blank = XCreateBitmapFromData(xdisplay, DefaultRootWindow(xdisplay),
- nodata, 8, 8);
- invisible_cursor =
- XCreatePixmapCursor(xdisplay, blank, blank, &black, &black, 0, 0);
- XFreePixmap(xdisplay, blank);
- return invisible_cursor;
-}
-
void SetUseOSWindowFrame(x11::Window window, bool use_os_window_frame) {
// This data structure represents additional hints that we send to the window
// manager and has a direct lineage back to Motif, which defined this de facto
@@ -894,7 +600,7 @@ bool PropertyExists(x11::Window window, const std::string& property_name) {
bool GetRawBytesOfProperty(x11::Window window,
x11::Atom property,
- std::vector<uint8_t>* out_data,
+ scoped_refptr<base::RefCountedMemory>* out_data,
x11::Atom* out_type) {
auto future = x11::Connection::Get()->GetProperty({
.window = static_cast<x11::Window>(window),
@@ -905,7 +611,7 @@ bool GetRawBytesOfProperty(x11::Window window,
auto response = future.Sync();
if (!response || !response->format)
return false;
- *out_data = std::move(response->value);
+ *out_data = response->value;
if (out_type)
*out_type = response->type;
return true;
@@ -1249,6 +955,59 @@ std::string GuessWindowManagerName() {
return "Unknown";
}
+UMALinuxWindowManager GetWindowManagerUMA() {
+ switch (GuessWindowManager()) {
+ case WM_OTHER:
+ return UMALinuxWindowManager::kOther;
+ case WM_UNNAMED:
+ return UMALinuxWindowManager::kUnnamed;
+ case WM_AWESOME:
+ return UMALinuxWindowManager::kAwesome;
+ case WM_BLACKBOX:
+ return UMALinuxWindowManager::kBlackbox;
+ case WM_COMPIZ:
+ return UMALinuxWindowManager::kCompiz;
+ case WM_ENLIGHTENMENT:
+ return UMALinuxWindowManager::kEnlightenment;
+ case WM_FLUXBOX:
+ return UMALinuxWindowManager::kFluxbox;
+ case WM_I3:
+ return UMALinuxWindowManager::kI3;
+ case WM_ICE_WM:
+ return UMALinuxWindowManager::kIceWM;
+ case WM_ION3:
+ return UMALinuxWindowManager::kIon3;
+ case WM_KWIN:
+ return UMALinuxWindowManager::kKWin;
+ case WM_MATCHBOX:
+ return UMALinuxWindowManager::kMatchbox;
+ case WM_METACITY:
+ return UMALinuxWindowManager::kMetacity;
+ case WM_MUFFIN:
+ return UMALinuxWindowManager::kMuffin;
+ case WM_MUTTER:
+ return UMALinuxWindowManager::kMutter;
+ case WM_NOTION:
+ return UMALinuxWindowManager::kNotion;
+ case WM_OPENBOX:
+ return UMALinuxWindowManager::kOpenbox;
+ case WM_QTILE:
+ return UMALinuxWindowManager::kQtile;
+ case WM_RATPOISON:
+ return UMALinuxWindowManager::kRatpoison;
+ case WM_STUMPWM:
+ return UMALinuxWindowManager::kStumpWM;
+ case WM_WMII:
+ return UMALinuxWindowManager::kWmii;
+ case WM_XFWM4:
+ return UMALinuxWindowManager::kXfwm4;
+ case WM_XMONAD:
+ return UMALinuxWindowManager::kXmonad;
+ }
+ NOTREACHED();
+ return UMALinuxWindowManager::kOther;
+}
+
bool IsCompositingManagerPresent() {
auto is_compositing_manager_present_impl = []() {
auto response = x11::Connection::Get()
@@ -1311,10 +1070,10 @@ gfx::ICCProfile GetICCProfileForMonitor(int monitor) {
std::string atom_name = monitor == 0
? "_ICC_PROFILE"
: base::StringPrintf("_ICC_PROFILE_%d", monitor);
- std::vector<uint8_t> data;
+ scoped_refptr<base::RefCountedMemory> data;
if (GetRawBytesOfProperty(GetX11RootWindow(), gfx::GetAtom(atom_name), &data,
nullptr)) {
- icc_profile = gfx::ICCProfile::FromData(data.data(), data.size());
+ icc_profile = gfx::ICCProfile::FromData(data->data(), data->size());
}
return icc_profile;
}
@@ -1340,34 +1099,42 @@ bool IsSyncExtensionAvailable() {
#endif
}
-SkColorType ColorTypeForVisual(void* visual) {
+SkColorType ColorTypeForVisual(x11::VisualId visual) {
struct {
SkColorType color_type;
unsigned long red_mask;
unsigned long green_mask;
unsigned long blue_mask;
+ int bpp;
} color_infos[] = {
- {kRGB_565_SkColorType, 0xf800, 0x7e0, 0x1f},
- {kARGB_4444_SkColorType, 0xf000, 0xf00, 0xf0},
- {kRGBA_8888_SkColorType, 0xff, 0xff00, 0xff0000},
- {kBGRA_8888_SkColorType, 0xff0000, 0xff00, 0xff},
- {kRGBA_1010102_SkColorType, 0x3ff, 0xffc00, 0x3ff00000},
- {kBGRA_1010102_SkColorType, 0x3ff00000, 0xffc00, 0x3ff},
+ {kRGB_565_SkColorType, 0xf800, 0x7e0, 0x1f, 16},
+ {kARGB_4444_SkColorType, 0xf000, 0xf00, 0xf0, 16},
+ {kRGBA_8888_SkColorType, 0xff, 0xff00, 0xff0000, 32},
+ {kBGRA_8888_SkColorType, 0xff0000, 0xff00, 0xff, 32},
+ {kRGBA_1010102_SkColorType, 0x3ff, 0xffc00, 0x3ff00000, 32},
+ {kBGRA_1010102_SkColorType, 0x3ff00000, 0xffc00, 0x3ff, 32},
};
- Visual* vis = reinterpret_cast<Visual*>(visual);
- // When running under Xvfb, a visual may not be set.
- if (!vis || !vis->red_mask || !vis->green_mask || !vis->blue_mask)
+ auto* connection = x11::Connection::Get();
+ const auto* vis = connection->GetVisualInfoFromId(visual);
+ if (!vis)
+ return kUnknown_SkColorType;
+ // We don't currently support anything other than TrueColor and DirectColor.
+ if (!vis->visual_type->red_mask || !vis->visual_type->green_mask ||
+ !vis->visual_type->blue_mask) {
return kUnknown_SkColorType;
+ }
for (const auto& color_info : color_infos) {
- if (vis->red_mask == color_info.red_mask &&
- vis->green_mask == color_info.green_mask &&
- vis->blue_mask == color_info.blue_mask) {
+ if (vis->visual_type->red_mask == color_info.red_mask &&
+ vis->visual_type->green_mask == color_info.green_mask &&
+ vis->visual_type->blue_mask == color_info.blue_mask &&
+ vis->format->bits_per_pixel == color_info.bpp) {
return color_info.color_type;
}
}
LOG(ERROR) << "Unsupported visual with rgb mask 0x" << std::hex
- << vis->red_mask << ", 0x" << vis->green_mask << ", 0x"
- << vis->blue_mask
+ << vis->visual_type->red_mask << ", 0x"
+ << vis->visual_type->green_mask << ", 0x"
+ << vis->visual_type->blue_mask
<< ". Please report this to https://crbug.com/1025266";
return kUnknown_SkColorType;
}
@@ -1379,13 +1146,7 @@ x11::Future<void> SendClientMessage(x11::Window window,
x11::EventMask event_mask) {
x11::ClientMessageEvent event{.format = 32, .window = window, .type = type};
event.data.data32 = data;
- auto event_bytes = x11::Write(event);
- DCHECK_EQ(event_bytes.size(), 32ul);
-
- auto* connection = x11::Connection::Get();
- x11::SendEventRequest request{false, target, event_mask};
- std::copy(event_bytes.begin(), event_bytes.end(), request.event.begin());
- return connection->SendEvent(request);
+ return SendEvent(event, target, event_mask);
}
XRefcountedMemory::XRefcountedMemory(unsigned char* x11_data, size_t length)
@@ -1401,71 +1162,10 @@ size_t XRefcountedMemory::size() const {
XRefcountedMemory::~XRefcountedMemory() = default;
-XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
- : cursor_(cursor), display_(display) {}
-
-XScopedCursor::~XScopedCursor() {
- reset(0U);
-}
-
-::Cursor XScopedCursor::get() const {
- return cursor_;
-}
-
-void XScopedCursor::reset(::Cursor cursor) {
- if (cursor_)
- XFreeCursor(display_, cursor_);
- cursor_ = cursor;
-}
-
void XImageDeleter::operator()(XImage* image) const {
XDestroyImage(image);
}
-namespace test {
-
-const XcursorImage* GetCachedXcursorImage(::Cursor cursor) {
- return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor);
-}
-} // namespace test
-
-// ----------------------------------------------------------------------------
-// These functions are declared in x11_util_internal.h because they require
-// XLib.h to be included, and it conflicts with many other headers.
-XRenderPictFormat* GetRenderARGB32Format(XDisplay* dpy) {
- static XRenderPictFormat* pictformat = nullptr;
- if (pictformat)
- return pictformat;
-
- // First look for a 32-bit format which ignores the alpha value
- XRenderPictFormat templ;
- templ.depth = 32;
- templ.type = PictTypeDirect;
- templ.direct.red = 16;
- templ.direct.green = 8;
- templ.direct.blue = 0;
- templ.direct.redMask = 0xff;
- templ.direct.greenMask = 0xff;
- templ.direct.blueMask = 0xff;
- templ.direct.alphaMask = 0;
-
- static const unsigned long kMask =
- PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask |
- PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
- PictFormatBlueMask | PictFormatAlphaMask;
-
- pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */);
-
- if (!pictformat) {
- // Not all X servers support xRGB32 formats. However, the XRENDER spec says
- // that they must support an ARGB32 format, so we can always return that.
- pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
- CHECK(pictformat) << "XRENDER ARGB32 not supported.";
- }
-
- return pictformat;
-}
-
void SetX11ErrorHandlers(XErrorHandler error_handler,
XIOErrorHandler io_error_handler) {
XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
@@ -1484,7 +1184,7 @@ XVisualManager::XVisualManager() : connection_(x11::Connection::Get()) {
for (const auto& depth : connection_->default_screen().allowed_depths) {
for (const auto& visual : depth.visuals) {
visuals_[visual.visual_id] =
- std::make_unique<XVisualData>(depth.depth, &visual);
+ std::make_unique<XVisualData>(connection_, depth.depth, &visual);
}
}
@@ -1515,6 +1215,7 @@ XVisualManager::~XVisualManager() = default;
void XVisualManager::ChooseVisualForWindow(bool want_argb_visual,
x11::VisualId* visual_id,
uint8_t* depth,
+ x11::ColorMap* colormap,
bool* visual_has_alpha) {
base::AutoLock lock(lock_);
bool use_argb = want_argb_visual && IsCompositingManagerPresent() &&
@@ -1525,15 +1226,16 @@ void XVisualManager::ChooseVisualForWindow(bool want_argb_visual,
if (visual_id)
*visual_id = visual;
- bool success = GetVisualInfoImpl(visual, depth, visual_has_alpha);
+ bool success = GetVisualInfoImpl(visual, depth, colormap, visual_has_alpha);
DCHECK(success);
}
bool XVisualManager::GetVisualInfo(x11::VisualId visual_id,
uint8_t* depth,
+ x11::ColorMap* colormap,
bool* visual_has_alpha) {
base::AutoLock lock(lock_);
- return GetVisualInfoImpl(visual_id, depth, visual_has_alpha);
+ return GetVisualInfoImpl(visual_id, depth, colormap, visual_has_alpha);
}
bool XVisualManager::OnGPUInfoChanged(bool software_rendering,
@@ -1565,6 +1267,7 @@ bool XVisualManager::ArgbVisualAvailable() const {
bool XVisualManager::GetVisualInfoImpl(x11::VisualId visual_id,
uint8_t* depth,
+ x11::ColorMap* colormap,
bool* visual_has_alpha) {
auto it = visuals_.find(visual_id);
if (it == visuals_.end())
@@ -1572,8 +1275,12 @@ bool XVisualManager::GetVisualInfoImpl(x11::VisualId visual_id,
XVisualData& data = *it->second;
const x11::VisualType& info = *data.info;
+ bool is_default_visual = visual_id == default_visual_id_;
+
if (depth)
*depth = data.depth;
+ if (colormap)
+ *colormap = is_default_visual ? x11::ColorMap{} : data.GetColormap();
if (visual_has_alpha) {
auto popcount = [](auto x) {
return std::bitset<8 * sizeof(decltype(x))>(x).count();
@@ -1585,13 +1292,22 @@ bool XVisualManager::GetVisualInfoImpl(x11::VisualId visual_id,
return true;
}
-XVisualManager::XVisualData::XVisualData(uint8_t depth,
+XVisualManager::XVisualData::XVisualData(x11::Connection* connection,
+ uint8_t depth,
const x11::VisualType* info)
- : depth(depth), info(info) {}
+ : depth(depth), info(info), connection_(connection) {}
+// Do not free the colormap as this would uninstall the colormap even for
+// non-Chromium clients.
XVisualManager::XVisualData::~XVisualData() = default;
-// ----------------------------------------------------------------------------
-// End of x11_util_internal.h
+x11::ColorMap XVisualManager::XVisualData::GetColormap() {
+ if (colormap_ == x11::ColorMap{}) {
+ colormap_ = connection_->GenerateId<x11::ColorMap>();
+ connection_->CreateColormap({x11::ColormapAlloc::None, colormap_,
+ connection_->default_root(), info->visual_id});
+ }
+ return colormap_;
+}
} // namespace ui
diff --git a/chromium/ui/base/x/x11_util.h b/chromium/ui/base/x/x11_util.h
index 4c7c574a921..7211fb91e67 100644
--- a/chromium/ui/base/x/x11_util.h
+++ b/chromium/ui/base/x/x11_util.h
@@ -15,28 +15,39 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+#include "ui/base/x/x11_cursor.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/platform_event.h"
#include "ui/gfx/icc_profile.h"
#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xproto_types.h"
typedef unsigned long Cursor;
+class SkPixmap;
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
namespace gfx {
class Insets;
class Point;
class Rect;
} // namespace gfx
-class SkBitmap;
namespace ui {
@@ -142,9 +153,9 @@ bool GetArrayProperty(x11::Window window,
return false;
DCHECK_EQ(response->format / CHAR_BIT * response->value_len,
- response->value.size());
+ response->value->size());
value->resize(response->value_len);
- memcpy(value->data(), response->value.data(), response->value.size());
+ memcpy(value->data(), response->value->data(), response->value->size());
if (out_type)
*out_type = response->type;
return true;
@@ -173,7 +184,7 @@ void SetArrayProperty(x11::Window window,
.type = type,
.format = CHAR_BIT * sizeof(T),
.data_len = values.size(),
- .data = data});
+ .data = base::RefCountedBytes::TakeVector(&data)});
}
template <typename T>
@@ -185,15 +196,20 @@ void SetProperty(x11::Window window,
}
template <typename T>
-void SendEvent(const T& event, x11::Window target, x11::EventMask mask) {
+x11::Future<void> SendEvent(const T& event,
+ x11::Window target,
+ x11::EventMask mask) {
static_assert(T::type_id > 0, "T must be an x11::*Event type");
- auto event_bytes = x11::Write(event);
- DCHECK_LE(event_bytes.size(), 32ul);
- event_bytes.resize(32);
+ auto write_buffer = x11::Write(event);
+ DCHECK_EQ(write_buffer.GetBuffers().size(), 1ul);
+ auto& first_buffer = write_buffer.GetBuffers()[0];
+ DCHECK_LE(first_buffer->size(), 32ul);
+ std::vector<uint8_t> event_bytes(32);
+ memcpy(event_bytes.data(), first_buffer->data(), first_buffer->size());
x11::SendEventRequest send_event{false, target, mask};
std::copy(event_bytes.begin(), event_bytes.end(), send_event.event.begin());
- x11::Connection::Get()->SendEvent(send_event);
+ return x11::Connection::Get()->SendEvent(send_event);
}
COMPONENT_EXPORT(UI_BASE_X)
@@ -226,58 +242,38 @@ void DefineCursor(x11::Window window, x11::Cursor cursor);
COMPONENT_EXPORT(UI_BASE_X)
x11::Window CreateDummyWindow(const std::string& name = "");
-COMPONENT_EXPORT(UI_BASE_X)
-x11::KeyCode KeysymToKeycode(x11::Connection* connection, x11::KeySym keysym);
+// Draws an SkPixmap on |drawable| using the given |gc|, converting to the
+// server side visual as needed.
+COMPONENT_EXPORT(UI_BASE_X)
+void DrawPixmap(x11::Connection* connection,
+ x11::VisualId visual,
+ x11::Drawable drawable,
+ x11::GraphicsContext gc,
+ const SkPixmap& skia_pixmap,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ int width,
+ int height);
// These functions cache their results ---------------------------------
// Returns true if the system supports XINPUT2.
COMPONENT_EXPORT(UI_BASE_X) bool IsXInput2Available();
-// Return true iff the display supports Xrender
-COMPONENT_EXPORT(UI_BASE_X) bool QueryRenderSupport(XDisplay* dpy);
-
// Return true iff the display supports MIT-SHM.
COMPONENT_EXPORT(UI_BASE_X) bool QueryShmSupport();
// Returns the first event ID for the MIT-SHM extension, if available.
COMPONENT_EXPORT(UI_BASE_X) int ShmEventBase();
-// Creates a custom X cursor from the image. This takes ownership of image. The
-// caller must not free/modify the image. The refcount of the newly created
-// cursor is set to 1.
-COMPONENT_EXPORT(UI_BASE_X)::Cursor
- CreateReffedCustomXCursor(XcursorImage* image);
-
-// Increases the refcount of the custom cursor.
-COMPONENT_EXPORT(UI_BASE_X) void RefCustomXCursor(::Cursor cursor);
-
-// Decreases the refcount of the custom cursor, and destroys it if it reaches 0.
-COMPONENT_EXPORT(UI_BASE_X) void UnrefCustomXCursor(::Cursor cursor);
-
-// Creates a XcursorImage and copies the SkBitmap |bitmap| on it. Caller owns
-// the returned object.
-COMPONENT_EXPORT(UI_BASE_X)
-XcursorImage* SkBitmapToXcursorImage(const SkBitmap& bitmap,
- const gfx::Point& hotspot);
-
-// Loads and returns an X11 cursor, trying to find one that matches |type|. If
-// unavailable, x11::None is returned.
-COMPONENT_EXPORT(UI_BASE_X)
-::Cursor LoadCursorFromType(mojom::CursorType type);
-
// Coalesce all pending motion events (touch or mouse) that are at the top of
// the queue, and return the number eliminated, storing the last one in
// |last_event|.
COMPONENT_EXPORT(UI_BASE_X)
int CoalescePendingMotionEvents(const x11::Event* xev, x11::Event* last_event);
-// Hides the host cursor.
-COMPONENT_EXPORT(UI_BASE_X) void HideHostCursor();
-
-// Returns an invisible cursor.
-COMPONENT_EXPORT(UI_BASE_X)::Cursor CreateInvisibleCursor();
-
// Sets whether |window| should use the OS window frame.
COMPONENT_EXPORT(UI_BASE_X)
void SetUseOSWindowFrame(x11::Window window, bool use_os_window_frame);
@@ -336,7 +332,7 @@ bool PropertyExists(x11::Window window, const std::string& property_name);
COMPONENT_EXPORT(UI_BASE_X)
bool GetRawBytesOfProperty(x11::Window window,
x11::Atom property,
- std::vector<uint8_t>* out_data,
+ scoped_refptr<base::RefCountedMemory>* out_data,
x11::Atom* out_type);
// Get the value of an int, int array, atom array or string property. On
@@ -499,6 +495,42 @@ COMPONENT_EXPORT(UI_BASE_X) WindowManagerName GuessWindowManager();
// can't determine it, return "Unknown".
COMPONENT_EXPORT(UI_BASE_X) std::string GuessWindowManagerName();
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+//
+// Append new window managers before kMaxValue and update LinuxWindowManagerName
+// in tools/metrics/histograms/enums.xml accordingly.
+//
+// See also tools/metrics/histograms/README.md#enum-histograms
+enum class UMALinuxWindowManager {
+ kOther = 0,
+ kBlackbox = 1,
+ kChromeOS = 2, // Deprecated.
+ kCompiz = 3,
+ kEnlightenment = 4,
+ kIceWM = 5,
+ kKWin = 6,
+ kMetacity = 7,
+ kMuffin = 8,
+ kMutter = 9,
+ kOpenbox = 10,
+ kXfwm4 = 11,
+ kAwesome = 12,
+ kI3 = 13,
+ kIon3 = 14,
+ kMatchbox = 15,
+ kNotion = 16,
+ kQtile = 17,
+ kRatpoison = 18,
+ kStumpWM = 19,
+ kWmii = 20,
+ kFluxbox = 21,
+ kXmonad = 22,
+ kUnnamed = 23,
+ kMaxValue = kUnnamed
+};
+COMPONENT_EXPORT(UI_BASE_X) UMALinuxWindowManager GetWindowManagerUMA();
+
// Returns a buest-effort guess as to whether |window_manager| is tiling (true)
// or stacking (false).
COMPONENT_EXPORT(UI_BASE_X) bool IsWmTiling(WindowManagerName window_manager);
@@ -507,8 +539,8 @@ COMPONENT_EXPORT(UI_BASE_X) bool IsWmTiling(WindowManagerName window_manager);
COMPONENT_EXPORT(UI_BASE_X) bool IsCompositingManagerPresent();
// Enable the default X error handlers. These will log the error and abort
-// the process if called. Use SetX11ErrorHandlers() from x11_util_internal.h
-// to set your own error handlers.
+// the process if called. Use SetX11ErrorHandlers() to set your own error
+// handlers.
COMPONENT_EXPORT(UI_BASE_X) void SetDefaultX11ErrorHandlers();
// Returns true if a given window is in full-screen mode.
@@ -524,10 +556,10 @@ gfx::ICCProfile GetICCProfileForMonitor(int monitor);
// Return true if the display supports SYNC extension.
COMPONENT_EXPORT(UI_BASE_X) bool IsSyncExtensionAvailable();
-// Returns the preferred Skia colortype for an X11 visual. LOG(FATAL)'s if
-// there isn't a suitable colortype.
+// Returns the preferred Skia colortype for an X11 visual. Returns
+// kUnknown_SkColorType if there isn't a suitable colortype.
COMPONENT_EXPORT(UI_BASE_X)
-SkColorType ColorTypeForVisual(void* visual);
+SkColorType ColorTypeForVisual(x11::VisualId visual_id);
COMPONENT_EXPORT(UI_BASE_X)
x11::Future<void> SendClientMessage(
@@ -559,36 +591,103 @@ class COMPONENT_EXPORT(UI_BASE_X) XRefcountedMemory
DISALLOW_COPY_AND_ASSIGN(XRefcountedMemory);
};
-// Keeps track of a cursor returned by an X function and makes sure it's
-// XFreeCursor'd.
-class COMPONENT_EXPORT(UI_BASE_X) XScopedCursor {
+struct COMPONENT_EXPORT(UI_BASE_X) XImageDeleter {
+ void operator()(XImage* image) const;
+};
+using XScopedImage = std::unique_ptr<XImage, XImageDeleter>;
+
+// --------------------------------------------------------------------------
+// X11 error handling.
+// Sets the X Error Handlers. Passing NULL for either will enable the default
+// error handler, which if called will log the error and abort the process.
+COMPONENT_EXPORT(UI_BASE_X)
+void SetX11ErrorHandlers(XErrorHandler error_handler,
+ XIOErrorHandler io_error_handler);
+
+// NOTE: This function should not be called directly from the
+// X11 Error handler because it queries the server to decode the
+// error message, which may trigger other errors. A suitable workaround
+// is to post a task in the error handler to call this function.
+COMPONENT_EXPORT(UI_BASE_X)
+void LogErrorEventDescription(Display* dpy, const XErrorEvent& error_event);
+
+// --------------------------------------------------------------------------
+// Selects a visual with a preference for alpha support on compositing window
+// managers.
+class COMPONENT_EXPORT(UI_BASE_X) XVisualManager {
public:
- // Keeps track of |cursor| created with |display|.
- XScopedCursor(::Cursor cursor, XDisplay* display);
- ~XScopedCursor();
+ static XVisualManager* GetInstance();
+
+ // Picks the best argb or opaque visual given |want_argb_visual|.
+ void ChooseVisualForWindow(bool want_argb_visual,
+ x11::VisualId* visual_id,
+ uint8_t* depth,
+ x11::ColorMap* colormap,
+ bool* visual_has_alpha);
+
+ bool GetVisualInfo(x11::VisualId visual_id,
+ uint8_t* depth,
+ x11::ColorMap* colormap,
+ bool* visual_has_alpha);
- ::Cursor get() const;
- void reset(::Cursor cursor);
+ // Called by GpuDataManagerImplPrivate when GPUInfo becomes available. It is
+ // necessary for the GPU process to find out which visuals are best for GL
+ // because we don't want to load GL in the browser process. Returns false iff
+ // |default_visual_id| or |transparent_visual_id| are invalid.
+ bool OnGPUInfoChanged(bool software_rendering,
+ x11::VisualId default_visual_id,
+ x11::VisualId transparent_visual_id);
+
+ // Are all of the system requirements met for using transparent visuals?
+ bool ArgbVisualAvailable() const;
+
+ ~XVisualManager();
private:
- ::Cursor cursor_;
- XDisplay* display_;
+ friend struct base::DefaultSingletonTraits<XVisualManager>;
- DISALLOW_COPY_AND_ASSIGN(XScopedCursor);
-};
+ class XVisualData {
+ public:
+ XVisualData(x11::Connection* connection,
+ uint8_t depth,
+ const x11::VisualType* info);
+ ~XVisualData();
-struct COMPONENT_EXPORT(UI_BASE_X) XImageDeleter {
- void operator()(XImage* image) const;
-};
-using XScopedImage = std::unique_ptr<XImage, XImageDeleter>;
+ x11::ColorMap GetColormap();
-namespace test {
+ const uint8_t depth;
+ const x11::VisualType* const info;
-// Returns the cached XcursorImage for |cursor|.
-COMPONENT_EXPORT(UI_BASE_X)
-const XcursorImage* GetCachedXcursorImage(::Cursor cursor);
+ private:
+ x11::ColorMap colormap_{};
+ x11::Connection* const connection_;
+ };
+
+ XVisualManager();
+
+ bool GetVisualInfoImpl(x11::VisualId visual_id,
+ uint8_t* depth,
+ x11::ColorMap* colormap,
+ bool* visual_has_alpha);
+
+ mutable base::Lock lock_;
+
+ std::unordered_map<x11::VisualId, std::unique_ptr<XVisualData>> visuals_;
-} // namespace test
+ x11::Connection* const connection_;
+
+ x11::VisualId default_visual_id_{};
+
+ // The system visual is usually the same as the default visual, but
+ // may not be in general.
+ x11::VisualId system_visual_id_{};
+ x11::VisualId transparent_visual_id_{};
+
+ bool using_software_rendering_ = false;
+ bool have_gpu_argb_visual_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(XVisualManager);
+};
} // namespace ui
diff --git a/chromium/ui/base/x/x11_util_internal.h b/chromium/ui/base/x/x11_util_internal.h
deleted file mode 100644
index 705c606c7b5..00000000000
--- a/chromium/ui/base/x/x11_util_internal.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_X_X11_UTIL_INTERNAL_H_
-#define UI_BASE_X_X11_UTIL_INTERNAL_H_
-
-// This file declares utility functions for X11 (Linux only).
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/synchronization/lock.h"
-#include "build/build_config.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_types.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-namespace ui {
-
-// --------------------------------------------------------------------------
-// NOTE: these functions cache the results and must be called from the UI
-// thread.
-// Get the XRENDER format id for ARGB32 (Skia's format).
-//
-// NOTE:Currently this don't support multiple screens/displays.
-COMPONENT_EXPORT(UI_BASE_X)
-XRenderPictFormat* GetRenderARGB32Format(Display* dpy);
-
-// --------------------------------------------------------------------------
-// X11 error handling.
-// Sets the X Error Handlers. Passing NULL for either will enable the default
-// error handler, which if called will log the error and abort the process.
-COMPONENT_EXPORT(UI_BASE_X)
-void SetX11ErrorHandlers(XErrorHandler error_handler,
- XIOErrorHandler io_error_handler);
-
-// NOTE: This function should not be called directly from the
-// X11 Error handler because it queries the server to decode the
-// error message, which may trigger other errors. A suitable workaround
-// is to post a task in the error handler to call this function.
-COMPONENT_EXPORT(UI_BASE_X)
-void LogErrorEventDescription(Display* dpy, const XErrorEvent& error_event);
-
-// --------------------------------------------------------------------------
-// Selects a visual with a preference for alpha support on compositing window
-// managers.
-class COMPONENT_EXPORT(UI_BASE_X) XVisualManager {
- public:
- static XVisualManager* GetInstance();
-
- // Picks the best argb or opaque visual given |want_argb_visual|.
- void ChooseVisualForWindow(bool want_argb_visual,
- x11::VisualId* visual_id,
- uint8_t* depth,
- bool* visual_has_alpha);
-
- bool GetVisualInfo(x11::VisualId visual_id,
- uint8_t* depth,
- bool* visual_has_alpha);
-
- // Called by GpuDataManagerImplPrivate when GPUInfo becomes available. It is
- // necessary for the GPU process to find out which visuals are best for GL
- // because we don't want to load GL in the browser process. Returns false iff
- // |default_visual_id| or |transparent_visual_id| are invalid.
- bool OnGPUInfoChanged(bool software_rendering,
- x11::VisualId default_visual_id,
- x11::VisualId transparent_visual_id);
-
- // Are all of the system requirements met for using transparent visuals?
- bool ArgbVisualAvailable() const;
-
- ~XVisualManager();
-
- private:
- friend struct base::DefaultSingletonTraits<XVisualManager>;
-
- class XVisualData {
- public:
- XVisualData(uint8_t depth, const x11::VisualType* info);
- ~XVisualData();
-
- uint8_t depth = 0;
- const x11::VisualType* info = nullptr;
- };
-
- XVisualManager();
-
- bool GetVisualInfoImpl(x11::VisualId visual_id,
- uint8_t* depth,
- bool* visual_has_alpha);
-
- mutable base::Lock lock_;
-
- std::unordered_map<x11::VisualId, std::unique_ptr<XVisualData>> visuals_;
-
- x11::Connection* const connection_;
-
- x11::VisualId default_visual_id_{};
-
- // The system visual is usually the same as the default visual, but
- // may not be in general.
- x11::VisualId system_visual_id_{};
- x11::VisualId transparent_visual_id_{};
-
- bool using_software_rendering_ = false;
- bool have_gpu_argb_visual_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(XVisualManager);
-};
-
-} // namespace ui
-
-#endif // UI_BASE_X_X11_UTIL_INTERNAL_H_
diff --git a/chromium/ui/base/x/x11_whole_screen_move_loop.cc b/chromium/ui/base/x/x11_whole_screen_move_loop.cc
index 39f4d0c12aa..42370306c8d 100644
--- a/chromium/ui/base/x/x11_whole_screen_move_loop.cc
+++ b/chromium/ui/base/x/x11_whole_screen_move_loop.cc
@@ -11,10 +11,10 @@
#include "base/bind.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
+#include "base/task/current_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/x/x11_pointer_grab.h"
#include "ui/base/x/x11_util.h"
@@ -28,6 +28,7 @@
#include "ui/events/x/x11_window_event_manager.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
@@ -52,12 +53,13 @@ constexpr x11::ModMask kModifiersMasks[] = {
X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
: delegate_(delegate),
in_move_loop_(false),
- initial_cursor_(x11::None),
grab_input_window_(x11::Window::None),
grabbed_pointer_(false),
canceled_(false) {}
-X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() = default;
+X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {
+ EndMoveLoop();
+}
void X11WholeScreenMoveLoop::DispatchMouseMovement() {
if (!last_motion_in_screen_)
@@ -90,7 +92,7 @@ bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) {
}
uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
// This method processes all events while the move loop is active.
if (!in_move_loop_)
@@ -131,9 +133,10 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
return ui::POST_DISPATCH_PERFORM_DEFAULT;
}
-bool X11WholeScreenMoveLoop::RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) {
+bool X11WholeScreenMoveLoop::RunMoveLoop(
+ bool can_grab_pointer,
+ scoped_refptr<ui::X11Cursor> old_cursor,
+ scoped_refptr<ui::X11Cursor> new_cursor) {
DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
// Query the mouse cursor prior to the move loop starting so that it can be
@@ -181,7 +184,7 @@ bool X11WholeScreenMoveLoop::RunMoveLoop(bool can_grab_pointer,
return !canceled_;
}
-void X11WholeScreenMoveLoop::UpdateCursor(::Cursor cursor) {
+void X11WholeScreenMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {
if (in_move_loop_)
ui::ChangeActivePointerGrabCursor(cursor);
}
@@ -204,7 +207,7 @@ void X11WholeScreenMoveLoop::EndMoveLoop() {
UpdateCursor(initial_cursor_);
auto* connection = x11::Connection::Get();
- auto esc_keycode = KeysymToKeycode(connection, kEscKeysym);
+ auto esc_keycode = connection->KeysymToKeycode(kEscKeysym);
for (auto mask : kModifiersMasks)
connection->UngrabKey({esc_keycode, grab_input_window_, mask});
@@ -219,23 +222,24 @@ void X11WholeScreenMoveLoop::EndMoveLoop() {
std::move(quit_closure_).Run();
}
-bool X11WholeScreenMoveLoop::GrabPointer(::Cursor cursor) {
+bool X11WholeScreenMoveLoop::GrabPointer(scoped_refptr<X11Cursor> cursor) {
auto* connection = x11::Connection::Get();
// Pass "owner_events" as false so that X sends all mouse events to
// |grab_input_window_|.
- int ret = ui::GrabPointer(grab_input_window_, false, cursor);
- if (ret != GrabSuccess) {
+ auto ret = ui::GrabPointer(grab_input_window_, false, cursor);
+ if (ret != x11::GrabStatus::Success) {
DLOG(ERROR) << "Grabbing pointer for dragging failed: "
- << ui::GetX11ErrorString(connection->display(), ret);
+ << ui::GetX11ErrorString(connection->display(),
+ static_cast<int>(ret));
}
connection->Flush();
- return ret == GrabSuccess;
+ return ret == x11::GrabStatus::Success;
}
void X11WholeScreenMoveLoop::GrabEscKey() {
auto* connection = x11::Connection::Get();
- auto esc_keycode = KeysymToKeycode(connection, kEscKeysym);
+ auto esc_keycode = connection->KeysymToKeycode(kEscKeysym);
for (auto mask : kModifiersMasks) {
connection->GrabKey({false, grab_input_window_, mask, esc_keycode,
x11::GrabMode::Async, x11::GrabMode::Async});
diff --git a/chromium/ui/base/x/x11_whole_screen_move_loop.h b/chromium/ui/base/x/x11_whole_screen_move_loop.h
index eae75d85f04..7717b3d3913 100644
--- a/chromium/ui/base/x/x11_whole_screen_move_loop.h
+++ b/chromium/ui/base/x/x11_whole_screen_move_loop.h
@@ -42,15 +42,15 @@ class COMPONENT_EXPORT(UI_BASE_X) X11WholeScreenMoveLoop
// X11MoveLoop:
bool RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) override;
- void UpdateCursor(::Cursor cursor) override;
+ scoped_refptr<ui::X11Cursor> old_cursor,
+ scoped_refptr<ui::X11Cursor> new_cursor) override;
+ void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) override;
void EndMoveLoop() override;
private:
// Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if
// successful.
- bool GrabPointer(::Cursor cursor);
+ bool GrabPointer(scoped_refptr<X11Cursor> cursor);
void GrabEscKey();
@@ -70,7 +70,7 @@ class COMPONENT_EXPORT(UI_BASE_X) X11WholeScreenMoveLoop
// Cursor in use prior to the move loop starting. Restored when the move loop
// quits.
- ::Cursor initial_cursor_;
+ scoped_refptr<X11Cursor> initial_cursor_ = nullptr;
// An invisible InputOnly window. Keyboard grab and sometimes mouse grab
// are set on this window.
diff --git a/chromium/ui/base/x/x11_window.cc b/chromium/ui/base/x/x11_window.cc
index bc5f4fefa7a..24dbb60257b 100644
--- a/chromium/ui/base/x/x11_window.cc
+++ b/chromium/ui/base/x/x11_window.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <vector>
+#include "base/bind.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
@@ -16,11 +17,11 @@
#include "net/base/network_interfaces.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/hit_test_x11.h"
+#include "ui/base/ui_base_features.h"
#include "ui/base/wm_role_names_linux.h"
#include "ui/base/x/x11_menu_registrar.h"
#include "ui/base/x/x11_pointer_grab.h"
#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_util_internal.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event.h"
@@ -216,7 +217,6 @@ void XWindow::Init(const Configuration& config) {
if (!activatable_ || config.override_redirect)
req.override_redirect = x11::Bool32(true);
-#if !defined(USE_X11)
// It seems like there is a difference how tests are instantiated in case of
// non-Ozone X11 and Ozone. See more details in
// EnableTestConfigForPlatformWindows. The reason why this must be here is
@@ -227,9 +227,8 @@ void XWindow::Init(const Configuration& config) {
// PlatformWindow, and non-Ozone X11 uses it, we have to add this workaround
// here. Otherwise, tests for non-Ozone X11 fail.
// TODO(msisov): figure out usage of this for non-Ozone X11.
- if (UseTestConfigForPlatformWindows())
+ if (features::IsUsingOzonePlatform() && UseTestConfigForPlatformWindows())
req.override_redirect = x11::Bool32(true);
-#endif
override_redirect_ = req.override_redirect.has_value();
@@ -258,11 +257,14 @@ void XWindow::Init(const Configuration& config) {
x11::VisualId visual_id = visual_id_;
uint8_t depth = 0;
+ x11::ColorMap colormap{};
XVisualManager* visual_manager = XVisualManager::GetInstance();
if (visual_id_ == x11::VisualId{} ||
- !visual_manager->GetVisualInfo(visual_id_, &depth, &visual_has_alpha_)) {
- visual_manager->ChooseVisualForWindow(
- enable_transparent_visuals, &visual_id, &depth, &visual_has_alpha_);
+ !visual_manager->GetVisualInfo(visual_id_, &depth, &colormap,
+ &visual_has_alpha_)) {
+ visual_manager->ChooseVisualForWindow(enable_transparent_visuals,
+ &visual_id, &depth, &colormap,
+ &visual_has_alpha_);
}
// x.org will BadMatch if we don't set a border when the depth isn't the
@@ -278,6 +280,7 @@ void XWindow::Init(const Configuration& config) {
req.depth = depth;
req.c_class = x11::WindowClass::InputOutput;
req.visual = visual_id;
+ req.colormap = colormap;
xwindow_ = connection_->GenerateId<x11::Window>();
req.wid = xwindow_;
connection_->CreateWindow(req);
@@ -446,10 +449,10 @@ void XWindow::Map(bool inactive) {
SetWmNormalHints(xwindow_, size_hints);
ignore_keyboard_input_ = inactive;
- uint32_t wm_user_time_ms =
- ignore_keyboard_input_ ? 0
+ auto wm_user_time_ms = ignore_keyboard_input_
+ ? x11::Time::CurrentTime
: X11EventSource::GetInstance()->GetTimestamp();
- if (inactive || wm_user_time_ms != 0) {
+ if (inactive || wm_user_time_ms != x11::Time::CurrentTime) {
SetProperty(xwindow_, gfx::GetAtom("_NET_WM_USER_TIME"),
x11::Atom::CARDINAL, wm_user_time_ms);
}
@@ -548,7 +551,7 @@ void XWindow::Activate() {
GuessWindowManager() != WM_WMII &&
WmSupportsHint(gfx::GetAtom("_NET_ACTIVE_WINDOW"));
- ::Time timestamp = X11EventSource::GetInstance()->GetTimestamp();
+ x11::Time timestamp = X11EventSource::GetInstance()->GetTimestamp();
// override_redirect windows ignore _NET_ACTIVE_WINDOW.
// https://crbug.com/940924
@@ -556,7 +559,7 @@ void XWindow::Activate() {
std::array<uint32_t, 5> data = {
// We're an app.
1,
- timestamp,
+ static_cast<uint32_t>(timestamp),
// TODO(thomasanderson): if another chrome window is active, specify
// that here. The EWMH spec claims this may make the WM more likely to
// service our _NET_ACTIVE_WINDOW request.
@@ -574,9 +577,9 @@ void XWindow::Activate() {
->SetInputFocus({x11::InputFocus::Parent, xwindow_,
static_cast<x11::Time>(timestamp)})
.IgnoreError();
- // At this point, we know we will receive focus, and some
- // webdriver tests depend on a window being IsActive() immediately
- // after an Activate(), so just set this state now.
+ // At this point, we know we will receive focus, and some webdriver tests
+ // depend on a window being IsActive() immediately after an Activate(), so
+ // just set this state now.
has_pointer_focus_ = false;
has_window_focus_ = true;
window_mapped_in_server_ = true;
@@ -603,6 +606,7 @@ bool XWindow::IsActive() const {
// stacking order in addition to changing the focus state.
return (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_;
}
+
void XWindow::SetSize(const gfx::Size& size_in_pixels) {
connection_->ConfigureWindow({.window = xwindow_,
.width = size_in_pixels.width(),
@@ -692,7 +696,8 @@ gfx::Rect XWindow::GetOuterBounds() const {
void XWindow::GrabPointer() {
// If the pointer is already in |xwindow_|, we will not get a crossing event
// with a mode of NotifyGrab, so we must record the grab state manually.
- has_pointer_grab_ |= !ui::GrabPointer(xwindow_, true, x11::None);
+ has_pointer_grab_ |=
+ (ui::GrabPointer(xwindow_, true, nullptr) == x11::GrabStatus::Success);
}
void XWindow::ReleasePointerGrab() {
@@ -732,9 +737,11 @@ void XWindow::StackXWindowAtTop() {
RaiseWindow(xwindow_);
}
-void XWindow::SetCursor(::Cursor cursor) {
+void XWindow::SetCursor(scoped_refptr<X11Cursor> cursor) {
last_cursor_ = cursor;
- DefineCursor(xwindow_, static_cast<x11::Cursor>(cursor));
+ on_cursor_loaded_.Reset(base::BindOnce(DefineCursor, xwindow_));
+ if (cursor)
+ cursor->OnCursorLoaded(on_cursor_loaded_.callback());
}
bool XWindow::SetTitle(base::string16 title) {
@@ -1072,12 +1079,15 @@ void XWindow::OnFocusEvent(bool focus_in,
}
bool XWindow::IsTargetedBy(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- auto target_window = static_cast<x11::Window>(
- xev.type == x11::GeGenericEvent::opcode
- ? static_cast<XIDeviceEvent*>(xev.xcookie.data)->event
- : xev.xany.window);
- return target_window == xwindow_;
+ return x11_event.window() == xwindow_;
+}
+
+bool XWindow::IsTransientWindowTargetedBy(const x11::Event& x11_event) const {
+ return x11_event.window() == transient_window_;
+}
+
+void XWindow::SetTransientWindow(x11::Window window) {
+ transient_window_ = window;
}
void XWindow::WmMoveResize(int hittest, const gfx::Point& location) const {
@@ -1088,12 +1098,6 @@ void XWindow::WmMoveResize(int hittest, const gfx::Point& location) const {
DoWMMoveResize(connection_, x_root_window_, xwindow_, location, direction);
}
-// In Ozone, there are no *Event constructors receiving XEvent* as input,
-// in this case PlatformEvent is expected. Furthermore,
-// X11EventSourceLibevent is used in that case, which already translates
-// Mouse/Key/Touch/Scroll events into Events so they should not be handled
-// by PlatformWindow, which is supposed to use XWindow in Ozone builds. So
-// handling these events is disabled for Ozone.
void XWindow::ProcessEvent(x11::Event* xev) {
// We can lose track of the window's position when the window is reparented.
// When the parent window is moved, we won't get an event, so the window's
@@ -1129,7 +1133,7 @@ void XWindow::ProcessEvent(x11::Event* xev) {
OnConfigureEvent(*configure);
} else if (auto* crossing = xev->As<x11::Input::CrossingEvent>()) {
TouchFactory* factory = TouchFactory::GetInstance();
- if (factory->ShouldProcessXI2Event(&xev->xlib_event())) {
+ if (factory->ShouldProcessCrossingEvent(*crossing)) {
auto mode = XI2ModeToXMode(crossing->mode);
auto detail = XI2DetailToXDetail(crossing->detail);
switch (crossing->opcode) {
@@ -1165,16 +1169,9 @@ void XWindow::ProcessEvent(x11::Event* xev) {
} else if (protocol == gfx::GetAtom("_NET_WM_PING")) {
x11::ClientMessageEvent reply_event = *client;
reply_event.window = x_root_window_;
-
- auto event_bytes = x11::Write(reply_event);
- DCHECK_EQ(event_bytes.size(), 32ul);
-
- x11::SendEventRequest request{false, x_root_window_,
- x11::EventMask::SubstructureNotify |
- x11::EventMask::SubstructureRedirect};
- std::copy(event_bytes.begin(), event_bytes.end(),
- request.event.begin());
- connection_->SendEvent(request);
+ SendEvent(reply_event, x_root_window_,
+ x11::EventMask::SubstructureNotify |
+ x11::EventMask::SubstructureRedirect);
} else if (protocol == gfx::GetAtom("_NET_WM_SYNC_REQUEST")) {
pending_counter_value_ =
client->data.data32[2] +
@@ -1184,29 +1181,14 @@ void XWindow::ProcessEvent(x11::Event* xev) {
} else {
OnXWindowDragDropEvent(xev);
}
- } else if (auto* mapping = xev->As<x11::MappingNotifyEvent>()) {
- switch (mapping->request) {
- case x11::Mapping::Modifier:
- case x11::Mapping::Keyboard:
- XRefreshKeyboardMapping(&xev->xlib_event().xmapping);
- break;
- case x11::Mapping::Pointer:
- DeviceDataManagerX11::GetInstance()->UpdateButtonMap();
- break;
- default:
- NOTIMPLEMENTED() << " Unknown request: "
- << static_cast<int>(mapping->request);
- break;
- }
} else if (auto* property = xev->As<x11::PropertyNotifyEvent>()) {
x11::Atom changed_atom = property->atom;
- if (changed_atom == gfx::GetAtom("_NET_WM_STATE")) {
+ if (changed_atom == gfx::GetAtom("_NET_WM_STATE"))
OnWMStateUpdated();
- } else if (changed_atom == gfx::GetAtom("_NET_FRAME_EXTENTS")) {
+ else if (changed_atom == gfx::GetAtom("_NET_FRAME_EXTENTS"))
OnFrameExtentsUpdated();
- } else if (changed_atom == gfx::GetAtom("_NET_WM_DESKTOP")) {
+ else if (changed_atom == gfx::GetAtom("_NET_WM_DESKTOP"))
OnWorkspaceUpdated();
- }
} else if (auto* selection = xev->As<x11::SelectionNotifyEvent>()) {
OnXWindowSelectionEvent(xev);
}
@@ -1353,14 +1335,13 @@ void XWindow::NotifySwapAfterResize() {
}
}
-// Removes |delayed_resize_task_| from the task queue (if it's in
-// the queue) and adds it back at the end of the queue.
+// Removes |delayed_resize_task_| from the task queue (if it's in the queue) and
+// adds it back at the end of the queue.
void XWindow::DispatchResize() {
if (update_counter_ == x11::Sync::Counter{} ||
configure_counter_value_ == 0) {
- // WM doesn't support _NET_WM_SYNC_REQUEST.
- // Or we are too slow, so _NET_WM_SYNC_REQUEST is disabled by the
- // compositor.
+ // WM doesn't support _NET_WM_SYNC_REQUEST. Or we are too slow, so
+ // _NET_WM_SYNC_REQUEST is disabled by the compositor.
delayed_resize_task_.Reset(base::BindOnce(
&XWindow::DelayedResize, base::Unretained(this), bounds_in_pixels_));
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -1457,7 +1438,7 @@ void XWindow::SetOverrideRedirect(bool override_redirect) {
if (remap) {
Map();
if (has_pointer_grab_)
- ChangeActivePointerGrabCursor(x11::None);
+ ChangeActivePointerGrabCursor(nullptr);
}
}
@@ -1587,8 +1568,8 @@ bool XWindow::InitializeAsStatusIcon() {
auto future = SendClientMessage(
manager, manager, gfx::GetAtom("_NET_SYSTEM_TRAY_OPCODE"),
- {X11EventSource::GetInstance()->GetTimestamp(), kSystemTrayRequestDock,
- static_cast<uint32_t>(xwindow_), 0, 0},
+ {static_cast<uint32_t>(X11EventSource::GetInstance()->GetTimestamp()),
+ kSystemTrayRequestDock, static_cast<uint32_t>(xwindow_), 0, 0},
x11::EventMask::NoEvent);
return !future.Sync().error;
}
diff --git a/chromium/ui/base/x/x11_window.h b/chromium/ui/base/x/x11_window.h
index c5b0d7227a5..af282d4190d 100644
--- a/chromium/ui/base/x/x11_window.h
+++ b/chromium/ui/base/x/x11_window.h
@@ -16,6 +16,7 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/strings/string16.h"
+#include "ui/base/x/x11_cursor.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
@@ -38,6 +39,7 @@ namespace ui {
class Event;
class XScopedEventSelector;
+class X11Cursor;
////////////////////////////////////////////////////////////////////////////////
// XWindow class
@@ -115,6 +117,8 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
void StackXWindowAbove(x11::Window window);
void StackXWindowAtTop();
bool IsTargetedBy(const x11::Event& xev) const;
+ bool IsTransientWindowTargetedBy(const x11::Event& x11_event) const;
+ void SetTransientWindow(x11::Window window);
void WmMoveResize(int hittest, const gfx::Point& location) const;
void ProcessEvent(x11::Event* xev);
@@ -126,7 +130,7 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
bool IsFullscreen() const;
gfx::Rect GetOuterBounds() const;
- void SetCursor(::Cursor cursor);
+ void SetCursor(scoped_refptr<X11Cursor> cursor);
bool SetTitle(base::string16 title);
void SetXWindowOpacity(float opacity);
void SetXWindowAspectRatio(const gfx::SizeF& aspect_ratio);
@@ -176,7 +180,7 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
x11::Sync::Counter extended_update_counter() const {
return extended_update_counter_;
}
- ::Cursor last_cursor() const { return last_cursor_; }
+ scoped_refptr<X11Cursor> last_cursor() const { return last_cursor_; }
protected:
// Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active.
@@ -262,6 +266,9 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
x11::Window xwindow_ = x11::Window::None;
x11::Window x_root_window_ = x11::Window::None;
+ // Any native, modal dialog hanging from this window.
+ x11::Window transient_window_ = x11::Window::None;
+
// Events selected on |xwindow_|.
std::unique_ptr<ui::XScopedEventSelector> xwindow_events_;
@@ -387,7 +394,9 @@ class COMPONENT_EXPORT(UI_BASE_X) XWindow {
bool has_pointer_barriers_ = false;
std::array<x11::XFixes::Barrier, 4> pointer_barriers_;
- ::Cursor last_cursor_ = x11::None;
+ scoped_refptr<X11Cursor> last_cursor_;
+
+ base::CancelableOnceCallback<void(x11::Cursor)> on_cursor_loaded_;
};
} // namespace ui
diff --git a/chromium/ui/base/x/x11_workspace_handler.cc b/chromium/ui/base/x/x11_workspace_handler.cc
index cb48d38fc4a..adf92e51adb 100644
--- a/chromium/ui/base/x/x11_workspace_handler.cc
+++ b/chromium/ui/base/x/x11_workspace_handler.cc
@@ -50,37 +50,27 @@ std::string X11WorkspaceHandler::GetCurrentWorkspace() {
return workspace_;
}
-bool X11WorkspaceHandler::DispatchXEvent(x11::Event* x11_event) {
- XEvent* event = &x11_event->xlib_event();
- if (event->type != PropertyNotify ||
- event->xproperty.window != static_cast<uint32_t>(x_root_window_)) {
- return false;
- }
- switch (event->type) {
- case x11::PropertyNotifyEvent::opcode: {
- if (event->xproperty.atom ==
- static_cast<uint32_t>(gfx::GetAtom("_NET_CURRENT_DESKTOP"))) {
- GetWorkspace().OnResponse(
- base::BindOnce(&X11WorkspaceHandler::OnWorkspaceResponse,
- weak_factory_.GetWeakPtr()));
- }
- break;
- }
- default:
- NOTREACHED();
+bool X11WorkspaceHandler::DispatchXEvent(x11::Event* xev) {
+ auto* prop = xev->As<x11::PropertyNotifyEvent>();
+ if (prop && prop->window == x_root_window_ &&
+ prop->atom == gfx::GetAtom("_NET_CURRENT_DESKTOP")) {
+ GetWorkspace().OnResponse(base::BindOnce(
+ &X11WorkspaceHandler::OnWorkspaceResponse, weak_factory_.GetWeakPtr()));
+ return true;
}
+
return false;
}
void X11WorkspaceHandler::OnWorkspaceResponse(
x11::GetPropertyResponse response) {
- if (!response || response->format != 32 || response->value.size() < 4)
+ if (!response || response->format != 32 || response->value->size() < 4)
return;
DCHECK_EQ(response->bytes_after, 0U);
DCHECK_EQ(response->type, static_cast<x11::Atom>(gfx::GetAtom("CARDINAL")));
uint32_t workspace;
- memcpy(&workspace, response->value.data(), 4);
+ memcpy(&workspace, response->value->data(), 4);
workspace_ = base::NumberToString(workspace);
delegate_->OnCurrentWorkspaceChanged(workspace_);
}
diff --git a/chromium/ui/webui/resources/css/BUILD.gn b/chromium/ui/chromeos/colors/BUILD.gn
index 49dfa4a1a96..9d523769d6e 100644
--- a/chromium/ui/webui/resources/css/BUILD.gn
+++ b/chromium/ui/chromeos/colors/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# 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.
diff --git a/chromium/ui/color/BUILD.gn b/chromium/ui/color/BUILD.gn
index a39c290e6b4..898dd69d377 100644
--- a/chromium/ui/color/BUILD.gn
+++ b/chromium/ui/color/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/buildflag_header.gni")
-import("//build/config/jumbo.gni")
import("//testing/test.gni")
import("//ui/base/ui_features.gni")
@@ -22,7 +21,7 @@ source_set("color_headers") {
]
}
-jumbo_component("color") {
+component("color") {
sources = [
"color_mixer.cc",
"color_provider.cc",
@@ -65,7 +64,7 @@ test("color_unittests") {
]
}
-jumbo_component("mixers") {
+component("mixers") {
sources = [
"color_mixers.h",
"core_default_color_mixer.cc",
@@ -87,10 +86,10 @@ jumbo_component("mixers") {
sources += [ "cros/native_color_mixers.cc" ]
} else if (is_fuchsia) {
sources += [ "fuchsia/native_color_mixers.cc" ]
- } else if (is_linux) {
+ } else if (is_linux || is_chromeos) {
sources += [ "linux/native_color_mixers.cc" ]
} else if (is_mac) {
- libs = [ "AppKit.framework" ]
+ frameworks = [ "AppKit.framework" ]
sources += [
"mac/native_color_mixers.mm",
"mac/scoped_current_nsappearance.h",
diff --git a/chromium/ui/color/color_id.h b/chromium/ui/color/color_id.h
index ae5f30f2ebe..f71b88074c2 100644
--- a/chromium/ui/color/color_id.h
+++ b/chromium/ui/color/color_id.h
@@ -31,6 +31,8 @@
NativeTheme::kColorId_BubbleFooterBackground) \
E(kColorButtonBackground, NativeTheme::kColorId_ButtonColor) \
E(kColorButtonBorder, NativeTheme::kColorId_ButtonBorderColor) \
+ E(kColorButtonDisabledBorder, \
+ NativeTheme::kColorId_DisabledButtonBorderColor) \
E(kColorButtonDisabledForeground, NativeTheme::kColorId_ButtonDisabledColor) \
E(kColorButtonForeground, NativeTheme::kColorId_ButtonEnabledColor) \
/* TODO(https://crbug.com/1003612): Map this to old color id. */ \
@@ -85,6 +87,7 @@
E(kColorMenuSeparator, NativeTheme::kColorId_MenuSeparatorColor) \
E(kColorTabContentSeparator, NativeTheme::kColorId_TabBottomBorder) \
E(kColorTabForeground, NativeTheme::kColorId_TabTitleColorInactive) \
+ E(kColorTabSelectedBorder, NativeTheme::kColorId_TabSelectedBorderColor) \
E(kColorTabSelectedForeground, \
NativeTheme::kColorId_TabTitleColorActive) \
E(kColorTableBackground, NativeTheme::kColorId_TableBackground) \
@@ -165,7 +168,7 @@
E(kColorNativeWindowText, COLOR_WINDOWTEXT)
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MACOSX_COLOR_IDS \
E(kColorTableBackgroundAlternate, \
NativeTheme::kColorId_TableBackgroundAlternate)
@@ -175,7 +178,7 @@
#define COLOR_IDS \
CROSS_PLATFORM_COLOR_IDS \
WIN_COLOR_IDS
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#define COLOR_IDS \
CROSS_PLATFORM_COLOR_IDS \
MACOSX_COLOR_IDS
diff --git a/chromium/ui/color/ui_color_mixer.cc b/chromium/ui/color/ui_color_mixer.cc
index 7949aa26f50..427c77e141b 100644
--- a/chromium/ui/color/ui_color_mixer.cc
+++ b/chromium/ui/color/ui_color_mixer.cc
@@ -14,17 +14,19 @@ namespace ui {
void AddUiColorMixer(ColorProvider* provider) {
ColorMixer& mixer = provider->AddMixer();
+ const auto button_disabled_background =
+ BlendForMinContrastWithSelf(kColorButtonBackground, 1.2f);
mixer[kColorBubbleBackground] = {kColorPrimaryBackground};
mixer[kColorBubbleFooterBackground] = {kColorSubtleEmphasisBackground};
mixer[kColorButtonBackground] = {kColorPrimaryBackground};
mixer[kColorButtonBorder] = {kColorBorderAndSeparatorForeground};
+ mixer[kColorButtonDisabledBorder] = button_disabled_background;
mixer[kColorButtonDisabledForeground] = {kColorDisabledForeground};
mixer[kColorButtonForeground] = {kColorAccent};
mixer[kColorButtonPressedBackground] = {kColorButtonBackground};
mixer[kColorButtonProminentBackground] = {kColorAccent};
- mixer[kColorButtonProminentDisabledBackground] =
- BlendForMinContrastWithSelf(kColorButtonBackground, 1.2f);
+ mixer[kColorButtonProminentDisabledBackground] = button_disabled_background;
mixer[kColorButtonProminentFocusedBackground] =
BlendForMinContrastWithSelf(kColorButtonProminentBackground, 1.3f);
mixer[kColorButtonProminentForeground] =
@@ -58,6 +60,7 @@ void AddUiColorMixer(ColorProvider* provider) {
mixer[kColorMenuSeparator] = {kColorBorderAndSeparatorForeground};
mixer[kColorTabContentSeparator] = {kColorBorderAndSeparatorForeground};
mixer[kColorTabForeground] = {kColorSecondaryForeground};
+ mixer[kColorTabSelectedBorder] = {kColorAccent};
mixer[kColorTabSelectedForeground] = {kColorAccent};
mixer[kColorTableBackground] = {kColorPrimaryBackground};
mixer[kColorTableForeground] = {kColorPrimaryForeground};
diff --git a/chromium/ui/compositor/BUILD.gn b/chromium/ui/compositor/BUILD.gn
index 19ba19069b3..219d6a8f0a9 100644
--- a/chromium/ui/compositor/BUILD.gn
+++ b/chromium/ui/compositor/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -12,7 +11,7 @@ import("//testing/test.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("compositor") {
+component("compositor") {
sources = [
"animation_metrics_recorder.cc",
"animation_metrics_recorder.h",
@@ -125,7 +124,7 @@ jumbo_component("compositor") {
}
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"test/direct_layer_tree_frame_sink.cc",
@@ -209,18 +208,34 @@ jumbo_static_library("test_support") {
}
if (use_ozone) {
- sources += [ "test/test_compositor_host_ozone.cc" ]
+ sources += [
+ "test/test_compositor_host_ozone.cc",
+ "test/test_compositor_host_ozone.h",
+ ]
deps += [
"//ui/ozone",
"//ui/platform_window",
]
- } else if (use_x11) {
- sources += [ "test/test_compositor_host_x11.cc" ]
+ }
+
+ if (use_x11) {
+ sources += [
+ "test/test_compositor_host_x11.cc",
+ "test/test_compositor_host_x11.h",
+ ]
+ }
+
+ if (is_linux) {
+ sources += [ "test/test_compositor_host_linux.cc" ]
}
}
test("compositor_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [
"animation_throughput_reporter_unittest.cc",
"callback_layer_animation_observer_unittest.cc",
@@ -258,7 +273,7 @@ test("compositor_unittests") {
"//ui/resources",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//third_party/mesa_headers" ]
}
}
diff --git a/chromium/ui/compositor/animation_metrics_reporter.h b/chromium/ui/compositor/animation_metrics_reporter.h
index 1b12fb55acc..d06fe232e70 100644
--- a/chromium/ui/compositor/animation_metrics_reporter.h
+++ b/chromium/ui/compositor/animation_metrics_reporter.h
@@ -5,7 +5,6 @@
#ifndef UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
#define UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
-#include "base/metrics/histogram_macros.h"
#include "ui/compositor/compositor_export.h"
namespace ui {
@@ -23,25 +22,6 @@ class COMPOSITOR_EXPORT AnimationMetricsReporter {
virtual void Report(int value) = 0;
};
-// A subclass of AnimationMetricsReporter that writes into a percentage
-// histogram when Report() is called.
-template <const char* histogram_name>
-class COMPOSITOR_EXPORT HistogramPercentageMetricsReporter
- : public AnimationMetricsReporter {
- public:
- HistogramPercentageMetricsReporter() = default;
- HistogramPercentageMetricsReporter(
- const HistogramPercentageMetricsReporter&) = delete;
- HistogramPercentageMetricsReporter& operator=(
- const HistogramPercentageMetricsReporter&) = delete;
- ~HistogramPercentageMetricsReporter() override = default;
-
- // AnimationMetricsReporter:
- void Report(int value) override {
- UMA_HISTOGRAM_PERCENTAGE(histogram_name, value);
- }
-};
-
} // namespace ui
#endif // UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
diff --git a/chromium/ui/compositor/animation_throughput_reporter.cc b/chromium/ui/compositor/animation_throughput_reporter.cc
index bcb963bb367..5704e26554d 100644
--- a/chromium/ui/compositor/animation_throughput_reporter.cc
+++ b/chromium/ui/compositor/animation_throughput_reporter.cc
@@ -17,6 +17,7 @@
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_delegate.h"
+#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/throughput_tracker.h"
@@ -47,7 +48,7 @@ class AnimationThroughputReporter::AnimationTracker
void OnAnimatorAttachedToTimeline() override { MaybeStartTracking(); }
void OnAnimatorDetachedFromTimeline() override {
// Gives up tracking when detached from the timeline.
- should_start_tracking_ = false;
+ first_animation_group_id_.reset();
if (throughput_tracker_)
throughput_tracker_.reset();
@@ -59,18 +60,32 @@ class AnimationThroughputReporter::AnimationTracker
void OnLayerAnimationStarted(LayerAnimationSequence* sequence) override {
CallbackLayerAnimationObserver::OnLayerAnimationStarted(sequence);
- should_start_tracking_ = true;
- MaybeStartTracking();
+ if (!first_animation_group_id_.has_value()) {
+ first_animation_group_id_ = sequence->animation_group_id();
+ MaybeStartTracking();
+ }
// Make sure SetActive() is called so that OnAnimationEnded callback will be
// invoked when all attached layer animation sequences finish.
if (!active())
SetActive();
}
+ void OnLayerAnimationAborted(LayerAnimationSequence* sequence) override {
+ // Check whether the aborted animation sequence is among the relevant ones
+ // (started while the tracker is alive). This is done by checking the
+ // animation_group_id() and assuming the id is monotonic increasing.
+ if (first_animation_group_id_.has_value() &&
+ first_animation_group_id_.value() <= sequence->animation_group_id()) {
+ started_animations_aborted_ = true;
+ }
+
+ // Note the following call could delete |this|.
+ CallbackLayerAnimationObserver::OnLayerAnimationAborted(sequence);
+ }
void MaybeStartTracking() {
// No tracking if no layer animation sequence is started.
- if (!should_start_tracking_)
+ if (!first_animation_group_id_.has_value())
return;
// No tracking if |animator_| is not attached to a timeline. Layer animation
@@ -88,17 +103,16 @@ class AnimationThroughputReporter::AnimationTracker
// Invoked when all animation sequences finish.
bool OnAnimationEnded(const CallbackLayerAnimationObserver& self) {
- // |tracking_started_| could reset when detached from animation timeline.
- // E.g. underlying Layer is moved from one Compositor to another. No report
- // for such case.
+ // |throughput_tracker| could reset when detached from animation timeline.
if (throughput_tracker_) {
- if (self.aborted_count())
+ if (started_animations_aborted_)
throughput_tracker_->Cancel();
else
throughput_tracker_->Stop();
}
- should_start_tracking_ = false;
+ first_animation_group_id_.reset();
+ started_animations_aborted_ = false;
return should_delete_;
}
@@ -109,8 +123,8 @@ class AnimationThroughputReporter::AnimationTracker
base::Optional<ThroughputTracker> throughput_tracker_;
- // Whether |throughput_tracker_| should be started.
- bool should_start_tracking_ = false;
+ base::Optional<int> first_animation_group_id_;
+ bool started_animations_aborted_ = false;
AnimationThroughputReporter::ReportCallback report_callback_;
};
diff --git a/chromium/ui/compositor/animation_throughput_reporter_unittest.cc b/chromium/ui/compositor/animation_throughput_reporter_unittest.cc
index c6a14ba2f45..446a57db6bf 100644
--- a/chromium/ui/compositor/animation_throughput_reporter_unittest.cc
+++ b/chromium/ui/compositor/animation_throughput_reporter_unittest.cc
@@ -268,4 +268,47 @@ TEST_F(AnimationThroughputReporterTest, EndDetachedNoReportNoLeak) {
// AnimationTracker in |reporter| should not leak in asan.
}
+// Tests animation throughput are reported if there was a previous animation
+// preempted under IMMEDIATELY_ANIMATE_TO_NEW_TARGET strategy.
+TEST_F(AnimationThroughputReporterTest, ReportForAnimateToNewTarget) {
+ auto layer = std::make_unique<Layer>();
+ layer->SetOpacity(0.f);
+ layer->SetBounds(gfx::Rect(0, 0, 1, 2));
+ root_layer()->Add(layer.get());
+
+ LayerAnimator* animator = layer->GetAnimator();
+
+ // Schedule an animation that will be preempted. No report should happen.
+ {
+ AnimationThroughputReporter reporter(
+ animator, base::BindLambdaForTesting(
+ [&](cc::FrameSequenceMetrics::ThroughputData) {
+ ADD_FAILURE() << "No report for aborted animations.";
+ }));
+
+ ScopedLayerAnimationSettings settings(animator);
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+ layer->SetOpacity(0.5f);
+ layer->SetBounds(gfx::Rect(0, 0, 3, 4));
+ }
+
+ // Animate to new target. Report should happen.
+ base::RunLoop run_loop;
+ {
+ AnimationThroughputReporter reporter(
+ animator, base::BindLambdaForTesting(
+ [&](cc::FrameSequenceMetrics::ThroughputData) {
+ run_loop.Quit();
+ }));
+
+ ScopedLayerAnimationSettings settings(animator);
+ settings.SetPreemptionStrategy(
+ LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+ layer->SetOpacity(1.0f);
+ layer->SetBounds(gfx::Rect(0, 0, 5, 6));
+ }
+ run_loop.Run();
+}
+
} // namespace ui
diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc
index 65d2f3ec773..5b6f5f2f04e 100644
--- a/chromium/ui/compositor/compositor.cc
+++ b/chromium/ui/compositor/compositor.cc
@@ -172,7 +172,7 @@ Compositor::Compositor(const viz::FrameSinkId& frame_sink_id,
settings.use_rgba_4444 =
command_line->HasSwitch(switches::kUIEnableRGBA4444Textures);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Using CoreAnimation to composite requires using GpuMemoryBuffers, which
// require zero copy.
settings.resource_settings.use_gpu_memory_buffer_resources =
@@ -451,7 +451,8 @@ void Compositor::SetDisplayColorSpaces(
return;
display_color_spaces_ = display_color_spaces;
- host_->SetRasterColorSpace(display_color_spaces_.GetRasterColorSpace());
+ host_->SetDisplayColorSpaces(display_color_spaces_);
+
// Always force the ui::Compositor to re-draw all layers, because damage
// tracking bugs result in black flashes.
// https://crbug.com/804430
@@ -698,8 +699,7 @@ void Compositor::DidSubmitCompositorFrame() {
}
void Compositor::FrameIntervalUpdated(base::TimeDelta interval) {
- refresh_rate_ =
- base::Time::kMicrosecondsPerSecond / interval.InMicrosecondsF();
+ refresh_rate_ = interval.ToHz();
}
void Compositor::OnFirstSurfaceActivation(
@@ -769,4 +769,10 @@ void Compositor::ReportThroughputForTracker(
throughput_tracker_map_.erase(it);
}
+void Compositor::SetDelegatedInkPointRenderer(
+ mojo::PendingReceiver<viz::mojom::DelegatedInkPointRenderer> receiver) {
+ if (display_private_)
+ display_private_->SetDelegatedInkPointRenderer(std::move(receiver));
+}
+
} // namespace ui
diff --git a/chromium/ui/compositor/compositor.h b/chromium/ui/compositor/compositor.h
index 438dd212d88..13665946e08 100644
--- a/chromium/ui/compositor/compositor.h
+++ b/chromium/ui/compositor/compositor.h
@@ -72,6 +72,7 @@ namespace viz {
namespace mojom {
class DisplayPrivate;
class ExternalBeginFrameController;
+class DelegatedInkPointRenderer;
} // namespace mojom
class ContextProvider;
class HostFrameSinkManager;
@@ -404,6 +405,9 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient,
return scroll_input_handler_.get();
}
+ virtual void SetDelegatedInkPointRenderer(
+ mojo::PendingReceiver<viz::mojom::DelegatedInkPointRenderer> receiver);
+
private:
friend class base::RefCounted<Compositor>;
@@ -416,6 +420,13 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient,
ui::ContextFactory* context_factory_;
+ // |display_private_| can be null for:
+ // 1. Tests that don't set |display_private_|.
+ // 2. Intermittently on creation or if there is some kind of error (GPU crash,
+ // GL context loss, etc.) that triggers reinitializing message pipes to the
+ // GPU process RootCompositorFrameSinkImpl.
+ // Therefore, it should always be null checked for safety before use.
+ //
// These pointers are owned by |context_factory_|, and must be reset before
// calling RemoveCompositor();
viz::mojom::DisplayPrivate* display_private_ = nullptr;
diff --git a/chromium/ui/compositor/compositor_observer.h b/chromium/ui/compositor/compositor_observer.h
index 501aa3dbfbb..a5d845d5870 100644
--- a/chromium/ui/compositor/compositor_observer.h
+++ b/chromium/ui/compositor/compositor_observer.h
@@ -46,7 +46,7 @@ class COMPOSITOR_EXPORT CompositorObserver {
// Called when a swap with new size is completed.
virtual void OnCompositingCompleteSwapWithNewSize(ui::Compositor* compositor,
const gfx::Size& size) {}
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
// Called at the top of the compositor's destructor, to give observers a
// chance to remove themselves.
diff --git a/chromium/ui/compositor/compositor_switches.cc b/chromium/ui/compositor/compositor_switches.cc
index dfedaea2cf6..7cd81cfafc9 100644
--- a/chromium/ui/compositor/compositor_switches.cc
+++ b/chromium/ui/compositor/compositor_switches.cc
@@ -50,7 +50,7 @@ bool IsUIZeroCopyEnabled() {
// Match the behavior of IsZeroCopyUploadEnabled() in content/browser/gpu.
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return !command_line.HasSwitch(switches::kUIDisableZeroCopy);
#else
return command_line.HasSwitch(switches::kUIEnableZeroCopy);
diff --git a/chromium/ui/compositor/float_animation_curve_adapter.cc b/chromium/ui/compositor/float_animation_curve_adapter.cc
index df1f96ee6bf..1ae6bd14c93 100644
--- a/chromium/ui/compositor/float_animation_curve_adapter.cc
+++ b/chromium/ui/compositor/float_animation_curve_adapter.cc
@@ -5,7 +5,6 @@
#include "ui/compositor/float_animation_curve_adapter.h"
#include "base/memory/ptr_util.h"
-#include "cc/base/time_util.h"
namespace ui {
@@ -34,10 +33,9 @@ float FloatAnimationCurveAdapter::GetValue(base::TimeDelta t) const {
return target_value_;
if (t <= base::TimeDelta())
return initial_value_;
- double progress = cc::TimeUtil::Divide(t, duration_);
+
return gfx::Tween::FloatValueBetween(
- gfx::Tween::CalculateValue(tween_type_, progress),
- initial_value_,
+ gfx::Tween::CalculateValue(tween_type_, t / duration_), initial_value_,
target_value_);
}
diff --git a/chromium/ui/compositor/layer.cc b/chromium/ui/compositor/layer.cc
index 27efca014fd..12f75f828e6 100644
--- a/chromium/ui/compositor/layer.cc
+++ b/chromium/ui/compositor/layer.cc
@@ -5,6 +5,7 @@
#include "ui/compositor/layer.h"
#include <algorithm>
+#include <cmath>
#include <memory>
#include <utility>
@@ -59,7 +60,7 @@ void CheckSnapped(float snapped_position) {
// artifacts as well as large enough to not cause false crashes when an
// uncommon device scale factor is applied.
const float kEplison = 0.003f;
- float diff = std::abs(snapped_position - gfx::ToRoundedInt(snapped_position));
+ float diff = std::abs(snapped_position - std::round(snapped_position));
DCHECK_LT(diff, kEplison);
}
#endif
diff --git a/chromium/ui/compositor/layer.h b/chromium/ui/compositor/layer.h
index 8434ac53c33..b3f6b8bc74f 100644
--- a/chromium/ui/compositor/layer.h
+++ b/chromium/ui/compositor/layer.h
@@ -509,6 +509,10 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate,
bool ContainsMirrorForTest(Layer* mirror) const;
+ void SetCompositorForTesting(Compositor* compositor) {
+ compositor_ = compositor;
+ }
+
private:
friend class LayerOwner;
class LayerMirror;
diff --git a/chromium/ui/compositor/layer_animation_element.cc b/chromium/ui/compositor/layer_animation_element.cc
index 9da426ae66a..2d60ac1dd44 100644
--- a/chromium/ui/compositor/layer_animation_element.cc
+++ b/chromium/ui/compositor/layer_animation_element.cc
@@ -628,7 +628,7 @@ bool LayerAnimationElement::Progress(base::TimeTicks now,
base::TimeDelta elapsed = now - effective_start_time_;
if ((duration_ > base::TimeDelta()) && (elapsed < duration_))
- t = elapsed.InMillisecondsF() / duration_.InMillisecondsF();
+ t = elapsed / duration_;
base::WeakPtr<LayerAnimationElement> alive(weak_ptr_factory_.GetWeakPtr());
need_draw = OnProgress(gfx::Tween::CalculateValue(tween_type_, t), delegate);
if (!alive)
diff --git a/chromium/ui/compositor/layer_animator_unittest.cc b/chromium/ui/compositor/layer_animator_unittest.cc
index bbb9f4c5c37..1aa26315d36 100644
--- a/chromium/ui/compositor/layer_animator_unittest.cc
+++ b/chromium/ui/compositor/layer_animator_unittest.cc
@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_mock_clock_override.h"
@@ -1209,7 +1210,7 @@ TEST(LayerAnimatorTest, StartTogetherSetsLastStepTime) {
// should be enormous. Arbitrarily choosing 1 minute as the threshold,
// though a much smaller value would probably have sufficed.
delta = base::TimeTicks::Now() - animator->last_step_time();
- EXPECT_GT(60.0, delta.InSecondsF());
+ EXPECT_LT(delta, base::TimeDelta::FromMinutes(1));
}
//-------------------------------------------------------
@@ -3502,17 +3503,15 @@ TEST(LayerAnimatorTest,
Layer root;
compositor->SetRootLayer(&root);
- constexpr base::TimeDelta kAnimationDuration =
- base::TimeDelta::FromMilliseconds(50);
+ constexpr auto kAnimationDuration = base::TimeDelta::FromMilliseconds(50);
// Draw enough frames so that missing the start frame number would cause the
// reporter to always report 100% smoothness. 4 times of the expected
// animation frames because somehow the refresh rate changes from 60fps to
// 200fps when reporting.
- const float frame_interval =
- base::Time::kMillisecondsPerSecond / compositor->refresh_rate();
const int kStartFrameNumber =
- static_cast<int>(kAnimationDuration.InMillisecondsF() / frame_interval) *
+ base::ClampFloor(kAnimationDuration.InSecondsF() *
+ compositor->refresh_rate()) *
4;
while (compositor->activated_frame_count() < kStartFrameNumber) {
compositor->ScheduleFullRedraw();
diff --git a/chromium/ui/compositor/layer_unittest.cc b/chromium/ui/compositor/layer_unittest.cc
index 77945544e9f..57c280b75da 100644
--- a/chromium/ui/compositor/layer_unittest.cc
+++ b/chromium/ui/compositor/layer_unittest.cc
@@ -1709,7 +1709,7 @@ TEST_P(LayerWithRealCompositorTest, SetRootLayer) {
// TODO(vollick): could be reorganized into compositor_unittest.cc
// Flaky on Windows. See https://crbug.com/784563.
// Flaky on Linux tsan. See https://crbug.com/834026.
-#if defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
#define MAYBE_CompositorObservers DISABLED_CompositorObservers
#else
#define MAYBE_CompositorObservers CompositorObservers
diff --git a/chromium/ui/compositor/test/test_compositor_host_ozone.cc b/chromium/ui/compositor/test/test_compositor_host_ozone.cc
index d60cbe986af..bb170d382f3 100644
--- a/chromium/ui/compositor/test/test_compositor_host_ozone.cc
+++ b/chromium/ui/compositor/test/test_compositor_host_ozone.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/compositor/test/test_compositor_host.h"
+#include "ui/compositor/test/test_compositor_host_ozone.h"
#include <memory>
@@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/geometry/rect.h"
@@ -24,11 +25,10 @@
namespace ui {
-namespace {
-
// Stub implementation of PlatformWindowDelegate that stores the
// AcceleratedWidget.
-class StubPlatformWindowDelegate : public PlatformWindowDelegate {
+class TestCompositorHostOzone::StubPlatformWindowDelegate
+ : public PlatformWindowDelegate {
public:
StubPlatformWindowDelegate() {}
~StubPlatformWindowDelegate() override {}
@@ -46,6 +46,7 @@ class StubPlatformWindowDelegate : public PlatformWindowDelegate {
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {
widget_ = widget;
}
+ void OnWillDestroyAcceleratedWidget() override {}
void OnAcceleratedWidgetDestroyed() override {
widget_ = gfx::kNullAcceleratedWidget;
}
@@ -58,26 +59,6 @@ class StubPlatformWindowDelegate : public PlatformWindowDelegate {
DISALLOW_COPY_AND_ASSIGN(StubPlatformWindowDelegate);
};
-class TestCompositorHostOzone : public TestCompositorHost {
- public:
- TestCompositorHostOzone(const gfx::Rect& bounds,
- ui::ContextFactory* context_factory);
- ~TestCompositorHostOzone() override;
-
- private:
- // Overridden from TestCompositorHost:
- void Show() override;
- ui::Compositor* GetCompositor() override;
-
- gfx::Rect bounds_;
- ui::Compositor compositor_;
- std::unique_ptr<PlatformWindow> window_;
- StubPlatformWindowDelegate window_delegate_;
- viz::ParentLocalSurfaceIdAllocator allocator_;
-
- DISALLOW_COPY_AND_ASSIGN(TestCompositorHostOzone);
-};
-
TestCompositorHostOzone::TestCompositorHostOzone(
const gfx::Rect& bounds,
ui::ContextFactory* context_factory)
@@ -85,7 +66,8 @@ TestCompositorHostOzone::TestCompositorHostOzone(
compositor_(context_factory->AllocateFrameSinkId(),
context_factory,
base::ThreadTaskRunnerHandle::Get(),
- false /* enable_pixel_canvas */) {}
+ false /* enable_pixel_canvas */),
+ window_delegate_(std::make_unique<StubPlatformWindowDelegate>()) {}
TestCompositorHostOzone::~TestCompositorHostOzone() {
// |window_| should be destroyed earlier than |window_delegate_| as it refers
@@ -98,12 +80,12 @@ void TestCompositorHostOzone::Show() {
properties.bounds = bounds_;
// Create a PlatformWindow to get the AcceleratedWidget backing it.
window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
- &window_delegate_, std::move(properties));
+ window_delegate_.get(), std::move(properties));
window_->Show();
- DCHECK_NE(window_delegate_.widget(), gfx::kNullAcceleratedWidget);
+ DCHECK_NE(window_delegate_->widget(), gfx::kNullAcceleratedWidget);
allocator_.GenerateId();
- compositor_.SetAcceleratedWidget(window_delegate_.widget());
+ compositor_.SetAcceleratedWidget(window_delegate_->widget());
compositor_.SetScaleAndSize(1.0f, bounds_.size(),
allocator_.GetCurrentLocalSurfaceIdAllocation());
compositor_.SetVisible(true);
@@ -113,13 +95,16 @@ ui::Compositor* TestCompositorHostOzone::GetCompositor() {
return &compositor_;
}
-} // namespace
-
+// To avoid multiple definitions when use_x11 && use_ozone is true, disable this
+// factory method for OS_LINUX as Linux has a factory method that decides what
+// screen to use based on IsUsingOzonePlatform feature flag.
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// static
TestCompositorHost* TestCompositorHost::Create(
const gfx::Rect& bounds,
ui::ContextFactory* context_factory) {
return new TestCompositorHostOzone(bounds, context_factory);
}
+#endif
} // namespace ui
diff --git a/chromium/ui/compositor/test/test_compositor_host_ozone.h b/chromium/ui/compositor/test/test_compositor_host_ozone.h
new file mode 100644
index 00000000000..7b764f20382
--- /dev/null
+++ b/chromium/ui/compositor/test/test_compositor_host_ozone.h
@@ -0,0 +1,44 @@
+// 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 UI_COMPOSITOR_TEST_TEST_COMPOSITOR_HOST_OZONE_H_
+#define UI_COMPOSITOR_TEST_TEST_COMPOSITOR_HOST_OZONE_H_
+
+#include "ui/compositor/test/test_compositor_host.h"
+
+#include <memory>
+
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "ui/compositor/compositor.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui {
+
+class PlatformWindow;
+
+class TestCompositorHostOzone : public TestCompositorHost {
+ public:
+ TestCompositorHostOzone(const gfx::Rect& bounds,
+ ui::ContextFactory* context_factory);
+ TestCompositorHostOzone(const TestCompositorHostOzone&) = delete;
+ TestCompositorHostOzone& operator=(const TestCompositorHostOzone&) = delete;
+ ~TestCompositorHostOzone() override;
+
+ private:
+ class StubPlatformWindowDelegate;
+
+ // Overridden from TestCompositorHost:
+ void Show() override;
+ ui::Compositor* GetCompositor() override;
+
+ gfx::Rect bounds_;
+ ui::Compositor compositor_;
+ std::unique_ptr<PlatformWindow> window_;
+ std::unique_ptr<StubPlatformWindowDelegate> window_delegate_;
+ viz::ParentLocalSurfaceIdAllocator allocator_;
+};
+
+} // namespace ui
+
+#endif // UI_COMPOSITOR_TEST_TEST_COMPOSITOR_HOST_OZONE_H_
diff --git a/chromium/ui/compositor/transform_animation_curve_adapter.cc b/chromium/ui/compositor/transform_animation_curve_adapter.cc
index 49350962a25..9a871abab33 100644
--- a/chromium/ui/compositor/transform_animation_curve_adapter.cc
+++ b/chromium/ui/compositor/transform_animation_curve_adapter.cc
@@ -5,7 +5,6 @@
#include "ui/compositor/transform_animation_curve_adapter.h"
#include "base/memory/ptr_util.h"
-#include "cc/base/time_util.h"
namespace ui {
@@ -56,12 +55,10 @@ cc::TransformOperations TransformAnimationCurveAdapter::GetValue(
return target_wrapped_value_;
if (t <= base::TimeDelta())
return initial_wrapped_value_;
- double progress = cc::TimeUtil::Divide(t, duration_);
gfx::DecomposedTransform to_return = gfx::BlendDecomposedTransforms(
decomposed_target_value_, decomposed_initial_value_,
- gfx::Tween::CalculateValue(tween_type_, progress));
-
+ gfx::Tween::CalculateValue(tween_type_, t / duration_));
return WrapTransform(gfx::ComposeTransform(to_return));
}
diff --git a/chromium/ui/display/BUILD.gn b/chromium/ui/display/BUILD.gn
index fa824fb6aae..0b17650b229 100644
--- a/chromium/ui/display/BUILD.gn
+++ b/chromium/ui/display/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
import("//ui/display/display.gni")
@@ -13,7 +12,7 @@ import("//ui/display/display.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("display") {
+component("display") {
sources = [
"display.cc",
"display.h",
@@ -113,7 +112,7 @@ jumbo_component("display") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreGraphics.framework",
"QuartzCore.framework",
@@ -139,7 +138,7 @@ component("display_manager_test_api") {
]
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"test/display_matchers.cc",
diff --git a/chromium/ui/display/display.cc b/chromium/ui/display/display.cc
index ced58c7672a..96df627f101 100644
--- a/chromium/ui/display/display.cc
+++ b/chromium/ui/display/display.cc
@@ -307,7 +307,7 @@ void Display::SetScaleAndBounds(float device_scale_factor,
const gfx::Rect& bounds_in_pixel) {
gfx::Insets insets = bounds_.InsetsFrom(work_area_);
if (!HasForceDeviceScaleFactor()) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Unless an explicit scale factor was provided for testing, ensure the
// scale is integral.
device_scale_factor = static_cast<int>(device_scale_factor);
diff --git a/chromium/ui/display/display_features.cc b/chromium/ui/display/display_features.cc
index bbe1b07aa4a..9d5a08ed60f 100644
--- a/chromium/ui/display/display_features.cc
+++ b/chromium/ui/display/display_features.cc
@@ -4,13 +4,22 @@
#include "ui/display/display_features.h"
+#include "build/build_config.h"
+
namespace display {
namespace features {
#if defined(OS_CHROMEOS)
// Enables using HDR transfer function if the monitor says it supports it.
-const base::Feature kUseHDRTransferFunction{"UseHDRTransferFunction",
- base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kUseHDRTransferFunction {
+ "UseHDRTransferFunction",
+ // TODO(b/168843009): Temporarily disable on ARM while investigating.
+#if defined(ARCH_CPU_ARM_FAMILY)
+ base::FEATURE_DISABLED_BY_DEFAULT
+#else
+ base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
#endif
// This features allows listing all display modes of external displays in the
diff --git a/chromium/ui/display/fake/BUILD.gn b/chromium/ui/display/fake/BUILD.gn
index 69771bf685b..9617ef1ff0e 100644
--- a/chromium/ui/display/fake/BUILD.gn
+++ b/chromium/ui/display/fake/BUILD.gn
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
# This target contains dummy or fake classes that can be used as
# placeholders when lacking something better, or for testing.
-jumbo_component("fake") {
+component("fake") {
sources = [
"fake_display_delegate.cc",
"fake_display_delegate.h",
diff --git a/chromium/ui/display/fake/fake_display_delegate.cc b/chromium/ui/display/fake/fake_display_delegate.cc
index c363cbf9c19..767a9017732 100644
--- a/chromium/ui/display/fake/fake_display_delegate.cc
+++ b/chromium/ui/display/fake/fake_display_delegate.cc
@@ -35,11 +35,18 @@ constexpr uint32_t kProductCodeHash = 3692486807;
constexpr base::TimeDelta kConfigureDisplayDelay =
base::TimeDelta::FromMilliseconds(200);
+bool AreModesEqual(const display::DisplayMode& lhs,
+ const display::DisplayMode& rhs) {
+ return lhs.size() == rhs.size() &&
+ lhs.is_interlaced() == rhs.is_interlaced() &&
+ lhs.refresh_rate() == rhs.refresh_rate();
+}
+
} // namespace
-FakeDisplayDelegate::FakeDisplayDelegate() {}
+FakeDisplayDelegate::FakeDisplayDelegate() = default;
-FakeDisplayDelegate::~FakeDisplayDelegate() {}
+FakeDisplayDelegate::~FakeDisplayDelegate() = default;
int64_t FakeDisplayDelegate::AddDisplay(const gfx::Size& display_size) {
DCHECK(!display_size.IsEmpty());
@@ -122,27 +129,37 @@ void FakeDisplayDelegate::GetDisplays(GetDisplaysCallback callback) {
std::move(callback).Run(displays);
}
-void FakeDisplayDelegate::Configure(const DisplaySnapshot& output,
- const DisplayMode* mode,
- const gfx::Point& origin,
- ConfigureCallback callback) {
- bool configure_success = false;
-
- if (!mode) {
- // This is a request to turn off the display.
- configure_success = true;
- } else {
- // Check that |mode| is appropriate for the display snapshot.
- for (const auto& existing_mode : output.modes()) {
- if (existing_mode.get() == mode) {
- configure_success = true;
- break;
+void FakeDisplayDelegate::Configure(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ ConfigureCallback callback) {
+ base::flat_map<int64_t, bool> statuses;
+ for (const auto& config : config_requests) {
+ bool configure_success = false;
+
+ if (config.mode.has_value()) {
+ // Find display snapshot of display ID.
+ auto snapshot =
+ find_if(displays_.begin(), displays_.end(),
+ [&config](std::unique_ptr<DisplaySnapshot>& snapshot) {
+ return snapshot->display_id() == config.id;
+ });
+ if (snapshot != displays_.end()) {
+ // Check that config mode is appropriate for the display snapshot.
+ for (const auto& existing_mode : snapshot->get()->modes()) {
+ if (AreModesEqual(*existing_mode.get(), *config.mode.value().get())) {
+ configure_success = true;
+ break;
+ }
+ }
}
+ } else {
+ // This is a request to turn off the display.
+ configure_success = true;
}
+ statuses.insert(std::make_pair(config.id, configure_success));
}
- configure_callbacks_.push(
- base::BindOnce(std::move(callback), configure_success));
+ configure_callbacks_.push(base::BindOnce(std::move(callback), statuses));
// Start the timer if it's not already running. If there are multiple queued
// configuration requests then ConfigureDone() will handle starting the
diff --git a/chromium/ui/display/fake/fake_display_delegate.h b/chromium/ui/display/fake/fake_display_delegate.h
index 996093713b8..7e9c71cd1cc 100644
--- a/chromium/ui/display/fake/fake_display_delegate.h
+++ b/chromium/ui/display/fake/fake_display_delegate.h
@@ -14,7 +14,6 @@
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/timer/timer.h"
-#include "ui/display/fake/fake_display_delegate.h"
#include "ui/display/fake/fake_display_export.h"
#include "ui/display/types/fake_display_controller.h"
#include "ui/display/types/native_display_delegate.h"
@@ -84,10 +83,9 @@ class FAKE_DISPLAY_EXPORT FakeDisplayDelegate : public NativeDisplayDelegate,
void TakeDisplayControl(DisplayControlCallback callback) override;
void RelinquishDisplayControl(DisplayControlCallback callback) override;
void GetDisplays(GetDisplaysCallback callback) override;
- void Configure(const DisplaySnapshot& output,
- const DisplayMode* mode,
- const gfx::Point& origin,
- ConfigureCallback callback) override;
+ void Configure(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ ConfigureCallback callback) override;
void GetHDCPState(const DisplaySnapshot& output,
GetHDCPStateCallback callback) override;
void SetHDCPState(const DisplaySnapshot& output,
diff --git a/chromium/ui/display/mac/screen_mac.mm b/chromium/ui/display/mac/screen_mac.mm
index a4e374dd355..00415aac8b8 100644
--- a/chromium/ui/display/mac/screen_mac.mm
+++ b/chromium/ui/display/mac/screen_mac.mm
@@ -20,6 +20,7 @@
#include "base/mac/sdk_forward_declarations.h"
#include "base/stl_util.h"
#include "base/timer/timer.h"
+#include "base/trace_event/trace_event.h"
#include "ui/display/display.h"
#include "ui/display/display_change_notifier.h"
#include "ui/display/mac/display_link_mac.h"
@@ -54,6 +55,7 @@ NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) {
}
Display BuildDisplayForScreen(NSScreen* screen) {
+ TRACE_EVENT0("ui", "BuildDisplayForScreen");
NSRect frame = [screen frame];
CGDirectDisplayID display_id = [[[screen deviceDescription]
@@ -139,6 +141,11 @@ Display BuildDisplayForScreen(NSScreen* screen) {
// CGDisplayRotation returns a double. Display::SetRotationAsDegree will
// handle the unexpected situations were the angle is not a multiple of 90.
display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id)));
+
+ // TODO(crbug.com/1078903): Support multiple internal displays.
+ if (CGDisplayIsBuiltin(display_id))
+ Display::SetInternalDisplayId(display_id);
+
return display;
}
@@ -147,6 +154,8 @@ Display BuildPrimaryDisplay() {
}
std::vector<Display> BuildDisplaysFromQuartz() {
+ TRACE_EVENT0("ui", "BuildDisplaysFromQuartz");
+
// Don't just return all online displays. This would include displays
// that mirror other displays, which are not desired in this list. It's
// tempting to use the count returned by CGGetActiveDisplayList, but active
@@ -385,6 +394,7 @@ class ScreenMac : public Screen {
}
void OnNSScreensMayHaveChanged() {
+ TRACE_EVENT0("ui", "OnNSScreensMayHaveChanged");
auto new_displays = BuildDisplaysFromQuartz();
if (displays_ == new_displays)
return;
diff --git a/chromium/ui/display/manager/BUILD.gn b/chromium/ui/display/manager/BUILD.gn
index d98485a4cbc..a243223e044 100644
--- a/chromium/ui/display/manager/BUILD.gn
+++ b/chromium/ui/display/manager/BUILD.gn
@@ -2,11 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/display/display.gni")
-jumbo_component("manager") {
+component("manager") {
sources = [
"display_layout_store.cc",
"display_layout_store.h",
diff --git a/chromium/ui/display/manager/configure_displays_task.cc b/chromium/ui/display/manager/configure_displays_task.cc
index 9bb4b1a4967..2af79d1c126 100644
--- a/chromium/ui/display/manager/configure_displays_task.cc
+++ b/chromium/ui/display/manager/configure_displays_task.cc
@@ -11,6 +11,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/stl_util.h"
#include "ui/display/manager/display_util.h"
+#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/native_display_delegate.h"
@@ -72,6 +73,15 @@ int ComputeDisplayResolutionEnum(const DisplayMode* mode) {
return width_idx * base::size(kDisplayResolutionSamples) + height_idx + 1;
}
+std::__wrap_iter<const DisplayConfigureRequest*> GetRequestForDisplayId(
+ int64_t display_id,
+ const std::vector<DisplayConfigureRequest>& requests) {
+ return find_if(requests.begin(), requests.end(),
+ [display_id](const DisplayConfigureRequest& request) {
+ return request.display->display_id() == display_id;
+ });
+}
+
} // namespace
DisplayConfigureRequest::DisplayConfigureRequest(DisplaySnapshot* display,
@@ -109,30 +119,40 @@ void ConfigureDisplaysTask::Run() {
{
base::AutoReset<bool> recursivity_guard(&is_configuring_, true);
while (!pending_request_indexes_.empty()) {
- size_t index = pending_request_indexes_.front();
- DisplayConfigureRequest* request = &requests_[index];
- pending_request_indexes_.pop();
- const bool internal =
- request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
- base::UmaHistogramExactLinear(
- internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
- : "ConfigureDisplays.External.Modeset.Resolution",
- ComputeDisplayResolutionEnum(request->mode),
- base::size(kDisplayResolutionSamples) *
- base::size(kDisplayResolutionSamples) +
- 2);
-
- base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
- internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
- : "ConfigureDisplays.External.Modeset.RefreshRate",
- 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
- : 0);
-
- delegate_->Configure(
- *request->display, request->mode, request->origin,
- base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
- weak_ptr_factory_.GetWeakPtr(), index));
+ // Loop over all the current requests, then it will loop again making sure
+ // no new requests were added and are pending.
+ std::vector<display::DisplayConfigurationParams> config_requests;
+ for (size_t i = 0; i < pending_request_indexes_.size(); ++i) {
+ size_t index = pending_request_indexes_.front();
+ DisplayConfigureRequest* request = &requests_[index];
+ pending_request_indexes_.pop();
+
+ const bool internal =
+ request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+ base::UmaHistogramExactLinear(
+ internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
+ : "ConfigureDisplays.External.Modeset.Resolution",
+ ComputeDisplayResolutionEnum(request->mode),
+ base::size(kDisplayResolutionSamples) *
+ base::size(kDisplayResolutionSamples) +
+ 2);
+ base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+ internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
+ : "ConfigureDisplays.External.Modeset.RefreshRate",
+ 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
+ : 0);
+
+ display::DisplayConfigurationParams display_config_params(
+ request->display->display_id(), request->origin, request->mode);
+ config_requests.push_back(std::move(display_config_params));
+ }
+ if (!config_requests.empty()) {
+ delegate_->Configure(
+ config_requests,
+ base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
}
}
@@ -153,44 +173,83 @@ void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
Run();
}
-void ConfigureDisplaysTask::OnConfigured(size_t index, bool success) {
- DisplayConfigureRequest* request = &requests_[index];
- VLOG(2) << "Configured status=" << success
- << " display=" << request->display->display_id()
- << " origin=" << request->origin.ToString()
- << " mode=" << (request->mode ? request->mode->ToString() : "null");
-
- const bool internal =
- request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
- base::UmaHistogramBoolean(
- internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
- : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
- success);
-
- if (!success) {
- request->mode = FindNextMode(*request->display, request->mode);
- if (request->mode) {
- pending_request_indexes_.push(index);
+void ConfigureDisplaysTask::OnConfigured(
+ const base::flat_map<int64_t, bool>& statuses) {
+ bool config_success = true;
+
+ // Check if all displays are successfully configured.
+ for (const auto& status : statuses) {
+ int64_t display_id = status.first;
+ bool display_success = status.second;
+ config_success &= display_success;
+
+ auto request = GetRequestForDisplayId(display_id, requests_);
+ DCHECK(request != requests_.end());
+
+ VLOG(2) << "Configured status=" << display_success
+ << " display=" << request->display->display_id()
+ << " origin=" << request->origin.ToString()
+ << " mode=" << (request->mode ? request->mode->ToString() : "null");
+
+ bool internal =
+ request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+ base::UmaHistogramBoolean(
+ internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
+ : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
+ display_success);
+ }
+
+ if (config_success) {
+ for (const auto& status : statuses) {
+ auto request = GetRequestForDisplayId(status.first, requests_);
+ request->display->set_current_mode(request->mode);
+ request->display->set_origin(request->origin);
+ }
+ } else {
+ bool should_reconfigure = false;
+ // For the failing config, check if there is another mode to be requested.
+ // If there is one, attempt to reconfigure everything again.
+ for (const auto& status : statuses) {
+ int64_t display_id = status.first;
+ bool display_success = status.second;
+ if (!display_success) {
+ const DisplayConfigureRequest* request =
+ GetRequestForDisplayId(display_id, requests_).base();
+ const_cast<DisplayConfigureRequest*>(request)->mode =
+ FindNextMode(*request->display, request->mode);
+ should_reconfigure = !!request->mode;
+ }
+ }
+ // When reconfiguring, reconfigure all displays, not only the failing ones
+ // as they could potentially depend on each other.
+ if (should_reconfigure) {
+ for (const auto& status : statuses) {
+ auto const_iterator = GetRequestForDisplayId(status.first, requests_);
+ auto request = requests_.erase(const_iterator, const_iterator);
+ size_t index = std::distance(requests_.begin(), request);
+ pending_request_indexes_.push(index);
+ }
if (task_status_ == SUCCESS)
task_status_ = PARTIAL_SUCCESS;
-
Run();
return;
}
- } else {
- request->display->set_current_mode(request->mode);
- request->display->set_origin(request->origin);
}
- num_displays_configured_++;
+ // If no reconfigurations are happening, update the final state.
+ for (const auto& status : statuses) {
+ auto request = GetRequestForDisplayId(status.first, requests_);
+ bool internal =
+ request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+ base::UmaHistogramBoolean(
+ internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
+ : "ConfigureDisplays.External.Modeset.FinalStatus",
+ config_success);
+ }
- base::UmaHistogramBoolean(
- internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
- : "ConfigureDisplays.External.Modeset.FinalStatus",
- success);
- if (!success)
+ num_displays_configured_ += statuses.size();
+ if (!config_success)
task_status_ = ERROR;
-
Run();
}
diff --git a/chromium/ui/display/manager/configure_displays_task.h b/chromium/ui/display/manager/configure_displays_task.h
index 86368b3ee3c..25ca3f7b10a 100644
--- a/chromium/ui/display/manager/configure_displays_task.h
+++ b/chromium/ui/display/manager/configure_displays_task.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -65,7 +66,7 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
void OnDisplaySnapshotsInvalidated() override;
private:
- void OnConfigured(size_t index, bool success);
+ void OnConfigured(const base::flat_map<int64_t, bool>& statuses);
NativeDisplayDelegate* delegate_; // Not owned.
diff --git a/chromium/ui/display/manager/configure_displays_task_unittest.cc b/chromium/ui/display/manager/configure_displays_task_unittest.cc
index a032da4d319..a442d8bcb03 100644
--- a/chromium/ui/display/manager/configure_displays_task_unittest.cc
+++ b/chromium/ui/display/manager/configure_displays_task_unittest.cc
@@ -40,7 +40,7 @@ class ConfigureDisplaysTaskTest : public testing::Test {
.AddMode(small_mode_.Clone())
.Build();
}
- ~ConfigureDisplaysTaskTest() override {}
+ ~ConfigureDisplaysTaskTest() override = default;
void ConfigureCallback(ConfigureDisplaysTask::Status status) {
callback_called_ = true;
@@ -92,8 +92,9 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
- EXPECT_EQ(GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()),
- log_.GetActionsAndClear());
+ EXPECT_EQ(
+ GetCrtcAction({displays_[0]->display_id(), gfx::Point(), &small_mode_}),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) {
@@ -102,8 +103,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) {
std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) {
- requests.push_back(DisplayConfigureRequest(
- displays_[i].get(), displays_[i]->native_mode(), gfx::Point()));
+ requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(),
+ gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
@@ -111,12 +112,14 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &big_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) {
@@ -132,10 +135,11 @@ TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
- EXPECT_EQ(
- JoinActions(GetCrtcAction(*displays_[0], nullptr, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction(
+ {displays_[0]->display_id(), gfx::Point(), nullptr})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) {
@@ -151,12 +155,14 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[1], &big_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &big_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
@@ -167,8 +173,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) {
- requests.push_back(DisplayConfigureRequest(
- displays_[i].get(), displays_[i]->native_mode(), gfx::Point()));
+ requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(),
+ gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
@@ -176,13 +182,17 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &big_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) {
@@ -193,8 +203,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) {
std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) {
- requests.push_back(DisplayConfigureRequest(
- displays_[i].get(), displays_[i]->native_mode(), gfx::Point()));
+ requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(),
+ gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
@@ -202,13 +212,17 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &big_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
@@ -220,8 +234,8 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) {
- requests.push_back(DisplayConfigureRequest(
- displays_[i].get(), displays_[i]->native_mode(), gfx::Point()));
+ requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(),
+ gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
@@ -232,13 +246,17 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &big_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
} // namespace test
diff --git a/chromium/ui/display/manager/display_change_observer.cc b/chromium/ui/display/manager/display_change_observer.cc
index 89a9139d3fe..37678081d68 100644
--- a/chromium/ui/display/manager/display_change_observer.cc
+++ b/chromium/ui/display/manager/display_change_observer.cc
@@ -14,6 +14,8 @@
#include "base/check_op.h"
#include "base/command_line.h"
+#include "base/cpu.h"
+#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/user_activity/user_activity_detector.h"
@@ -96,9 +98,16 @@ gfx::DisplayColorSpaces FillDisplayColorSpaces(
gfx::DisplayColorSpaces display_color_spaces(
gfx::ColorSpace::CreateSRGB(), DisplaySnapshot::PrimaryFormat());
- if (allow_high_bit_depth) {
- constexpr float kSDRJoint = 0.5;
- constexpr float kHDRLevel = 3.0;
+ // AMD Chromebooks have issues playing back and scanning out high bit depth
+ // content. TODO(b/169576243, b/165825264): remove this provision when fixed.
+ static const base::NoDestructor<base::CPU> cpuid;
+ static const bool is_amd = cpuid->vendor_name() == "AuthenticAMD";
+ if (is_amd)
+ return display_color_spaces;
+
+ if (allow_high_bit_depth && snapshot_color_space.IsHDR()) {
+ constexpr float kSDRJoint = 0.75;
+ constexpr float kHDRLevel = 4.0;
const auto primary_id = snapshot_color_space.GetPrimaryID();
gfx::ColorSpace hdr_color_space;
if (primary_id == gfx::ColorSpace::PrimaryID::CUSTOM) {
@@ -350,11 +359,11 @@ ManagedDisplayInfo DisplayChangeObserver::CreateManagedDisplayInfo(
new_info.set_from_native_platform(true);
float device_scale_factor = 1.0f;
- // Sets dpi only if the screen size is not blacklisted.
- const float dpi = IsDisplaySizeBlackListed(snapshot->physical_size())
- ? 0
- : kInchInMm * mode_info->size().width() /
- snapshot->physical_size().width();
+ // Sets dpi only if the screen size is valid.
+ const float dpi = IsDisplaySizeValid(snapshot->physical_size())
+ ? kInchInMm * mode_info->size().width() /
+ snapshot->physical_size().width()
+ : 0;
constexpr gfx::Size k225DisplaySizeHack(3000, 2000);
if (snapshot->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) {
diff --git a/chromium/ui/display/manager/display_configurator.cc b/chromium/ui/display/manager/display_configurator.cc
index 30774956842..53dc139a4e8 100644
--- a/chromium/ui/display/manager/display_configurator.cc
+++ b/chromium/ui/display/manager/display_configurator.cc
@@ -44,31 +44,14 @@ struct DisplayState {
const DisplayMode* mirror_mode = nullptr;
};
-// This is used for calling either SetColorMatrix() or SetGammaCorrection()
-// depending on the given |color_correction_closure| which is run synchronously.
-// If |reset_color_space_on_success| is true and running
-// |color_correction_closure| returns true, then the color space of the display
-// with |display_id| will be reset.
-bool RunColorCorrectionClosureSync(
+// Returns whether |display_id| can be found in |display_list|,
+bool IsDisplayIdInDisplayStateList(
int64_t display_id,
- const DisplayConfigurator::DisplayStateList& cached_displays,
- bool reset_color_space_on_success,
- base::OnceCallback<bool(void)> color_correction_closure) {
- for (DisplaySnapshot* display : cached_displays) {
- if (display->display_id() != display_id)
- continue;
-
- const bool success = std::move(color_correction_closure).Run();
-
- // Nullify the |display|s ColorSpace to avoid correcting colors twice, if
- // we have successfully configured something.
- if (success && reset_color_space_on_success)
- display->reset_color_space();
-
- return success;
- }
-
- return false;
+ const DisplayConfigurator::DisplayStateList& display_list) {
+ return std::find_if(display_list.begin(), display_list.end(),
+ [display_id](DisplaySnapshot* display) {
+ return display->display_id() == display_id;
+ }) != display_list.end();
}
// Returns true if a platform native |mode| is equal to a |managed_mode|.
@@ -568,7 +551,6 @@ DisplayConfigurator::DisplayConfigurator()
configure_display_(chromeos::IsRunningAsSystemCompositor()),
current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
current_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
- current_internal_display_(nullptr),
requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
pending_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
has_pending_power_state_(false),
@@ -762,49 +744,37 @@ void DisplayConfigurator::ForceInitialConfigure() {
bool DisplayConfigurator::SetColorMatrix(
int64_t display_id,
const std::vector<float>& color_matrix) {
- return RunColorCorrectionClosureSync(
- display_id, cached_displays_,
- !color_matrix.empty() /* reset_color_space_on_success */,
- base::BindOnce(&NativeDisplayDelegate::SetColorMatrix,
- base::Unretained(native_display_delegate_.get()),
- display_id, color_matrix));
+ if (!IsDisplayIdInDisplayStateList(display_id, cached_displays_))
+ return false;
+ return native_display_delegate_->SetColorMatrix(display_id, color_matrix);
}
bool DisplayConfigurator::SetGammaCorrection(
int64_t display_id,
const std::vector<GammaRampRGBEntry>& degamma_lut,
const std::vector<GammaRampRGBEntry>& gamma_lut) {
- const bool reset_color_space_on_success =
- !degamma_lut.empty() || !gamma_lut.empty();
- return RunColorCorrectionClosureSync(
- display_id, cached_displays_, reset_color_space_on_success,
- base::BindOnce(&NativeDisplayDelegate::SetGammaCorrection,
- base::Unretained(native_display_delegate_.get()),
- display_id, degamma_lut, gamma_lut));
-}
-
-bool DisplayConfigurator::IsPrivacyScreenSupportedOnInternalDisplay() const {
- return current_internal_display_ &&
- current_internal_display_->privacy_screen_state() != kNotSupported &&
- current_internal_display_->current_mode();
+ if (!IsDisplayIdInDisplayStateList(display_id, cached_displays_))
+ return false;
+ return native_display_delegate_->SetGammaCorrection(display_id, degamma_lut,
+ gamma_lut);
}
-bool DisplayConfigurator::SetPrivacyScreenOnInternalDisplay(bool enabled) {
- if (IsPrivacyScreenSupportedOnInternalDisplay()) {
- native_display_delegate_->SetPrivacyScreen(
- current_internal_display_->display_id(), enabled);
- return true;
- }
-
- if (!current_internal_display_) {
- LOG(ERROR) << "This device does not have an internal display.";
- } else if (current_internal_display_->privacy_screen_state() ==
- kNotSupported) {
- LOG(ERROR) << "The internal display of this device does not support "
- "privacy screen.";
+void DisplayConfigurator::SetPrivacyScreen(int64_t display_id, bool enabled) {
+#if DCHECK_IS_ON()
+ DisplaySnapshot* internal_display = nullptr;
+ for (DisplaySnapshot* display : cached_displays_) {
+ if (display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) {
+ internal_display = display;
+ break;
+ }
}
+ DCHECK(internal_display);
+ DCHECK_EQ(internal_display->display_id(), display_id);
+ DCHECK_NE(internal_display->privacy_screen_state(), kNotSupported);
+ DCHECK(internal_display->current_mode());
+#endif
- return false;
+ native_display_delegate_->SetPrivacyScreen(display_id, enabled);
}
chromeos::DisplayPowerState DisplayConfigurator::GetRequestedPowerState()
@@ -812,16 +782,6 @@ chromeos::DisplayPowerState DisplayConfigurator::GetRequestedPowerState()
return requested_power_state_.value_or(chromeos::DISPLAY_POWER_ALL_ON);
}
-void DisplayConfigurator::UpdateInternalDisplayCache() {
- for (DisplaySnapshot* display : cached_displays_) {
- if (display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) {
- current_internal_display_ = display;
- return;
- }
- }
- current_internal_display_ = nullptr;
-}
-
void DisplayConfigurator::PrepareForExit() {
configure_display_ = false;
}
@@ -1034,9 +994,6 @@ void DisplayConfigurator::OnConfigured(
if (success) {
current_display_state_ = new_display_state;
UpdatePowerState(new_power_state);
- UpdateInternalDisplayCache();
- } else {
- current_internal_display_ = nullptr;
}
configuration_task_.reset();
diff --git a/chromium/ui/display/manager/display_configurator.h b/chromium/ui/display/manager/display_configurator.h
index e435a06bc04..fcf74a0b186 100644
--- a/chromium/ui/display/manager/display_configurator.h
+++ b/chromium/ui/display/manager/display_configurator.h
@@ -266,11 +266,9 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator
const std::vector<GammaRampRGBEntry>& degamma_lut,
const std::vector<GammaRampRGBEntry>& gamma_lut);
- // Enable/disable the privacy screen on the internal display of the device.
- // For this to succeed, privacy screen must be supported by the internal
- // display.
- bool SetPrivacyScreenOnInternalDisplay(bool enabled);
- bool IsPrivacyScreenSupportedOnInternalDisplay() const;
+ // Enable/disable the privacy screen on display with |display_id|.
+ // For this to succeed, privacy screen must be supported by the display.
+ void SetPrivacyScreen(int64_t display_id, bool enabled);
// Returns the requested power state if set or the default power state.
chromeos::DisplayPowerState GetRequestedPowerState() const;
@@ -372,7 +370,6 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator
// Current configuration state.
MultipleDisplayState current_display_state_;
chromeos::DisplayPowerState current_power_state_;
- DisplaySnapshot* current_internal_display_; // Not owned.
// Pending requests. These values are used when triggering the next display
// configuration.
diff --git a/chromium/ui/display/manager/display_configurator_unittest.cc b/chromium/ui/display/manager/display_configurator_unittest.cc
index e4cd8803313..a650f954172 100644
--- a/chromium/ui/display/manager/display_configurator_unittest.cc
+++ b/chromium/ui/display/manager/display_configurator_unittest.cc
@@ -119,7 +119,7 @@ class TestObserver : public DisplayConfigurator::Observer {
class TestStateController : public DisplayConfigurator::StateController {
public:
TestStateController() : state_(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED) {}
- ~TestStateController() override {}
+ ~TestStateController() override = default;
void set_state(MultipleDisplayState state) { state_ = state; }
@@ -144,7 +144,7 @@ class TestMirroringController
: public DisplayConfigurator::SoftwareMirroringController {
public:
TestMirroringController() : software_mirroring_enabled_(false) {}
- ~TestMirroringController() override {}
+ ~TestMirroringController() override = default;
void SetSoftwareMirroring(bool enabled) override {
software_mirroring_enabled_ = enabled;
@@ -349,8 +349,9 @@ class DisplayConfiguratorTest : public testing::Test {
Modes... modes) {
static_assert(I < kNumOutputs, "More expected modes than outputs");
- std::string action = GetCrtcAction(
- *outputs_[I], config == DisplayConfig::kOff ? nullptr : mode, origin);
+ std::string action =
+ GetCrtcAction({outputs_[I]->display_id(), origin,
+ config == DisplayConfig::kOff ? nullptr : mode});
if (mode && config != DisplayConfig::kMirror)
origin += {0, mode->size().height() + DisplayConfigurator::kVerticalGap};
@@ -947,13 +948,17 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
UpdateOutputs(1, true);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(),
- GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(),
- GetCrtcAction(*outputs_[0], modes[2].get(), gfx::Point(0, 0)).c_str(),
- nullptr),
- log_->GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({outputs_[0]->display_id(),
+ gfx::Point(0, 0), modes[0].get()})
+ .c_str(),
+ GetCrtcAction({outputs_[0]->display_id(),
+ gfx::Point(0, 0), modes[3].get()})
+ .c_str(),
+ GetCrtcAction({outputs_[0]->display_id(),
+ gfx::Point(0, 0), modes[2].get()})
+ .c_str(),
+ nullptr),
+ log_->GetActionsAndClear());
outputs_[1] = FakeDisplaySnapshot::Builder()
.SetId(kDisplayIds[1])
@@ -976,26 +981,40 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
EXPECT_EQ(
JoinActions(
- GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(),
+ GetCrtcAction(
+ {outputs_[0]->display_id(), gfx::Point(0, 0), modes[0].get()})
+ .c_str(),
// Then attempt to configure crtc1 with the first mode.
- GetCrtcAction(*outputs_[1], modes[0].get(), gfx::Point(0, 0)).c_str(),
+ GetCrtcAction(
+ {outputs_[1]->display_id(), gfx::Point(0, 0), modes[0].get()})
+ .c_str(),
// First mode tried is expected to fail and it will
// retry wil the 4th mode in the list.
- GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(),
- GetCrtcAction(*outputs_[1], modes[3].get(), gfx::Point(0, 0)).c_str(),
+ GetCrtcAction(
+ {outputs_[0]->display_id(), gfx::Point(0, 0), modes[3].get()})
+ .c_str(),
+ GetCrtcAction(
+ {outputs_[1]->display_id(), gfx::Point(0, 0), modes[3].get()})
+ .c_str(),
// Since it was requested to go into mirror mode
// and the configured modes were different, it
// should now try and setup a valid configurable
// extended mode.
- GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(),
- GetCrtcAction(*outputs_[1], modes[0].get(),
- gfx::Point(0, modes[0]->size().height() +
- DisplayConfigurator::kVerticalGap))
+ GetCrtcAction(
+ {outputs_[0]->display_id(), gfx::Point(0, 0), modes[0].get()})
+ .c_str(),
+ GetCrtcAction({outputs_[1]->display_id(),
+ gfx::Point(0, modes[0]->size().height() +
+ DisplayConfigurator::kVerticalGap),
+ modes[0].get()})
.c_str(),
- GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(),
- GetCrtcAction(*outputs_[1], modes[3].get(),
- gfx::Point(0, modes[0]->size().height() +
- DisplayConfigurator::kVerticalGap))
+ GetCrtcAction(
+ {outputs_[0]->display_id(), gfx::Point(0, 0), modes[3].get()})
+ .c_str(),
+ GetCrtcAction({outputs_[1]->display_id(),
+ gfx::Point(0, modes[0]->size().height() +
+ DisplayConfigurator::kVerticalGap),
+ modes[3].get()})
.c_str(),
nullptr),
log_->GetActionsAndClear());
@@ -1213,9 +1232,10 @@ TEST_F(DisplayConfiguratorTest,
EXPECT_EQ(
JoinActions(
GetCrtcActions(&small_mode_, &big_mode_).c_str(),
- GetCrtcAction(*outputs_[1], &small_mode_,
- gfx::Point(0, small_mode_.size().height() +
- DisplayConfigurator::kVerticalGap))
+ GetCrtcAction({outputs_[1]->display_id(),
+ gfx::Point(0, small_mode_.size().height() +
+ DisplayConfigurator::kVerticalGap),
+ &small_mode_})
.c_str(),
nullptr),
log_->GetActionsAndClear());
@@ -1431,63 +1451,6 @@ TEST_F(DisplayConfiguratorTest, PowerStateChange) {
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, observer_.latest_power_state());
}
-TEST_F(DisplayConfiguratorTest, EnablePrivacyScreenOnSupportedEmbeddedDisplay) {
- outputs_[0] = FakeDisplaySnapshot::Builder()
- .SetId(kDisplayIds[0])
- .SetNativeMode(small_mode_.Clone())
- .SetCurrentMode(small_mode_.Clone())
- .AddMode(big_mode_.Clone())
- .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
- .SetIsAspectPerservingScaling(true)
- .SetPrivacyScreen(kDisabled)
- .Build();
-
- state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
- InitWithOutputs(&small_mode_);
- observer_.Reset();
-
- EXPECT_TRUE(configurator_.SetPrivacyScreenOnInternalDisplay(true));
- EXPECT_EQ(SetPrivacyScreenAction(kDisplayIds[0], true),
- log_->GetActionsAndClear());
-}
-
-TEST_F(DisplayConfiguratorTest,
- EnablePrivacyScreenOnUnsupportedEmbeddedDisplay) {
- outputs_[0] = FakeDisplaySnapshot::Builder()
- .SetId(kDisplayIds[0])
- .SetNativeMode(big_mode_.Clone())
- .SetCurrentMode(big_mode_.Clone())
- .AddMode(small_mode_.Clone())
- .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL)
- .SetIsAspectPerservingScaling(true)
- .SetPrivacyScreen(kNotSupported)
- .Build();
- state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
- InitWithOutputs(&big_mode_);
- observer_.Reset();
-
- EXPECT_FALSE(configurator_.SetPrivacyScreenOnInternalDisplay(true));
- EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
-}
-
-TEST_F(DisplayConfiguratorTest, EnablePrivacyScreenOnExternalDisplay) {
- outputs_[0] = FakeDisplaySnapshot::Builder()
- .SetId(kDisplayIds[0])
- .SetNativeMode(small_mode_.Clone())
- .SetCurrentMode(small_mode_.Clone())
- .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT)
- .SetIsAspectPerservingScaling(true)
- .SetPrivacyScreen(kNotSupported)
- .Build();
-
- state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
- InitWithOutputs(&small_mode_);
- observer_.Reset();
-
- EXPECT_FALSE(configurator_.SetPrivacyScreenOnInternalDisplay(true));
- EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
-}
-
class DisplayConfiguratorMultiMirroringTest : public DisplayConfiguratorTest {
public:
DisplayConfiguratorMultiMirroringTest() = default;
diff --git a/chromium/ui/display/manager/display_manager_utilities.cc b/chromium/ui/display/manager/display_manager_utilities.cc
index ac8acd8910d..e5bda9d98dc 100644
--- a/chromium/ui/display/manager/display_manager_utilities.cc
+++ b/chromium/ui/display/manager/display_manager_utilities.cc
@@ -73,13 +73,10 @@ bool ForceFirstDisplayInternal() {
return ret;
}
-bool ComputeBoundary(const Display& a_display,
- const Display& b_display,
- gfx::Rect* a_edge_in_screen,
- gfx::Rect* b_edge_in_screen) {
- const gfx::Rect& a_bounds = a_display.bounds();
- const gfx::Rect& b_bounds = b_display.bounds();
-
+bool ComputeBoundary(const gfx::Rect& a_bounds,
+ const gfx::Rect& b_bounds,
+ gfx::Rect* a_edge,
+ gfx::Rect* b_edge) {
// Find touching side.
int rx = std::max(a_bounds.x(), b_bounds.x());
int ry = std::max(a_bounds.y(), b_bounds.y());
@@ -125,11 +122,11 @@ bool ComputeBoundary(const Display& a_display,
int left = std::max(a_bounds.x(), b_bounds.x());
int right = std::min(a_bounds.right(), b_bounds.right());
if (position == DisplayPlacement::TOP) {
- a_edge_in_screen->SetRect(left, a_bounds.y(), right - left, 1);
- b_edge_in_screen->SetRect(left, b_bounds.bottom() - 1, right - left, 1);
+ a_edge->SetRect(left, a_bounds.y(), right - left, 1);
+ b_edge->SetRect(left, b_bounds.bottom() - 1, right - left, 1);
} else {
- a_edge_in_screen->SetRect(left, a_bounds.bottom() - 1, right - left, 1);
- b_edge_in_screen->SetRect(left, b_bounds.y(), right - left, 1);
+ a_edge->SetRect(left, a_bounds.bottom() - 1, right - left, 1);
+ b_edge->SetRect(left, b_bounds.y(), right - left, 1);
}
break;
}
@@ -138,11 +135,11 @@ bool ComputeBoundary(const Display& a_display,
int top = std::max(a_bounds.y(), b_bounds.y());
int bottom = std::min(a_bounds.bottom(), b_bounds.bottom());
if (position == DisplayPlacement::LEFT) {
- a_edge_in_screen->SetRect(a_bounds.x(), top, 1, bottom - top);
- b_edge_in_screen->SetRect(b_bounds.right() - 1, top, 1, bottom - top);
+ a_edge->SetRect(a_bounds.x(), top, 1, bottom - top);
+ b_edge->SetRect(b_bounds.right() - 1, top, 1, bottom - top);
} else {
- a_edge_in_screen->SetRect(a_bounds.right() - 1, top, 1, bottom - top);
- b_edge_in_screen->SetRect(b_bounds.x(), top, 1, bottom - top);
+ a_edge->SetRect(a_bounds.right() - 1, top, 1, bottom - top);
+ b_edge->SetRect(b_bounds.x(), top, 1, bottom - top);
}
break;
}
@@ -150,6 +147,14 @@ bool ComputeBoundary(const Display& a_display,
return true;
}
+bool ComputeBoundary(const Display& display_a,
+ const Display& display_b,
+ gfx::Rect* a_edge_in_screen,
+ gfx::Rect* b_edge_in_screen) {
+ return ComputeBoundary(display_a.bounds(), display_b.bounds(),
+ a_edge_in_screen, b_edge_in_screen);
+}
+
DisplayIdList CreateDisplayIdList(const Displays& list) {
return GenerateDisplayIdList(
list.begin(), list.end(),
diff --git a/chromium/ui/display/manager/display_manager_utilities.h b/chromium/ui/display/manager/display_manager_utilities.h
index 99a957a32be..c326941663d 100644
--- a/chromium/ui/display/manager/display_manager_utilities.h
+++ b/chromium/ui/display/manager/display_manager_utilities.h
@@ -50,13 +50,21 @@ CreateUnifiedManagedDisplayModeList(
// internal display.
bool ForceFirstDisplayInternal();
-// Computes the bounds that defines the bounds between two displays.
-// Returns false if two displays do not intersect.
-DISPLAY_MANAGER_EXPORT bool ComputeBoundary(
- const Display& primary_display,
- const Display& secondary_display,
- gfx::Rect* primary_edge_in_screen,
- gfx::Rect* secondary_edge_in_screen);
+// If |a_bounds| and |b_bounds| share an edge, the shared edges are computed and
+// filled in |a_edge| and |b_edge|, and true is returned. Otherwise, it returns
+// false.
+DISPLAY_MANAGER_EXPORT bool ComputeBoundary(const gfx::Rect& a_bounds,
+ const gfx::Rect& b_bounds,
+ gfx::Rect* a_edge,
+ gfx::Rect* b_edge);
+
+// If |display_a| and |display_b| share an edge, the shared edges are computed
+// and filled in |a_edge_in_screen| and |b_edge_in_screen|, and true is
+// returned. Otherwise, it returns false.
+DISPLAY_MANAGER_EXPORT bool ComputeBoundary(const Display& display_a,
+ const Display& display_b,
+ gfx::Rect* a_edge_in_screen,
+ gfx::Rect* b_edge_in_screen);
// Sorts id list using |CompareDisplayIds| below.
DISPLAY_MANAGER_EXPORT void SortDisplayIdList(DisplayIdList* list);
diff --git a/chromium/ui/display/manager/update_display_configuration_task_unittest.cc b/chromium/ui/display/manager/update_display_configuration_task_unittest.cc
index c8518831bb6..fed77e738ac 100644
--- a/chromium/ui/display/manager/update_display_configuration_task_unittest.cc
+++ b/chromium/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -26,7 +26,7 @@ class TestSoftwareMirroringController
: public DisplayConfigurator::SoftwareMirroringController {
public:
TestSoftwareMirroringController() : is_enabled_(false) {}
- ~TestSoftwareMirroringController() override {}
+ ~TestSoftwareMirroringController() override = default;
// DisplayConfigurator::SoftwareMirroringController:
void SetSoftwareMirroring(bool enabled) override { is_enabled_ = enabled; }
@@ -162,7 +162,7 @@ class UpdateDisplayConfigurationTaskTest : public testing::Test {
.AddMode(small_mode_.Clone())
.Build();
}
- ~UpdateDisplayConfigurationTaskTest() override {}
+ ~UpdateDisplayConfigurationTaskTest() override = default;
void UpdateDisplays(size_t count) {
std::vector<DisplaySnapshot*> displays;
@@ -245,11 +245,11 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleConfiguration) {
EXPECT_TRUE(configuration_status_);
EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, display_state_);
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(UpdateDisplayConfigurationTaskTest, ExtendedConfiguration) {
@@ -269,12 +269,14 @@ TEST_F(UpdateDisplayConfigurationTaskTest, ExtendedConfiguration) {
EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, display_state_);
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_);
EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_,
- gfx::Point(0, small_mode_.size().height()))
- .c_str(),
- nullptr),
+ JoinActions(GetCrtcAction(
+ {displays_[0]->display_id(), gfx::Point(), &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(),
+ gfx::Point(0, small_mode_.size().height()),
+ &big_mode_})
+ .c_str(),
+ nullptr),
log_.GetActionsAndClear());
}
@@ -294,12 +296,14 @@ TEST_F(UpdateDisplayConfigurationTaskTest, MirrorConfiguration) {
EXPECT_TRUE(configuration_status_);
EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, display_state_);
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(UpdateDisplayConfigurationTaskTest, FailMirrorConfiguration) {
@@ -336,15 +340,18 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) {
EXPECT_TRUE(configured_);
EXPECT_FALSE(configuration_status_);
EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_,
- gfx::Point(0, small_mode_.size().height()))
- .c_str(),
- GetCrtcAction(*displays_[1], &small_mode_,
- gfx::Point(0, small_mode_.size().height()))
- .c_str(),
- nullptr),
+ JoinActions(GetCrtcAction(
+ {displays_[0]->display_id(), gfx::Point(), &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(),
+ gfx::Point(0, small_mode_.size().height()),
+ &big_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(),
+ gfx::Point(0, small_mode_.size().height()),
+ &small_mode_})
+ .c_str(),
+ nullptr),
log_.GetActionsAndClear());
}
@@ -364,11 +371,11 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleChangePowerConfiguration) {
EXPECT_TRUE(configuration_status_);
EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, display_state_);
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
+ &small_mode_})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
// Turn power off
{
@@ -383,10 +390,11 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleChangePowerConfiguration) {
EXPECT_TRUE(configuration_status_);
EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, display_state_);
EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, power_state_);
- EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], nullptr, gfx::Point()).c_str(), nullptr),
- log_.GetActionsAndClear());
+ EXPECT_EQ(JoinActions(GetCrtcAction(
+ {displays_[0]->display_id(), gfx::Point(), nullptr})
+ .c_str(),
+ nullptr),
+ log_.GetActionsAndClear());
}
TEST_F(UpdateDisplayConfigurationTaskTest, NoopSoftwareMirrorConfiguration) {
@@ -454,12 +462,14 @@ TEST_F(UpdateDisplayConfigurationTaskTest,
EXPECT_TRUE(layout_manager_.GetSoftwareMirroringController()
->SoftwareMirroringEnabled());
EXPECT_EQ(
- JoinActions(
- GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(),
- GetCrtcAction(*displays_[1], &big_mode_,
- gfx::Point(0, small_mode_.size().height()))
- .c_str(),
- nullptr),
+ JoinActions(GetCrtcAction(
+ {displays_[0]->display_id(), gfx::Point(), &small_mode_})
+ .c_str(),
+ GetCrtcAction({displays_[1]->display_id(),
+ gfx::Point(0, small_mode_.size().height()),
+ &big_mode_})
+ .c_str(),
+ nullptr),
log_.GetActionsAndClear());
}
diff --git a/chromium/ui/display/screen.cc b/chromium/ui/display/screen.cc
index c289415a1a1..ac185dd8121 100644
--- a/chromium/ui/display/screen.cc
+++ b/chromium/ui/display/screen.cc
@@ -26,7 +26,7 @@ Screen::~Screen() = default;
// static
Screen* Screen::GetScreen() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// TODO(scottmg): https://crbug.com/558054
if (!g_screen)
g_screen = CreateNativeScreen();
diff --git a/chromium/ui/display/screen.h b/chromium/ui/display/screen.h
index 7742ef3e192..0b60feaa3fe 100644
--- a/chromium/ui/display/screen.h
+++ b/chromium/ui/display/screen.h
@@ -78,10 +78,10 @@ class DISPLAY_EXPORT Screen {
// the location of the view within that window won't influence the result).
virtual Display GetDisplayNearestView(gfx::NativeView view) const;
- // Returns the display nearest the specified point. |point| should be in DIPs.
+ // Returns the display nearest the specified DIP |point|.
virtual Display GetDisplayNearestPoint(const gfx::Point& point) const = 0;
- // Returns the display that most closely intersects the provided bounds.
+ // Returns the display that most closely intersects the DIP rect |match_rect|.
virtual Display GetDisplayMatching(const gfx::Rect& match_rect) const = 0;
// Returns the primary display. It is guaranteed that this will return a
diff --git a/chromium/ui/display/types/BUILD.gn b/chromium/ui/display/types/BUILD.gn
index 86ea7b44df1..5d42d9c73ed 100644
--- a/chromium/ui/display/types/BUILD.gn
+++ b/chromium/ui/display/types/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("types") {
+component("types") {
output_name = "display_types"
sources = [
"display_configuration_params.cc",
diff --git a/chromium/ui/display/types/display_configuration_params.cc b/chromium/ui/display/types/display_configuration_params.cc
index 996ade949e4..60d0aa3cad6 100644
--- a/chromium/ui/display/types/display_configuration_params.cc
+++ b/chromium/ui/display/types/display_configuration_params.cc
@@ -8,8 +8,22 @@ namespace display {
DisplayConfigurationParams::DisplayConfigurationParams() = default;
DisplayConfigurationParams::DisplayConfigurationParams(
+ DisplayConfigurationParams& other)
+ : id(other.id), origin(other.origin) {
+ if (other.mode)
+ mode = other.mode->get()->Clone();
+}
+
+DisplayConfigurationParams::DisplayConfigurationParams(
+ DisplayConfigurationParams&& other)
+ : id(other.id), origin(other.origin) {
+ if (other.mode)
+ mode = other.mode->get()->Clone();
+}
+
+DisplayConfigurationParams::DisplayConfigurationParams(
int64_t id,
- gfx::Point origin,
+ const gfx::Point& origin,
const display::DisplayMode* pmode)
: id(id), origin(origin) {
if (pmode)
diff --git a/chromium/ui/display/types/display_configuration_params.h b/chromium/ui/display/types/display_configuration_params.h
index 73c009435dd..31c44f6281d 100644
--- a/chromium/ui/display/types/display_configuration_params.h
+++ b/chromium/ui/display/types/display_configuration_params.h
@@ -16,8 +16,10 @@ namespace display {
struct DISPLAY_TYPES_EXPORT DisplayConfigurationParams {
DisplayConfigurationParams();
+ DisplayConfigurationParams(DisplayConfigurationParams& other);
+ DisplayConfigurationParams(DisplayConfigurationParams&& other);
DisplayConfigurationParams(int64_t id,
- gfx::Point origin,
+ const gfx::Point& origin,
const display::DisplayMode* pmode);
~DisplayConfigurationParams();
diff --git a/chromium/ui/display/types/display_snapshot.h b/chromium/ui/display/types/display_snapshot.h
index 22c567a3779..fc552e50f4e 100644
--- a/chromium/ui/display/types/display_snapshot.h
+++ b/chromium/ui/display/types/display_snapshot.h
@@ -71,7 +71,6 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot {
return color_correction_in_linear_space_;
}
const gfx::ColorSpace& color_space() const { return color_space_; }
- void reset_color_space() { color_space_ = gfx::ColorSpace(); }
uint32_t bits_per_channel() const { return bits_per_channel_; }
const std::string& display_name() const { return display_name_; }
const base::FilePath& sys_path() const { return sys_path_; }
@@ -122,7 +121,7 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot {
// instead of gamma compressed one.
const bool color_correction_in_linear_space_;
- gfx::ColorSpace color_space_;
+ const gfx::ColorSpace color_space_;
uint32_t bits_per_channel_;
diff --git a/chromium/ui/display/types/native_display_delegate.h b/chromium/ui/display/types/native_display_delegate.h
index 42760526333..cb8980acf00 100644
--- a/chromium/ui/display/types/native_display_delegate.h
+++ b/chromium/ui/display/types/native_display_delegate.h
@@ -10,24 +10,23 @@
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/display_types_export.h"
#include "ui/display/types/fake_display_controller.h"
-namespace gfx {
-class Point;
-}
-
namespace display {
-class DisplayMode;
class DisplaySnapshot;
class NativeDisplayObserver;
struct GammaRampRGBEntry;
+struct DisplayConfigurationParams;
using GetDisplaysCallback =
base::OnceCallback<void(const std::vector<DisplaySnapshot*>&)>;
-using ConfigureCallback = base::OnceCallback<void(bool)>;
+using ConfigureCallback =
+ base::OnceCallback<void(const base::flat_map<int64_t, bool>&)>;
using GetHDCPStateCallback = base::OnceCallback<void(bool, HDCPState)>;
using SetHDCPStateCallback = base::OnceCallback<void(bool)>;
using DisplayControlCallback = base::OnceCallback<void(bool)>;
@@ -58,10 +57,9 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate {
// the display to |origin| in the framebuffer. |mode| can be NULL, which
// represents disabling the display. The callback will return the status of
// the operation.
- virtual void Configure(const DisplaySnapshot& output,
- const DisplayMode* mode,
- const gfx::Point& origin,
- ConfigureCallback callback) = 0;
+ virtual void Configure(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ ConfigureCallback callback) = 0;
// Gets HDCP state of output.
virtual void GetHDCPState(const DisplaySnapshot& output,
diff --git a/chromium/ui/display/util/BUILD.gn b/chromium/ui/display/util/BUILD.gn
index 782be6f8f2d..e740cc5ad74 100644
--- a/chromium/ui/display/util/BUILD.gn
+++ b/chromium/ui/display/util/BUILD.gn
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//ui/ozone/ozone.gni")
-jumbo_component("util") {
+component("util") {
output_name = "display_util"
sources = [
"display_util.cc",
@@ -32,7 +31,7 @@ jumbo_component("util") {
if (is_chromeos) {
deps += [ "//ui/display/types" ]
} else if (is_mac) {
- libs = [ "IOSurface.framework" ]
+ frameworks = [ "IOSurface.framework" ]
}
}
diff --git a/chromium/ui/display/util/display_util.cc b/chromium/ui/display/util/display_util.cc
index f5a0ab66b71..8ca876e83d2 100644
--- a/chromium/ui/display/util/display_util.cc
+++ b/chromium/ui/display/util/display_util.cc
@@ -47,22 +47,22 @@ bool NearlyEqual(const skcms_Matrix3x3& lhs,
} // namespace
-bool IsDisplaySizeBlackListed(const gfx::Size& physical_size) {
+bool IsDisplaySizeValid(const gfx::Size& physical_size) {
// Ignore if the reported display is smaller than minimum size.
if (physical_size.width() <= kInvalidDisplaySizeList[0][0] ||
physical_size.height() <= kInvalidDisplaySizeList[0][1]) {
VLOG(1) << "Smaller than minimum display size";
- return true;
+ return false;
}
for (size_t i = 1; i < base::size(kInvalidDisplaySizeList); ++i) {
const gfx::Size size(kInvalidDisplaySizeList[i][0],
kInvalidDisplaySizeList[i][1]);
if (physical_size == size) {
VLOG(1) << "Black listed display size detected:" << size.ToString();
- return true;
+ return false;
}
}
- return false;
+ return true;
}
int64_t GenerateDisplayID(uint16_t manufacturer_id,
diff --git a/chromium/ui/display/util/display_util.h b/chromium/ui/display/util/display_util.h
index 8bf61ea55a0..8f4e16d83ad 100644
--- a/chromium/ui/display/util/display_util.h
+++ b/chromium/ui/display/util/display_util.h
@@ -30,10 +30,9 @@ enum class EdidColorSpaceChecksOutcome {
kMaxValue = kErrorBadGamma
};
-// Returns true if a given size is in the list of bogus sizes in mm that should
-// be ignored.
-DISPLAY_UTIL_EXPORT bool IsDisplaySizeBlackListed(
- const gfx::Size& physical_size);
+// Returns true if a given size is allowed. Will return false for certain bogus
+// sizes in mm that should be ignored.
+DISPLAY_UTIL_EXPORT bool IsDisplaySizeValid(const gfx::Size& physical_size);
// Returns 64-bit persistent ID for the specified manufacturer's ID and
// product_code_hash, and the index of the output it is connected to.
diff --git a/chromium/ui/display/util/display_util_unittest.cc b/chromium/ui/display/util/display_util_unittest.cc
index f4ab7470743..6b5d2339879 100644
--- a/chromium/ui/display/util/display_util_unittest.cc
+++ b/chromium/ui/display/util/display_util_unittest.cc
@@ -119,16 +119,16 @@ const unsigned char kBrokenBluePrimaries[] =
} // namespace
-TEST(DisplayUtilTest, TestBlackListedDisplay) {
- EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(10, 10)));
- EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(40, 30)));
- EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(50, 40)));
- EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(160, 90)));
- EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(160, 100)));
+TEST(DisplayUtilTest, TestValidDisplaySize) {
+ EXPECT_FALSE(IsDisplaySizeValid(gfx::Size(10, 10)));
+ EXPECT_FALSE(IsDisplaySizeValid(gfx::Size(40, 30)));
+ EXPECT_FALSE(IsDisplaySizeValid(gfx::Size(50, 40)));
+ EXPECT_FALSE(IsDisplaySizeValid(gfx::Size(160, 90)));
+ EXPECT_FALSE(IsDisplaySizeValid(gfx::Size(160, 100)));
- EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(50, 60)));
- EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(100, 70)));
- EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(272, 181)));
+ EXPECT_TRUE(IsDisplaySizeValid(gfx::Size(50, 60)));
+ EXPECT_TRUE(IsDisplaySizeValid(gfx::Size(100, 70)));
+ EXPECT_TRUE(IsDisplaySizeValid(gfx::Size(272, 181)));
}
TEST(DisplayUtilTest, GetColorSpaceFromEdid) {
diff --git a/chromium/ui/display/win/scaling_util.cc b/chromium/ui/display/win/scaling_util.cc
index e23e68ee6d5..823333dba8c 100644
--- a/chromium/ui/display/win/scaling_util.cc
+++ b/chromium/ui/display/win/scaling_util.cc
@@ -8,8 +8,8 @@
#include "base/check.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/range/range.h"
@@ -77,7 +77,7 @@ int ScaleOffset(int unscaled_length, float scale_factor, int unscaled_offset) {
float scaled_length = static_cast<float>(unscaled_length) / scale_factor;
float percent =
static_cast<float>(unscaled_offset) / static_cast<float>(unscaled_length);
- return gfx::ToFlooredInt(scaled_length * percent);
+ return base::ClampFloor(scaled_length * percent);
}
} // namespace
diff --git a/chromium/ui/display/win/screen_win.cc b/chromium/ui/display/win/screen_win.cc
index d4fc53fd60d..f5064d1b53d 100644
--- a/chromium/ui/display/win/screen_win.cc
+++ b/chromium/ui/display/win/screen_win.cc
@@ -13,6 +13,7 @@
#include "base/bind_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/ranges.h"
+#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "base/trace_event/trace_event.h"
@@ -29,7 +30,6 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/icc_profile.h"
@@ -136,7 +136,7 @@ float GetSDRWhiteLevel(const base::Optional<DISPLAYCONFIG_PATH_INFO>& path) {
white_level.header.adapterId = path->targetInfo.adapterId;
white_level.header.id = path->targetInfo.id;
if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS)
- return white_level.SDRWhiteLevel * 80.0 / 1000.0;
+ return white_level.SDRWhiteLevel * 80.0 / 1000.0; // From wingdi.h.
}
return 200.0f;
}
@@ -202,15 +202,11 @@ std::vector<DisplayInfo> FindAndRemoveTouchingDisplayInfos(
return touching_display_infos;
}
-// Default scRGB white level in nits. This is used to determine the SDR scaling
-// factor with the user configured white level from the SDR brightness slider.
-constexpr float kDefaultScrgbWhiteLevel = 80.0f;
-
// Helper function to create gfx::DisplayColorSpaces from given |color_space|
// and |sdr_white_level| with default buffer formats for Windows.
gfx::DisplayColorSpaces CreateDisplayColorSpaces(
const gfx::ColorSpace& color_space,
- float sdr_white_level = kDefaultScrgbWhiteLevel) {
+ float sdr_white_level = gfx::ColorSpace::kDefaultScrgbLinearSdrWhiteLevel) {
gfx::DisplayColorSpaces display_color_spaces(color_space);
// When alpha is not needed, specify BGRX_8888 to get
// DXGI_ALPHA_MODE_IGNORE. This saves significant power (see
@@ -229,8 +225,7 @@ gfx::DisplayColorSpaces GetDisplayColorSpacesForHdr(float sdr_white_level) {
// This will map to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709. In that space,
// the brightness of (1,1,1) is 80 nits.
- const auto scrgb_linear = gfx::ColorSpace::CreateSCRGBLinear(
- kDefaultScrgbWhiteLevel / sdr_white_level);
+ const auto scrgb_linear = gfx::ColorSpace::CreateSCRGBLinear(sdr_white_level);
// This will map to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, with sRGB's
// (1,1,1) mapping to the specified number of nits.
@@ -260,12 +255,11 @@ gfx::DisplayColorSpaces GetDisplayColorSpacesForHdr(float sdr_white_level) {
// Sets SDR white level and buffer formats on |display_color_spaces| when using
// a forced color profile.
gfx::DisplayColorSpaces GetForcedDisplayColorSpaces() {
- // Adjust white level to default for Windows if color space is PQ. This is
- // needed because the color space is created with the cross-platform default
- // white level which is different (gfx::ColorSpace::kDefaultSDRWhiteLevel).
+ // Adjust white level to a default value irrespective of whether the color
+ // space is scRGB linear (defaults to 80 nits) or PQ (defaults to 100 nits).
const auto& color_space =
- Display::GetForcedDisplayColorProfile().GetWithPQSDRWhiteLevel(
- kDefaultScrgbWhiteLevel);
+ Display::GetForcedDisplayColorProfile().GetWithSDRWhiteLevel(
+ gfx::ColorSpace::kDefaultScrgbLinearSdrWhiteLevel);
auto display_color_spaces = CreateDisplayColorSpaces(color_space);
// Use the forced color profile's buffer format for all content usages.
if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::SMPTEST2084) {
@@ -728,7 +722,8 @@ Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const {
}
Display ScreenWin::GetDisplayMatching(const gfx::Rect& match_rect) const {
- return GetScreenWinDisplayNearestScreenRect(match_rect).display();
+ const gfx::Rect screen_rect = DIPToScreenRect(nullptr, match_rect);
+ return GetScreenWinDisplayNearestScreenRect(screen_rect).display();
}
Display ScreenWin::GetPrimaryDisplay() const {
@@ -928,8 +923,8 @@ int ScreenWin::GetSystemMetricsForScaleFactor(float scale_factor,
// Windows 8.1 doesn't support GetSystemMetricsForDpi(), yet does support
// per-process dpi awareness.
- return gfx::ToRoundedInt(GetSystemMetrics(metric) * scale_factor /
- GetPrimaryDisplay().device_scale_factor());
+ return base::ClampRound(GetSystemMetrics(metric) * scale_factor /
+ GetPrimaryDisplay().device_scale_factor());
}
void ScreenWin::RecordDisplayScaleFactors() const {
diff --git a/chromium/ui/display/win/screen_win_unittest.cc b/chromium/ui/display/win/screen_win_unittest.cc
index fa5d8572c8f..748159ca5b5 100644
--- a/chromium/ui/display/win/screen_win_unittest.cc
+++ b/chromium/ui/display/win/screen_win_unittest.cc
@@ -542,7 +542,7 @@ TEST_F(ScreenWinTestSingleDisplay1_25x, GetDisplayMatching) {
Display display = screen->GetAllDisplays()[0];
EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1435, 859, 100, 100)));
}
TEST_F(ScreenWinTestSingleDisplay1_25x, GetPrimaryDisplay) {
Screen* screen = GetScreen();
@@ -698,7 +698,7 @@ TEST_F(ScreenWinTestSingleDisplay1_5x, GetDisplayMatching) {
Display display = screen->GetAllDisplays()[0];
EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1179, 633, 100, 100)));
}
TEST_F(ScreenWinTestSingleDisplay1_5x, GetPrimaryDisplay) {
Screen* screen = GetScreen();
@@ -853,8 +853,7 @@ TEST_F(ScreenWinTestSingleDisplay2x, GetDisplayMatching) {
Screen* screen = GetScreen();
Display display = screen->GetAllDisplays()[0];
EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
- EXPECT_EQ(display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ EXPECT_EQ(display, screen->GetDisplayMatching(gfx::Rect(859, 499, 100, 100)));
}
namespace {
@@ -1395,12 +1394,12 @@ TEST_F(ScreenWinTestTwoDisplays2x, GetDisplayMatching) {
EXPECT_EQ(left_display,
screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(left_display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(859, 499, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(1920, 0, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(960, 0, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(2619, 499, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1259, 199, 100, 100)));
}
TEST_F(ScreenWinTestTwoDisplays2x, GetPrimaryDisplay) {
@@ -1831,7 +1830,7 @@ TEST_F(ScreenWinTestManyDisplays1x, GetDisplayMatching) {
EXPECT_EQ(displays[1],
screen->GetDisplayMatching(gfx::Rect(640, 0, 100, 100)));
EXPECT_EQ(displays[1],
- screen->GetDisplayMatching(gfx::Rect(1563, 667, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1563, 659, 100, 100)));
EXPECT_EQ(displays[2],
screen->GetDisplayMatching(gfx::Rect(0, 480, 100, 100)));
EXPECT_EQ(displays[2],
@@ -2288,23 +2287,23 @@ TEST_F(ScreenWinTestManyDisplays2x, GetDisplayMatching) {
ASSERT_EQ(5u, displays.size());
EXPECT_EQ(displays[0], screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(displays[0],
- screen->GetDisplayMatching(gfx::Rect(539, 379, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(219, 139, 100, 100)));
EXPECT_EQ(displays[1],
- screen->GetDisplayMatching(gfx::Rect(640, 0, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(320, 0, 100, 100)));
EXPECT_EQ(displays[1],
- screen->GetDisplayMatching(gfx::Rect(1563, 667, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(731, 279, 100, 100)));
EXPECT_EQ(displays[2],
- screen->GetDisplayMatching(gfx::Rect(0, 480, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(0, 240, 100, 100)));
EXPECT_EQ(displays[2],
- screen->GetDisplayMatching(gfx::Rect(539, 679, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(219, 289, 100, 100)));
EXPECT_EQ(displays[3],
- screen->GetDisplayMatching(gfx::Rect(1664, 768, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(832, 384, 100, 100)));
EXPECT_EQ(displays[3],
- screen->GetDisplayMatching(gfx::Rect(1963, 1067, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(931, 483, 100, 100)));
EXPECT_EQ(displays[4],
- screen->GetDisplayMatching(gfx::Rect(1864, 1168, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(932, 584, 100, 100)));
EXPECT_EQ(displays[4],
- screen->GetDisplayMatching(gfx::Rect(1963, 1267, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(931, 583, 100, 100)));
}
TEST_F(ScreenWinTestManyDisplays2x, GetPrimaryDisplay) {
@@ -2596,7 +2595,7 @@ TEST_F(ScreenWinTestTwoDisplays1x2x, GetDisplayMatching) {
EXPECT_EQ(right_display,
screen->GetDisplayMatching(gfx::Rect(1920, 0, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(2619, 499, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(2219, 199, 100, 100)));
}
TEST_F(ScreenWinTestTwoDisplays1x2x, GetPrimaryDisplay) {
@@ -2878,12 +2877,12 @@ TEST_F(ScreenWinTestTwoDisplays1_5x1x, GetDisplayMatching) {
EXPECT_EQ(left_display,
screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(left_display,
- screen->GetDisplayMatching(gfx::Rect(699, 499, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(433, 299, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(800, 120, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(534, -80, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(1339, 499, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1073, 299, 100, 100)));
}
TEST_F(ScreenWinTestTwoDisplays1_5x1x, GetPrimaryDisplay) {
@@ -3168,12 +3167,12 @@ TEST_F(ScreenWinTestTwoDisplays2x1x, GetDisplayMatching) {
EXPECT_EQ(left_display,
screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(left_display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(859, 499, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(1920, 0, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(960, 0, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(2619, 499, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1559, 499, 100, 100)));
}
TEST_F(ScreenWinTestTwoDisplays2x1x, GetPrimaryDisplay) {
@@ -3460,12 +3459,12 @@ TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetDisplayMatching) {
EXPECT_EQ(left_display,
screen->GetDisplayMatching(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(left_display,
- screen->GetDisplayMatching(gfx::Rect(1819, 1099, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(1499, 699, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(6400, 0, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(3200, 0, 100, 100)));
EXPECT_EQ(right_display,
- screen->GetDisplayMatching(gfx::Rect(10139, 2299, 100, 100)));
+ screen->GetDisplayMatching(gfx::Rect(5019, 1099, 100, 100)));
}
TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetPrimaryDisplay) {
diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn
index 14aa1413f80..2ded85b6201 100644
--- a/chromium/ui/events/BUILD.gn
+++ b/chromium/ui/events/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
import("//ui/base/ui_features.gni")
@@ -23,7 +22,7 @@ if (is_ios) {
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_static_library("dom_keycode_converter") {
+static_library("dom_keycode_converter") {
public = [
"keycodes/dom/dom_code.h",
"keycodes/dom/dom_codes.h",
@@ -61,7 +60,7 @@ static_library("dom_keyboard_layout") {
"//base",
]
- if (is_win || is_mac || use_ozone) {
+ if (is_win || is_mac || use_ozone || use_x11) {
public += [ "keycodes/dom/dom_keyboard_layout_map.h" ]
sources += [
@@ -99,7 +98,7 @@ source_set("platform_event") {
sources = [ "platform_event.h" ]
}
-jumbo_component("events_base") {
+component("events_base") {
sources = [
"base_event_utils.cc",
"base_event_utils.h",
@@ -180,14 +179,14 @@ jumbo_component("events_base") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"Carbon.framework",
]
}
}
-jumbo_component("events") {
+component("events") {
public = [
"cocoa/cocoa_event_utils.h",
"event.h",
@@ -274,6 +273,7 @@ jumbo_component("events") {
":gesture_detection",
"//base/third_party/dynamic_annotations",
"//skia",
+ "//ui/base:features",
"//ui/gfx",
"//ui/gfx/geometry",
]
@@ -285,13 +285,17 @@ jumbo_component("events") {
friend = [ ":*" ]
if (use_x11) {
- sources += [ "x/keyboard_hook_x11.cc" ]
+ sources += [
+ "x/keyboard_hook_x11.cc",
+ "x/keyboard_hook_x11.h",
+ ]
}
if (use_x11 || ozone_platform_x11) {
public += [ "x/x11_event_translation.h" ]
sources += [ "x/x11_event_translation.cc" ]
deps += [
+ "//ui/base:features",
"//ui/events/devices",
"//ui/events/devices/x11",
"//ui/events/x",
@@ -305,6 +309,12 @@ jumbo_component("events") {
if (use_ozone || use_x11) {
sources += [ "events_default.cc" ]
+
+ if (is_linux || is_chromeos) {
+ # TODO(https://crbug.com/1099225): refactor X11 kbd hook and implement
+ # that for Ozone.
+ sources += [ "keyboard_hook_linux.cc" ]
+ }
}
if (is_win && use_ozone) {
@@ -312,12 +322,18 @@ jumbo_component("events") {
}
if (use_ozone) {
- public += [ "ozone/events_ozone.h" ]
+ public += [
+ "ozone/events_ozone.h",
+ "ozone/keyboard_hook_ozone.h",
+ ]
sources += [
"ozone/events_ozone.cc",
"ozone/keyboard_hook_ozone.cc",
]
- deps += [ "//ui/events/ozone/layout" ]
+ deps += [
+ "//ui/base:features",
+ "//ui/events/ozone/layout",
+ ]
}
if (use_aura) {
@@ -363,7 +379,7 @@ jumbo_component("events") {
}
if (is_mac) {
- libs = [ "AppKit.framework" ]
+ frameworks = [ "AppKit.framework" ]
}
if (is_fuchsia) {
@@ -376,7 +392,7 @@ jumbo_component("events") {
}
}
-jumbo_component("gesture_detection") {
+component("gesture_detection") {
sources = [
"gesture_detection/bitset_32.h",
"gesture_detection/filtered_gesture_provider.cc",
@@ -440,7 +456,7 @@ jumbo_component("gesture_detection") {
}
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
sources = [
"test/event_generator.cc",
"test/event_generator.h",
@@ -495,7 +511,7 @@ jumbo_static_library("test_support") {
]
if (is_mac) {
- libs = [ "Carbon.framework" ]
+ frameworks = [ "Carbon.framework" ]
}
if (use_x11 || ozone_platform_x11) {
@@ -515,7 +531,10 @@ jumbo_static_library("test_support") {
}
if (use_ozone) {
- deps += [ "//ui/events/ozone/layout" ]
+ deps += [
+ "//ui/base:features",
+ "//ui/events/ozone/layout",
+ ]
}
}
@@ -601,14 +620,6 @@ if (!is_ios) {
data_deps = [ "//third_party/mesa_headers" ]
}
- if (use_x11) {
- sources += [ "devices/x11/device_data_manager_x11_unittest.cc" ]
- deps += [
- "//ui/events/devices/x11",
- "//ui/events/x:unittests",
- ]
- }
-
if (use_x11 || use_ozone) {
sources += [ "devices/device_data_manager_unittest.cc" ]
}
diff --git a/chromium/ui/events/DEPS b/chromium/ui/events/DEPS
index b2daedd1417..d9f4d652ad3 100644
--- a/chromium/ui/events/DEPS
+++ b/chromium/ui/events/DEPS
@@ -1,5 +1,7 @@
include_rules = [
+ "+ui/base/ui_base_features.h",
"+ui/display",
"+ui/gfx",
"+ui/latency",
+ "+ui/base/ui_base_features.h",
]
diff --git a/chromium/ui/events/android/event_handler_android.cc b/chromium/ui/events/android/event_handler_android.cc
index 4f069034953..7124711af47 100644
--- a/chromium/ui/events/android/event_handler_android.cc
+++ b/chromium/ui/events/android/event_handler_android.cc
@@ -49,8 +49,11 @@ bool EventHandlerAndroid::ScrollTo(float x, float y) {
void EventHandlerAndroid::OnSizeChanged() {}
-void EventHandlerAndroid::OnPhysicalBackingSizeChanged() {}
+void EventHandlerAndroid::OnPhysicalBackingSizeChanged(
+ base::Optional<base::TimeDelta> deadline_override) {}
void EventHandlerAndroid::OnBrowserControlsHeightChanged() {}
+void EventHandlerAndroid::OnControlsResizeViewChanged() {}
+
} // namespace ui
diff --git a/chromium/ui/events/android/event_handler_android.h b/chromium/ui/events/android/event_handler_android.h
index 1cafd595db5..d2eaa35e0f8 100644
--- a/chromium/ui/events/android/event_handler_android.h
+++ b/chromium/ui/events/android/event_handler_android.h
@@ -5,6 +5,8 @@
#ifndef UI_EVENTS_ANDROID_EVENT_HANDLER_ANDROID_H_
#define UI_EVENTS_ANDROID_EVENT_HANDLER_ANDROID_H_
+#include "base/optional.h"
+#include "base/time/time.h"
#include "ui/events/events_export.h"
namespace ui {
@@ -28,8 +30,10 @@ class EVENTS_EXPORT EventHandlerAndroid {
virtual bool OnMouseWheelEvent(const MotionEventAndroid& event);
virtual bool OnGestureEvent(const GestureEventAndroid& event);
virtual void OnSizeChanged();
- virtual void OnPhysicalBackingSizeChanged();
+ virtual void OnPhysicalBackingSizeChanged(
+ base::Optional<base::TimeDelta> deadline_override);
virtual void OnBrowserControlsHeightChanged();
+ virtual void OnControlsResizeViewChanged();
virtual bool OnGenericMotionEvent(const MotionEventAndroid& event);
virtual bool OnKeyUp(const KeyEventAndroid& event);
diff --git a/chromium/ui/events/base_event_utils.cc b/chromium/ui/events/base_event_utils.cc
index 3c7928ddb97..f5c709d2e65 100644
--- a/chromium/ui/events/base_event_utils.cc
+++ b/chromium/ui/events/base_event_utils.cc
@@ -19,12 +19,12 @@ namespace {
#if defined(OS_CHROMEOS)
const int kSystemKeyModifierMask = EF_ALT_DOWN | EF_COMMAND_DOWN;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
// Alt modifier is used to input extended characters on Mac.
const int kSystemKeyModifierMask = EF_COMMAND_DOWN;
#else
const int kSystemKeyModifierMask = EF_ALT_DOWN;
-#endif // !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
+#endif // !defined(OS_CHROMEOS) && !defined(OS_APPLE)
bool IsValidTimebase(base::TimeTicks now, base::TimeTicks timestamp) {
int64_t delta = (now - timestamp).InMilliseconds();
diff --git a/chromium/ui/events/blink/BUILD.gn b/chromium/ui/events/blink/BUILD.gn
index 2ff0353b00c..bfe2424fdf5 100644
--- a/chromium/ui/events/blink/BUILD.gn
+++ b/chromium/ui/events/blink/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
-jumbo_component("blink_features") {
+component("blink_features") {
defines = [ "IS_BLINK_FEATURES_IMPL" ]
sources = [
@@ -16,7 +15,7 @@ jumbo_component("blink_features") {
deps = [ "//base" ]
}
-jumbo_source_set("blink") {
+source_set("blink") {
sources = [
"blink_event_util.cc",
"blink_event_util.h",
diff --git a/chromium/ui/events/blink/blink_event_util.cc b/chromium/ui/events/blink/blink_event_util.cc
index e9710b2743c..c53efe798b9 100644
--- a/chromium/ui/events/blink/blink_event_util.cc
+++ b/chromium/ui/events/blink/blink_event_util.cc
@@ -26,7 +26,6 @@
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/angle_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/transform.h"
diff --git a/chromium/ui/events/blink/web_input_event_unittest.cc b/chromium/ui/events/blink/web_input_event_unittest.cc
index abcc74c1df4..e3a815473c0 100644
--- a/chromium/ui/events/blink/web_input_event_unittest.cc
+++ b/chromium/ui/events/blink/web_input_event_unittest.cc
@@ -10,6 +10,7 @@
#include "base/stl_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/events/event.h"
@@ -20,11 +21,13 @@
#include "ui/events/test/keyboard_layout.h"
#if defined(USE_X11)
-#include "ui/events/test/events_test_utils_x11.h"
-#include "ui/events/x/x11_event_translation.h" // nogncheck
-#include "ui/gfx/x/event.h" // nogncheck
-#include "ui/gfx/x/x11.h" // nogncheck
-#include "ui/gfx/x/x11_types.h" // nogncheck
+#include "ui/base/x/x11_util.h" // nogncheck
+#include "ui/events/test/events_test_utils_x11.h" // nogncheck
+#include "ui/events/x/x11_event_translation.h" // nogncheck
+#include "ui/gfx/x/event.h" // nogncheck
+#include "ui/gfx/x/x11.h" // nogncheck
+#include "ui/gfx/x/x11_types.h" // nogncheck
+#include "ui/gfx/x/xproto.h" // nogncheck
#endif
namespace ui {
@@ -77,6 +80,9 @@ TEST(WebInputEventTest, TestMakeWebKeyboardEvent) {
EXPECT_EQ(static_cast<int>(DomKey::CONTROL), webkit_event.dom_key);
}
#if defined(USE_X11)
+ // https://crbug.com/1109112): fix this.
+ if (features::IsUsingOzonePlatform())
+ return;
const int kLocationModifiers =
blink::WebInputEvent::kIsLeft | blink::WebInputEvent::kIsRight;
ScopedXI2Event xev;
@@ -102,14 +108,16 @@ TEST(WebInputEventTest, TestMakeWebKeyboardEvent) {
TEST(WebInputEventTest, TestMakeWebKeyboardEventWindowsKeyCode) {
#if defined(USE_X11)
+ // https://crbug.com/1109112): enable this.
+ if (features::IsUsingOzonePlatform())
+ return;
ScopedXI2Event xev;
{
// Press left Ctrl.
xev.InitKeyEvent(ET_KEY_PRESSED, VKEY_CONTROL, 0);
x11::Event* x11_event = xev;
- XEvent* xevent = &x11_event->xlib_event();
- xevent->xkey.keycode =
- KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_LEFT);
+ x11_event->As<x11::KeyEvent>()->detail = static_cast<x11::KeyCode>(
+ KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_LEFT));
auto event = ui::BuildKeyEventFromXEvent(*xev);
blink::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(*event);
EXPECT_EQ(VKEY_CONTROL, webkit_event.windows_key_code);
@@ -118,9 +126,8 @@ TEST(WebInputEventTest, TestMakeWebKeyboardEventWindowsKeyCode) {
// Press right Ctrl.
xev.InitKeyEvent(ET_KEY_PRESSED, VKEY_CONTROL, 0);
x11::Event* x11_event = xev;
- XEvent* xevent = &x11_event->xlib_event();
- xevent->xkey.keycode =
- KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_RIGHT);
+ x11_event->As<x11::KeyEvent>()->detail = static_cast<x11::KeyCode>(
+ KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_RIGHT));
auto event = ui::BuildKeyEventFromXEvent(*xev);
blink::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(*event);
EXPECT_EQ(VKEY_CONTROL, webkit_event.windows_key_code);
@@ -213,6 +220,9 @@ TEST(WebInputEventTest, TestMakeWebKeyboardEventKeyPadKeyCode) {
<< "}, expect: " << test_case.expected_result;
}
#if defined(USE_X11)
+ // https://crbug.com/1109112): fix this.
+ if (features::IsUsingOzonePlatform())
+ return;
ScopedXI2Event xev;
for (size_t i = 0; i < base::size(kTesCases); ++i) {
const TestCase& test_case = kTesCases[i];
@@ -225,11 +235,11 @@ TEST(WebInputEventTest, TestMakeWebKeyboardEventKeyPadKeyCode) {
xev.InitKeyEvent(ET_KEY_PRESSED, test_case.ui_keycode, EF_NONE);
x11::Event* x11_event = xev;
- XEvent* xevent = &x11_event->xlib_event();
- xevent->xkey.keycode =
- XKeysymToKeycode(gfx::GetXDisplay(), test_case.x_keysym);
- if (!xevent->xkey.keycode)
+ auto keycode = x11::Connection::Get()->KeysymToKeycode(
+ static_cast<x11::KeySym>(test_case.x_keysym));
+ if (keycode == x11::KeyCode{})
continue;
+ x11_event->As<x11::KeyEvent>()->detail = keycode;
auto event = ui::BuildKeyEventFromXEvent(*xev);
blink::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(*event);
EXPECT_EQ(test_case.expected_result, (webkit_event.GetModifiers() &
diff --git a/chromium/ui/events/cocoa/cocoa_event_utils.mm b/chromium/ui/events/cocoa/cocoa_event_utils.mm
index 1ba23d8099c..d70b3dc6b18 100644
--- a/chromium/ui/events/cocoa/cocoa_event_utils.mm
+++ b/chromium/ui/events/cocoa/cocoa_event_utils.mm
@@ -78,6 +78,10 @@ int EventFlagsFromNSEventWithModifiers(NSEvent* event, NSUInteger modifiers) {
flags |= IsRightButtonEvent(event) ? ui::EF_RIGHT_MOUSE_BUTTON : 0;
flags |= IsMiddleButtonEvent(event) ? ui::EF_MIDDLE_MOUSE_BUTTON : 0;
+
+ if ([event type] == NSKeyDown && [event isARepeat])
+ flags |= ui::EF_IS_REPEAT;
+
return flags;
}
diff --git a/chromium/ui/events/cocoa/events_mac_unittest.mm b/chromium/ui/events/cocoa/events_mac_unittest.mm
index 0c2cb375eed..201cc6ce0b6 100644
--- a/chromium/ui/events/cocoa/events_mac_unittest.mm
+++ b/chromium/ui/events/cocoa/events_mac_unittest.mm
@@ -204,6 +204,10 @@ TEST_F(EventsMacTest, EventFlagsFromNative) {
NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask);
EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN,
EventFlagsFromNative(cmdalt));
+
+ // Make sure a repeat key-down event gets ui::EF_IS_REPEAT set.
+ NSEvent* repeat_key_down = cocoa_test_event_utils::KeyDownEventWithRepeat();
+ EXPECT_EQ(ui::EF_IS_REPEAT, EventFlagsFromNative(repeat_key_down));
}
// Tests mouse button presses and mouse wheel events.
diff --git a/chromium/ui/events/devices/BUILD.gn b/chromium/ui/events/devices/BUILD.gn
index 749e3a78594..80e545a8d17 100644
--- a/chromium/ui/events/devices/BUILD.gn
+++ b/chromium/ui/events/devices/BUILD.gn
@@ -2,8 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
@@ -15,7 +13,7 @@ if (is_android) {
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("devices") {
+component("devices") {
sources = [
"device_data_manager.cc",
"device_data_manager.h",
@@ -33,7 +31,7 @@ jumbo_component("devices") {
"touchscreen_device.h",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"device_util_linux.cc",
"device_util_linux.h",
diff --git a/chromium/ui/events/devices/gamepad_device.cc b/chromium/ui/events/devices/gamepad_device.cc
index 737649d20ba..8089f3abe45 100644
--- a/chromium/ui/events/devices/gamepad_device.cc
+++ b/chromium/ui/events/devices/gamepad_device.cc
@@ -7,8 +7,11 @@
namespace ui {
GamepadDevice::GamepadDevice(const InputDevice& input_device,
- std::vector<GamepadDevice::Axis>&& axes)
- : InputDevice(input_device), axes(std::move(axes)) {}
+ std::vector<GamepadDevice::Axis>&& axes,
+ bool supports_rumble)
+ : InputDevice(input_device),
+ axes(std::move(axes)),
+ supports_vibration_rumble(supports_rumble) {}
GamepadDevice::GamepadDevice(const GamepadDevice& other) = default;
diff --git a/chromium/ui/events/devices/gamepad_device.h b/chromium/ui/events/devices/gamepad_device.h
index 5ef4a4b9d7d..9d2fc2cc6e3 100644
--- a/chromium/ui/events/devices/gamepad_device.h
+++ b/chromium/ui/events/devices/gamepad_device.h
@@ -27,12 +27,17 @@ struct EVENTS_DEVICES_EXPORT GamepadDevice : public InputDevice {
int32_t resolution = 0;
};
- GamepadDevice(const InputDevice& input_device, std::vector<Axis>&& axes);
+ GamepadDevice(const InputDevice& input_device,
+ std::vector<Axis>&& axes,
+ bool supports_rumble);
GamepadDevice(const GamepadDevice& other);
~GamepadDevice() override;
// Axes the gamepad has e.g. analog thumb sticks.
std::vector<Axis> axes;
+
+ // Whether the gamepad device supports rumble type force feedback.
+ bool supports_vibration_rumble = false;
};
} // namespace ui
diff --git a/chromium/ui/events/devices/x11/BUILD.gn b/chromium/ui/events/devices/x11/BUILD.gn
index b6f246c6039..888e18ee8e1 100644
--- a/chromium/ui/events/devices/x11/BUILD.gn
+++ b/chromium/ui/events/devices/x11/BUILD.gn
@@ -2,13 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
assert(use_x11 || ozone_platform_x11)
-jumbo_component("x11") {
+component("x11") {
output_name = "events_devices_x11"
sources = [
@@ -19,6 +18,7 @@ jumbo_component("x11") {
"events_devices_x11_export.h",
"touch_factory_x11.cc",
"touch_factory_x11.h",
+ "xinput_util.h",
]
defines = [ "EVENTS_DEVICES_X11_IMPLEMENTATION" ]
diff --git a/chromium/ui/events/devices/x11/OWNERS b/chromium/ui/events/devices/x11/OWNERS
new file mode 100644
index 00000000000..280ba478dca
--- /dev/null
+++ b/chromium/ui/events/devices/x11/OWNERS
@@ -0,0 +1 @@
+thomasanderson@chromium.org
diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.cc b/chromium/ui/events/devices/x11/device_data_manager_x11.cc
index bebb83dc7a5..d83649108bc 100644
--- a/chromium/ui/events/devices/x11/device_data_manager_x11.cc
+++ b/chromium/ui/events/devices/x11/device_data_manager_x11.cc
@@ -15,10 +15,12 @@
#include "base/memory/singleton.h"
#include "base/stl_util.h"
#include "base/system/sys_info.h"
+#include "base/version.h"
#include "build/build_config.h"
#include "ui/display/display.h"
#include "ui/events/devices/x11/device_list_cache_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
+#include "ui/events/devices/x11/xinput_util.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_switches.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
@@ -32,14 +34,6 @@
#define XIScrollClass 3
#endif
-// Multi-touch support was introduced in XI 2.2. Add XI event types here
-// for backward-compatibility with older versions of XInput.
-#if !defined(XI_TouchBegin)
-#define XI_TouchBegin 18
-#define XI_TouchUpdate 19
-#define XI_TouchEnd 20
-#endif
-
// Copied from xserver-properties.h
#define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel"
#define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel"
@@ -118,9 +112,11 @@ namespace ui {
namespace {
template <typename Iterator>
-Iterator FindDeviceWithId(Iterator begin, Iterator end, int id) {
+Iterator FindDeviceWithId(Iterator begin,
+ Iterator end,
+ x11::Input::DeviceId id) {
for (auto it = begin; it != end; ++it) {
- if (it->id == id)
+ if (static_cast<x11::Input::DeviceId>(it->id) == id)
return it;
}
return end;
@@ -150,6 +146,29 @@ x11::Input::Fp3232 DoubleToFp3232(double in) {
return ret;
}
+// Identical to FP3232_TO_DOUBLE from libxi's XExtInt.c
+double Fp3232ToDouble(const x11::Input::Fp3232& x) {
+ return static_cast<double>(x.integral) +
+ static_cast<double>(x.frac) / (1ULL << 32);
+}
+
+bool GetSourceId(const x11::Event& x11_event, uint16_t* sourceid) {
+ x11::Input::DeviceId source{};
+ if (auto* dev = x11_event.As<x11::Input::DeviceEvent>())
+ source = dev->sourceid;
+ else if (auto* dev_change = x11_event.As<x11::Input::DeviceChangedEvent>())
+ source = dev_change->sourceid;
+ else if (auto* crossing = x11_event.As<x11::Input::CrossingEvent>())
+ source = crossing->sourceid;
+ else
+ return false;
+ uint16_t source16 = static_cast<uint16_t>(source);
+ if (source16 >= DeviceDataManagerX11::kMaxDeviceNum)
+ return false;
+ *sourceid = source16;
+ return true;
+}
+
} // namespace
bool DeviceDataManagerX11::IsCMTDataType(const int type) {
@@ -179,12 +198,11 @@ DeviceDataManagerX11* DeviceDataManagerX11::GetInstance() {
DeviceDataManagerX11::DeviceDataManagerX11()
: xi_opcode_(-1),
- high_precision_scrolling_disabled_(IsHighPrecisionScrollingDisabled()),
- button_map_count_(0) {
- CHECK(gfx::GetXDisplay());
+ high_precision_scrolling_disabled_(IsHighPrecisionScrollingDisabled()) {
+ CHECK(x11::Connection::Get());
InitializeXInputInternal();
- UpdateDeviceList(gfx::GetXDisplay());
+ UpdateDeviceList(x11::Connection::Get());
UpdateButtonMap();
}
@@ -193,41 +211,31 @@ DeviceDataManagerX11::~DeviceDataManagerX11() = default;
bool DeviceDataManagerX11::InitializeXInputInternal() {
// Check if XInput is available on the system.
xi_opcode_ = -1;
- int opcode, event, error;
- if (!XQueryExtension(gfx::GetXDisplay(), "XInputExtension", &opcode, &event,
- &error)) {
- VLOG(1) << "X Input extension not available: error=" << error;
+ auto* connection = x11::Connection::Get();
+ if (!connection->xinput().present()) {
+ VLOG(1) << "X Input extension not available";
return false;
}
// Check the XInput version.
- int major = 2, minor = 2;
- if (XIQueryVersion(gfx::GetXDisplay(), &major, &minor) == BadRequest) {
+ auto version = connection->xinput().XIQueryVersion(
+ {x11::Input::major_version, x11::Input::minor_version});
+ if (auto reply = version.Sync()) {
+ if (base::Version({reply->major_version, reply->minor_version}) <
+ base::Version("2.2")) {
+ DVLOG(1) << "XI version on server is " << reply->major_version << "."
+ << reply->minor_version << ". "
+ << "But 2.2 is required.";
+ return false;
+ }
+ } else {
VLOG(1) << "XInput2 not supported in the server.";
return false;
}
- if (major < 2 || (major == 2 && minor < 2)) {
- DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
- << "But 2.2 is required.";
- return false;
- }
- xi_opcode_ = opcode;
+ xi_opcode_ = connection->xinput().major_opcode();
CHECK_NE(-1, xi_opcode_);
- // Possible XI event types for XIDeviceEvent. See the XI2 protocol
- // specification.
- xi_device_event_types_[XI_KeyPress] = true;
- xi_device_event_types_[XI_KeyRelease] = true;
- xi_device_event_types_[XI_ButtonPress] = true;
- xi_device_event_types_[XI_ButtonRelease] = true;
- xi_device_event_types_[XI_Motion] = true;
- // Multi-touch support was introduced in XI 2.2.
- if (minor >= 2) {
- xi_device_event_types_[XI_TouchBegin] = true;
- xi_device_event_types_[XI_TouchUpdate] = true;
- xi_device_event_types_[XI_TouchEnd] = true;
- }
return true;
}
@@ -235,7 +243,7 @@ bool DeviceDataManagerX11::IsXInput2Available() const {
return xi_opcode_ != -1;
}
-void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
+void DeviceDataManagerX11::UpdateDeviceList(x11::Connection* connection) {
cmt_devices_.reset();
touchpads_.reset();
master_pointers_.clear();
@@ -253,40 +261,41 @@ void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
// Find all the touchpad devices.
const XDeviceList& dev_list =
- ui::DeviceListCacheX11::GetInstance()->GetXDeviceList(display);
- x11::Atom xi_touchpad = gfx::GetAtom(XI_TOUCHPAD);
- for (int i = 0; i < dev_list.count; ++i)
- if (static_cast<x11::Atom>(dev_list[i].type) == xi_touchpad)
- touchpads_[dev_list[i].id] = true;
+ ui::DeviceListCacheX11::GetInstance()->GetXDeviceList(connection);
+ x11::Atom xi_touchpad = gfx::GetAtom("TOUCHPAD");
+ for (const auto& device : dev_list) {
+ if (device.device_type == xi_touchpad)
+ touchpads_[device.device_id] = true;
+ }
if (!IsXInput2Available())
return;
// Update the structs with new valuator information
const XIDeviceList& info_list =
- ui::DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display);
+ ui::DeviceListCacheX11::GetInstance()->GetXI2DeviceList(connection);
x11::Atom atoms[DT_LAST_ENTRY];
for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type)
atoms[data_type] = gfx::GetAtom(kCachedAtoms[data_type]);
- for (int i = 0; i < info_list.count; ++i) {
- const XIDeviceInfo& info = info_list[i];
-
- if (info.use == XIMasterPointer)
+ for (const auto& info : info_list) {
+ if (info.type == x11::Input::DeviceType::MasterPointer)
master_pointers_.push_back(info.deviceid);
// We currently handle only slave, non-keyboard devices
- if (info.use != XISlavePointer && info.use != XIFloatingSlave)
+ if (info.type != x11::Input::DeviceType::SlavePointer &&
+ info.type != x11::Input::DeviceType::FloatingSlave) {
continue;
+ }
bool possible_cmt = false;
bool not_cmt = false;
- const int deviceid = info.deviceid;
+ const auto deviceid = static_cast<uint16_t>(info.deviceid);
- for (int j = 0; j < info.num_classes; ++j) {
- if (info.classes[j]->type == XIValuatorClass)
+ for (const auto& device_class : info.classes) {
+ if (device_class.valuator.has_value())
++valuator_count_[deviceid];
- else if (info.classes[j]->type == XIScrollClass)
+ else if (device_class.scroll.has_value())
not_cmt = true;
}
@@ -299,15 +308,14 @@ void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
DT_LAST_ENTRY);
for (int j = 0; j < kMaxSlotNum; j++)
last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
- for (int j = 0; j < info.num_classes; ++j) {
- if (info.classes[j]->type == XIValuatorClass) {
- if (UpdateValuatorClassDevice(
- reinterpret_cast<XIValuatorClassInfo*>(info.classes[j]), atoms,
- deviceid))
+ for (const auto& device_class : info.classes) {
+ if (device_class.valuator.has_value()) {
+ if (UpdateValuatorClassDevice(*device_class.valuator, atoms,
+ deviceid)) {
possible_cmt = true;
- } else if (info.classes[j]->type == XIScrollClass) {
- UpdateScrollClassDevice(
- reinterpret_cast<XIScrollClassInfo*>(info.classes[j]), deviceid);
+ }
+ } else if (device_class.scroll.has_value()) {
+ UpdateScrollClassDevice(*device_class.scroll, deviceid);
}
}
@@ -316,38 +324,38 @@ void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
}
}
-bool DeviceDataManagerX11::GetSlotNumber(const XIDeviceEvent* xiev, int* slot) {
+bool DeviceDataManagerX11::GetSlotNumber(const x11::Input::DeviceEvent& xiev,
+ int* slot) {
ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
- if (!factory->IsMultiTouchDevice(xiev->sourceid)) {
+ if (!factory->IsMultiTouchDevice(xiev.sourceid)) {
*slot = 0;
return true;
}
- return factory->QuerySlotForTrackingID(xiev->detail, slot);
+ return factory->QuerySlotForTrackingID(xiev.detail, slot);
}
void DeviceDataManagerX11::GetEventRawData(const x11::Event& x11_event,
EventData* data) {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xiev = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xiev)
return;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- CHECK_GE(xiev->sourceid, 0);
- CHECK_GE(xiev->deviceid, 0);
- if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
+ auto sourceid = static_cast<uint16_t>(xiev->sourceid);
+ auto deviceid = static_cast<uint16_t>(xiev->deviceid);
+ if (sourceid >= kMaxDeviceNum || deviceid >= kMaxDeviceNum)
return;
data->clear();
- const int sourceid = xiev->sourceid;
- double* valuators = xiev->valuators.values;
+ const x11::Input::Fp3232* valuators = xiev->axisvalues.data();
for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
- if (XIMaskIsSet(xiev->valuators.mask, i)) {
+ if (IsXinputMaskSet(xiev->valuator_mask.data(), i)) {
int type = data_type_lookup_[sourceid][i];
if (type != DT_LAST_ENTRY) {
- (*data)[type] = *valuators;
+ double valuator = Fp3232ToDouble(*valuators);
+ (*data)[type] = valuator;
if (IsTouchDataType(type)) {
int slot = -1;
- if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
- last_seen_valuator_[sourceid][slot][type] = *valuators;
+ if (GetSlotNumber(*xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
+ last_seen_valuator_[sourceid][slot][type] = valuator;
}
}
valuators++;
@@ -358,24 +366,23 @@ void DeviceDataManagerX11::GetEventRawData(const x11::Event& x11_event,
bool DeviceDataManagerX11::GetEventData(const x11::Event& x11_event,
const DataType type,
double* value) {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xiev = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xiev)
return false;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- CHECK_GE(xiev->sourceid, 0);
- CHECK_GE(xiev->deviceid, 0);
- if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
+ auto sourceid = static_cast<uint16_t>(xiev->sourceid);
+ auto deviceid = static_cast<uint16_t>(xiev->deviceid);
+ if (sourceid >= kMaxDeviceNum || deviceid >= kMaxDeviceNum)
return false;
- const int sourceid = xiev->sourceid;
if (valuator_lookup_[sourceid].empty())
return false;
if (type == DT_TOUCH_TRACKING_ID) {
// With XInput2 MT, Tracking ID is provided in the detail field for touch
// events.
- if (xiev->evtype == XI_TouchBegin || xiev->evtype == XI_TouchEnd ||
- xiev->evtype == XI_TouchUpdate) {
+ if (xiev->opcode == x11::Input::DeviceEvent::TouchBegin ||
+ xiev->opcode == x11::Input::DeviceEvent::TouchEnd ||
+ xiev->opcode == x11::Input::DeviceEvent::TouchUpdate) {
*value = xiev->detail;
} else {
*value = 0;
@@ -386,20 +393,20 @@ bool DeviceDataManagerX11::GetEventData(const x11::Event& x11_event,
int val_index = valuator_lookup_[sourceid][type].number;
int slot = 0;
if (val_index >= 0) {
- if (XIMaskIsSet(xiev->valuators.mask, val_index)) {
- double* valuators = xiev->valuators.values;
+ if (IsXinputMaskSet(xiev->valuator_mask.data(), val_index)) {
+ const x11::Input::Fp3232* valuators = xiev->axisvalues.data();
while (val_index--) {
- if (XIMaskIsSet(xiev->valuators.mask, val_index))
+ if (IsXinputMaskSet(xiev->valuator_mask.data(), val_index))
++valuators;
}
- *value = *valuators;
+ *value = Fp3232ToDouble(*valuators);
if (IsTouchDataType(type)) {
- if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
+ if (GetSlotNumber(*xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
last_seen_valuator_[sourceid][slot][type] = *value;
}
return true;
} else if (IsTouchDataType(type)) {
- if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
+ if (GetSlotNumber(*xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
*value = last_seen_valuator_[sourceid][slot][type];
}
}
@@ -408,68 +415,54 @@ bool DeviceDataManagerX11::GetEventData(const x11::Event& x11_event,
}
bool DeviceDataManagerX11::IsXIDeviceEvent(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode ||
- xev.xcookie.extension != xi_opcode_)
- return false;
- return xi_device_event_types_[xev.xcookie.evtype];
+ return x11_event.As<x11::Input::DeviceEvent>();
}
bool DeviceDataManagerX11::IsTouchpadXInputEvent(
const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
- return false;
-
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- CHECK_GE(xievent->sourceid, 0);
- if (xievent->sourceid >= kMaxDeviceNum)
+ uint16_t source;
+ if (!GetSourceId(x11_event, &source))
return false;
- return touchpads_[xievent->sourceid];
+ return touchpads_[source];
}
bool DeviceDataManagerX11::IsCMTDeviceEvent(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
- return false;
-
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- CHECK_GE(xievent->sourceid, 0);
- if (xievent->sourceid >= kMaxDeviceNum)
+ uint16_t source;
+ if (!GetSourceId(x11_event, &source))
return false;
- return cmt_devices_[xievent->sourceid];
+ return cmt_devices_[source];
}
int DeviceDataManagerX11::GetScrollClassEventDetail(
const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xievent = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xievent)
return SCROLL_TYPE_NO_SCROLL;
-
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- if (xievent->sourceid >= kMaxDeviceNum)
+ auto sourceid = static_cast<uint16_t>(xievent->sourceid);
+ if (sourceid >= kMaxDeviceNum)
return SCROLL_TYPE_NO_SCROLL;
- int horizontal_id = scroll_data_[xievent->sourceid].horizontal.number;
- int vertical_id = scroll_data_[xievent->sourceid].vertical.number;
+ int horizontal_id = scroll_data_[sourceid].horizontal.number;
+ int vertical_id = scroll_data_[sourceid].vertical.number;
return (horizontal_id != -1 &&
- XIMaskIsSet(xievent->valuators.mask, horizontal_id)
+ IsXinputMaskSet(xievent->valuator_mask.data(), horizontal_id)
? SCROLL_TYPE_HORIZONTAL
: 0) |
- (vertical_id != -1 && XIMaskIsSet(xievent->valuators.mask, vertical_id)
+ (vertical_id != -1 &&
+ IsXinputMaskSet(xievent->valuator_mask.data(), vertical_id)
? SCROLL_TYPE_VERTICAL
: 0);
}
int DeviceDataManagerX11::GetScrollClassDeviceDetail(
const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xiev = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xiev)
return SCROLL_TYPE_NO_SCROLL;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
+ auto sourceid = static_cast<uint16_t>(xiev->sourceid);
+ auto deviceid = static_cast<uint16_t>(xiev->deviceid);
+ if (sourceid >= kMaxDeviceNum || deviceid >= kMaxDeviceNum)
return SCROLL_TYPE_NO_SCROLL;
- const int sourceid = xiev->sourceid;
const ScrollInfo& device_data = scroll_data_[sourceid];
return (device_data.vertical.number >= 0 ? SCROLL_TYPE_VERTICAL : 0) |
(device_data.horizontal.number >= 0 ? SCROLL_TYPE_HORIZONTAL : 0);
@@ -479,58 +472,53 @@ bool DeviceDataManagerX11::IsCMTGestureEvent(const x11::Event& xev) const {
return (IsScrollEvent(xev) || IsFlingEvent(xev) || IsCMTMetricsEvent(xev));
}
-bool DeviceDataManagerX11::HasEventData(const XIDeviceEvent* xiev,
+bool DeviceDataManagerX11::HasEventData(const x11::Event& xev,
const DataType type) const {
- CHECK_GE(xiev->sourceid, 0);
- if (xiev->sourceid >= kMaxDeviceNum)
+ auto* xiev = xev.As<x11::Input::DeviceEvent>();
+ if (!xiev)
+ return false;
+ auto sourceid = static_cast<uint16_t>(xiev->sourceid);
+ if (sourceid >= kMaxDeviceNum)
return false;
- if (type >= valuator_lookup_[xiev->sourceid].size())
+ if (type >= valuator_lookup_[sourceid].size())
return false;
- const int idx = valuator_lookup_[xiev->sourceid][type].number;
- return (idx >= 0) && XIMaskIsSet(xiev->valuators.mask, idx);
+ const int idx = valuator_lookup_[sourceid][type].number;
+ return (idx >= 0) && IsXinputMaskSet(xiev->valuator_mask.data(), idx);
}
bool DeviceDataManagerX11::IsScrollEvent(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
if (!IsCMTDeviceEvent(x11_event))
return false;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return (HasEventData(xiev, DT_CMT_SCROLL_X) ||
- HasEventData(xiev, DT_CMT_SCROLL_Y));
+ return (HasEventData(x11_event, DT_CMT_SCROLL_X) ||
+ HasEventData(x11_event, DT_CMT_SCROLL_Y));
}
bool DeviceDataManagerX11::IsFlingEvent(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
if (!IsCMTDeviceEvent(x11_event))
return false;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return (HasEventData(xiev, DT_CMT_FLING_X) &&
- HasEventData(xiev, DT_CMT_FLING_Y) &&
- HasEventData(xiev, DT_CMT_FLING_STATE));
+ return (HasEventData(x11_event, DT_CMT_FLING_X) &&
+ HasEventData(x11_event, DT_CMT_FLING_Y) &&
+ HasEventData(x11_event, DT_CMT_FLING_STATE));
}
bool DeviceDataManagerX11::IsCMTMetricsEvent(
const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
if (!IsCMTDeviceEvent(x11_event))
return false;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return (HasEventData(xiev, DT_CMT_METRICS_TYPE) &&
- HasEventData(xiev, DT_CMT_METRICS_DATA1) &&
- HasEventData(xiev, DT_CMT_METRICS_DATA2));
+ return (HasEventData(x11_event, DT_CMT_METRICS_TYPE) &&
+ HasEventData(x11_event, DT_CMT_METRICS_DATA1) &&
+ HasEventData(x11_event, DT_CMT_METRICS_DATA2));
}
bool DeviceDataManagerX11::HasGestureTimes(const x11::Event& x11_event) const {
- const XEvent& xev = x11_event.xlib_event();
if (!IsCMTDeviceEvent(x11_event))
return false;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return (HasEventData(xiev, DT_CMT_START_TIME) &&
- HasEventData(xiev, DT_CMT_END_TIME));
+ return (HasEventData(x11_event, DT_CMT_START_TIME) &&
+ HasEventData(x11_event, DT_CMT_END_TIME));
}
void DeviceDataManagerX11::GetScrollOffsets(const x11::Event& xev,
@@ -563,20 +551,20 @@ void DeviceDataManagerX11::GetScrollOffsets(const x11::Event& xev,
void DeviceDataManagerX11::GetScrollClassOffsets(const x11::Event& x11_event,
double* x_offset,
double* y_offset) {
- const XEvent& xev = x11_event.xlib_event();
DCHECK_NE(SCROLL_TYPE_NO_SCROLL, GetScrollClassDeviceDetail(x11_event));
*x_offset = 0;
*y_offset = 0;
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xiev = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xiev)
return;
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
+ auto sourceid = static_cast<uint16_t>(xiev->sourceid);
+ auto deviceid = static_cast<uint16_t>(xiev->deviceid);
+ if (sourceid >= kMaxDeviceNum || deviceid >= kMaxDeviceNum)
return;
- const int sourceid = xiev->sourceid;
- double* valuators = xiev->valuators.values;
+ const x11::Input::Fp3232* valuators = xiev->axisvalues.data();
ScrollInfo* info = &scroll_data_[sourceid];
@@ -584,27 +572,29 @@ void DeviceDataManagerX11::GetScrollClassOffsets(const x11::Event& x11_event,
const int vertical_number = info->vertical.number;
for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
- if (!XIMaskIsSet(xiev->valuators.mask, i))
+ if (!IsXinputMaskSet(xiev->valuator_mask.data(), i))
continue;
- if (i == horizontal_number) {
- *x_offset = ExtractAndUpdateScrollOffset(&info->horizontal, *valuators);
- } else if (i == vertical_number) {
- *y_offset = ExtractAndUpdateScrollOffset(&info->vertical, *valuators);
- }
+ auto valuator = Fp3232ToDouble(*valuators);
+ if (i == horizontal_number)
+ *x_offset = ExtractAndUpdateScrollOffset(&info->horizontal, valuator);
+ else if (i == vertical_number)
+ *y_offset = ExtractAndUpdateScrollOffset(&info->vertical, valuator);
valuators++;
}
}
-void DeviceDataManagerX11::InvalidateScrollClasses(int device_id) {
+void DeviceDataManagerX11::InvalidateScrollClasses(
+ x11::Input::DeviceId device_id) {
if (device_id == kAllDevices) {
for (auto& i : scroll_data_) {
i.horizontal.seen = false;
i.vertical.seen = false;
}
} else {
- CHECK(device_id >= 0 && device_id < kMaxDeviceNum);
- scroll_data_[device_id].horizontal.seen = false;
- scroll_data_[device_id].vertical.seen = false;
+ auto device16 = static_cast<uint16_t>(device_id);
+ CHECK_LT(device16, kMaxDeviceNum);
+ scroll_data_[device16].horizontal.seen = false;
+ scroll_data_[device16].vertical.seen = false;
}
}
@@ -660,13 +650,14 @@ void DeviceDataManagerX11::GetMetricsData(const x11::Event& xev,
}
int DeviceDataManagerX11::GetMappedButton(int button) {
- return button > 0 && button <= button_map_count_ ? button_map_[button - 1]
- : button;
+ return button > 0 && static_cast<unsigned int>(button) <= button_map_.size()
+ ? button_map_[button - 1]
+ : button;
}
void DeviceDataManagerX11::UpdateButtonMap() {
- button_map_count_ = XGetPointerMapping(gfx::GetXDisplay(), button_map_,
- base::size(button_map_));
+ if (auto reply = x11::Connection::Get()->GetPointerMapping({}).Sync())
+ button_map_ = std::move(reply->map);
}
void DeviceDataManagerX11::GetGestureTimes(const x11::Event& xev,
@@ -684,7 +675,7 @@ void DeviceDataManagerX11::GetGestureTimes(const x11::Event& xev,
*end_time = data[DT_CMT_END_TIME];
}
-bool DeviceDataManagerX11::NormalizeData(int deviceid,
+bool DeviceDataManagerX11::NormalizeData(x11::Input::DeviceId deviceid,
const DataType type,
double* value) {
double max_value;
@@ -697,18 +688,18 @@ bool DeviceDataManagerX11::NormalizeData(int deviceid,
return false;
}
-bool DeviceDataManagerX11::GetDataRange(int deviceid,
+bool DeviceDataManagerX11::GetDataRange(x11::Input::DeviceId deviceid,
const DataType type,
double* min,
double* max) {
- CHECK_GE(deviceid, 0);
- if (deviceid >= kMaxDeviceNum)
+ auto device16 = static_cast<uint16_t>(deviceid);
+ if (device16 >= kMaxDeviceNum)
return false;
- if (valuator_lookup_[deviceid].empty())
+ if (valuator_lookup_[device16].empty())
return false;
- if (valuator_lookup_[deviceid][type].number >= 0) {
- *min = valuator_lookup_[deviceid][type].min;
- *max = valuator_lookup_[deviceid][type].max;
+ if (valuator_lookup_[device16][type].number >= 0) {
+ *min = valuator_lookup_[device16][type].min;
+ *max = valuator_lookup_[device16][type].max;
return true;
}
return false;
@@ -726,69 +717,46 @@ void DeviceDataManagerX11::SetDeviceListForTest(
last_seen_valuator_[i][j].clear();
}
- for (int deviceid : touchscreen) {
+ for (auto deviceid : touchscreen) {
InitializeValuatorsForTest(deviceid, kTouchDataTypeStart, kTouchDataTypeEnd,
0, 1000);
}
cmt_devices_.reset();
- for (int deviceid : cmt_devices) {
+ for (auto deviceid : cmt_devices) {
cmt_devices_[deviceid] = true;
touchpads_[deviceid] = true;
InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd,
-1000, 1000);
}
- for (int deviceid : other_devices) {
+ for (auto deviceid : other_devices) {
InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd,
-1000, 1000);
}
}
void DeviceDataManagerX11::SetValuatorDataForTest(
- XIDeviceEvent* xievent,
x11::Input::DeviceEvent* devev,
DataType type,
double value) {
- // Modify |xievent|.
- {
- int index = valuator_lookup_[xievent->deviceid][type].number;
- CHECK(!XIMaskIsSet(xievent->valuators.mask, index));
- CHECK(index >= 0 && index < valuator_count_[xievent->deviceid]);
- XISetMask(xievent->valuators.mask, index);
-
- double* valuators = xievent->valuators.values;
- for (int i = 0; i < index; ++i) {
- if (XIMaskIsSet(xievent->valuators.mask, i))
- valuators++;
- }
- for (int i = DT_LAST_ENTRY - 1; i > valuators - xievent->valuators.values;
- --i) {
- xievent->valuators.values[i] = xievent->valuators.values[i - 1];
- }
- *valuators = value;
+ uint16_t device = static_cast<uint16_t>(devev->deviceid);
+ int index = valuator_lookup_[device][type].number;
+ CHECK(!IsXinputMaskSet(devev->valuator_mask.data(), index));
+ CHECK(index >= 0 && index < valuator_count_[device]);
+ SetXinputMask(devev->valuator_mask.data(), index);
+
+ x11::Input::Fp3232* valuators = devev->axisvalues.data();
+ for (int i = 0; i < index; ++i) {
+ if (IsXinputMaskSet(devev->valuator_mask.data(), i))
+ valuators++;
}
-
- // Modify |devev|.
- {
- uint16_t device = static_cast<uint16_t>(devev->deviceid);
- int index = valuator_lookup_[device][type].number;
- CHECK(!XIMaskIsSet(devev->valuator_mask.data(), index));
- CHECK(index >= 0 && index < valuator_count_[device]);
- XISetMask(devev->valuator_mask.data(), index);
-
- x11::Input::Fp3232* valuators = devev->axisvalues.data();
- for (int i = 0; i < index; ++i) {
- if (XIMaskIsSet(devev->valuator_mask.data(), i))
- valuators++;
- }
- for (int i = DT_LAST_ENTRY - 1; i > valuators - devev->axisvalues.data();
- --i) {
- devev->axisvalues[i] = devev->axisvalues[i - 1];
- }
-
- *valuators = DoubleToFp3232(value);
+ for (int i = DT_LAST_ENTRY - 1; i > valuators - devev->axisvalues.data();
+ --i) {
+ devev->axisvalues[i] = devev->axisvalues[i - 1];
}
+
+ *valuators = DoubleToFp3232(value);
}
void DeviceDataManagerX11::InitializeValuatorsForTest(int deviceid,
@@ -811,46 +779,44 @@ void DeviceDataManagerX11::InitializeValuatorsForTest(int deviceid,
}
bool DeviceDataManagerX11::UpdateValuatorClassDevice(
- XIValuatorClassInfo* valuator_class_info,
+ const x11::Input::DeviceClass::Valuator& valuator_class_info,
x11::Atom* atoms,
- int deviceid) {
- DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
+ uint16_t deviceid) {
+ DCHECK_LT(deviceid, kMaxDeviceNum);
x11::Atom* label =
- std::find(atoms, atoms + DT_LAST_ENTRY,
- static_cast<x11::Atom>(valuator_class_info->label));
- if (label == atoms + DT_LAST_ENTRY) {
+ std::find(atoms, atoms + DT_LAST_ENTRY, valuator_class_info.label);
+ if (label == atoms + DT_LAST_ENTRY)
return false;
- }
int data_type = label - atoms;
DCHECK_GE(data_type, 0);
DCHECK_LT(data_type, DT_LAST_ENTRY);
auto& valuator_info = valuator_lookup_[deviceid][data_type];
- valuator_info.number = valuator_class_info->number;
- valuator_info.min = valuator_class_info->min;
- valuator_info.max = valuator_class_info->max;
- data_type_lookup_[deviceid][valuator_class_info->number] = data_type;
+ valuator_info.number = valuator_class_info.number;
+ valuator_info.min = Fp3232ToDouble(valuator_class_info.min);
+ valuator_info.max = Fp3232ToDouble(valuator_class_info.max);
+ data_type_lookup_[deviceid][valuator_class_info.number] = data_type;
return IsCMTDataType(data_type);
}
void DeviceDataManagerX11::UpdateScrollClassDevice(
- XIScrollClassInfo* scroll_class_info,
- int deviceid) {
+ const x11::Input::DeviceClass::Scroll& scroll_class_info,
+ uint16_t deviceid) {
if (high_precision_scrolling_disabled_)
return;
DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
ScrollInfo& info = scroll_data_[deviceid];
- switch (scroll_class_info->scroll_type) {
- case XIScrollTypeVertical:
- info.vertical.number = scroll_class_info->number;
- info.vertical.increment = scroll_class_info->increment;
+ switch (scroll_class_info.scroll_type) {
+ case x11::Input::ScrollType::Vertical:
+ info.vertical.number = scroll_class_info.number;
+ info.vertical.increment = Fp3232ToDouble(scroll_class_info.increment);
info.vertical.position = 0;
info.vertical.seen = false;
break;
- case XIScrollTypeHorizontal:
- info.horizontal.number = scroll_class_info->number;
- info.horizontal.increment = scroll_class_info->increment;
+ case x11::Input::ScrollType::Horizontal:
+ info.horizontal.number = scroll_class_info.number;
+ info.horizontal.increment = Fp3232ToDouble(scroll_class_info.increment);
info.horizontal.position = 0;
info.horizontal.seen = false;
break;
@@ -874,21 +840,20 @@ void DeviceDataManagerX11::SetDisabledKeyboardAllowedKeys(
blocked_keyboard_allowed_keys_ = std::move(excepted_keys);
}
-void DeviceDataManagerX11::DisableDevice(int deviceid) {
- blocked_devices_.set(deviceid, true);
+void DeviceDataManagerX11::DisableDevice(x11::Input::DeviceId deviceid) {
+ blocked_devices_.set(static_cast<uint32_t>(deviceid), true);
// TODO(rsadam@): Support blocking touchscreen devices.
std::vector<InputDevice> keyboards = GetKeyboardDevices();
auto it = FindDeviceWithId(keyboards.begin(), keyboards.end(), deviceid);
if (it != std::end(keyboards)) {
- blocked_keyboard_devices_.insert(
- std::pair<int, InputDevice>(deviceid, *it));
+ blocked_keyboard_devices_.emplace(deviceid, *it);
keyboards.erase(it);
DeviceDataManager::OnKeyboardDevicesUpdated(keyboards);
}
}
-void DeviceDataManagerX11::EnableDevice(int deviceid) {
- blocked_devices_.set(deviceid, false);
+void DeviceDataManagerX11::EnableDevice(x11::Input::DeviceId deviceid) {
+ blocked_devices_.set(static_cast<uint32_t>(deviceid), false);
auto it = blocked_keyboard_devices_.find(deviceid);
if (it != blocked_keyboard_devices_.end()) {
std::vector<InputDevice> devices = GetKeyboardDevices();
@@ -899,26 +864,25 @@ void DeviceDataManagerX11::EnableDevice(int deviceid) {
}
}
-bool DeviceDataManagerX11::IsDeviceEnabled(int device_id) const {
- return blocked_devices_.test(device_id);
+bool DeviceDataManagerX11::IsDeviceEnabled(
+ x11::Input::DeviceId device_id) const {
+ return blocked_devices_.test(static_cast<uint32_t>(device_id));
}
bool DeviceDataManagerX11::IsEventBlocked(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- // Only check XI2 events which have a source device id.
- if (xev.type != x11::GeGenericEvent::opcode)
+ auto* xievent = x11_event.As<x11::Input::DeviceEvent>();
+ if (!xievent)
return false;
-
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
// Allow any key events from blocked_keyboard_allowed_keys_.
if (blocked_keyboard_allowed_keys_ &&
- (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) &&
- blocked_keyboard_allowed_keys_->find(KeyboardCodeFromXKeyEvent(&xev)) !=
- blocked_keyboard_allowed_keys_->end()) {
+ (xievent->opcode == x11::Input::DeviceEvent::KeyPress ||
+ xievent->opcode == x11::Input::DeviceEvent::KeyRelease) &&
+ blocked_keyboard_allowed_keys_->find(KeyboardCodeFromXKeyEvent(
+ x11_event)) != blocked_keyboard_allowed_keys_->end()) {
return false;
}
- return blocked_devices_.test(xievent->sourceid);
+ return blocked_devices_.test(static_cast<uint16_t>(xievent->sourceid));
}
void DeviceDataManagerX11::OnKeyboardDevicesUpdated(
@@ -927,12 +891,12 @@ void DeviceDataManagerX11::OnKeyboardDevicesUpdated(
for (auto blocked_iter = blocked_keyboard_devices_.begin();
blocked_iter != blocked_keyboard_devices_.end();) {
// Check if the blocked device still exists in list of devices.
- int device_id = blocked_iter->first;
+ x11::Input::DeviceId device_id = blocked_iter->first;
auto it = FindDeviceWithId(keyboards.begin(), keyboards.end(), device_id);
// If the device no longer exists, unblock it, else filter it out from our
// active list.
if (it == keyboards.end()) {
- blocked_devices_.set((*blocked_iter).first, false);
+ blocked_devices_.set(static_cast<uint32_t>((*blocked_iter).first), false);
blocked_keyboard_devices_.erase(blocked_iter++);
} else {
keyboards.erase(it);
diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.h b/chromium/ui/events/devices/x11/device_data_manager_x11.h
index a415db641fb..30b97cfd64d 100644
--- a/chromium/ui/events/devices/x11/device_data_manager_x11.h
+++ b/chromium/ui/events/devices/x11/device_data_manager_x11.h
@@ -103,7 +103,7 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
// A Device ID number that can be passed to InvalidateScrollClasses that
// invalidates all devices.
- static const int kAllDevices = -1;
+ static constexpr auto kAllDevices = static_cast<x11::Input::DeviceId>(-1);
// Data struct to store extracted data from an input event.
typedef std::map<int, double> EventData;
@@ -121,16 +121,16 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
bool IsXInput2Available() const;
// Updates the list of devices.
- void UpdateDeviceList(Display* display);
+ void UpdateDeviceList(x11::Connection* connection);
// For multitouch events we use slot number to distinguish touches from
// different fingers. This function returns true if the associated slot
// for |xiev| can be found and it is saved in |slot|, returns false if
// no slot can be found.
- bool GetSlotNumber(const XIDeviceEvent* xiev, int* slot);
+ bool GetSlotNumber(const x11::Input::DeviceEvent& xiev, int* slot);
// Check if an XI event contains data of the specified type.
- bool HasEventData(const XIDeviceEvent* xiev, const DataType type) const;
+ bool HasEventData(const x11::Event& xev, const DataType type) const;
// Get all event data in one pass. We extract only data types that we know
// about (defined in enum DataType). The data is not processed (e.g. not
@@ -198,7 +198,7 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
// Invalidate stored scroll class counters, since they can change when
// pointing at other windows. If kAllDevices is specified, all devices are
// invalidated.
- void InvalidateScrollClasses(int device_id);
+ void InvalidateScrollClasses(x11::Input::DeviceId device_id);
// Extract data from a fling event. User must first verify the event type
// with IsFlingEvent. Pointers shouldn't be NULL.
@@ -233,11 +233,13 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
// *value = (*value - min_value_of_tp) / (max_value_of_tp - min_value_of_tp)
// Returns true and sets the normalized value in|value| if normalization is
// successful. Returns false and |value| is unchanged otherwise.
- bool NormalizeData(int deviceid, const DataType type, double* value);
+ bool NormalizeData(x11::Input::DeviceId deviceid,
+ const DataType type,
+ double* value);
// Extract the range of the data type. Return true if the range is available
// and written into min & max, false if the range is not available.
- bool GetDataRange(int deviceid,
+ bool GetDataRange(x11::Input::DeviceId deviceid,
const DataType type,
double* min,
double* max);
@@ -250,8 +252,7 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
const std::vector<int>& cmt_devices,
const std::vector<int>& other_devices);
- void SetValuatorDataForTest(XIDeviceEvent* xievent,
- x11::Input::DeviceEvent* devev,
+ void SetValuatorDataForTest(x11::Input::DeviceEvent* devev,
DataType type,
double value);
@@ -260,15 +261,17 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
std::unique_ptr<std::set<KeyboardCode>> excepted_keys);
// Disables and enables events from devices by device id.
- void DisableDevice(int deviceid);
- void EnableDevice(int deviceid);
+ void DisableDevice(x11::Input::DeviceId deviceid);
+ void EnableDevice(x11::Input::DeviceId deviceid);
- bool IsDeviceEnabled(int device_id) const;
+ bool IsDeviceEnabled(x11::Input::DeviceId device_id) const;
// Returns true if |native_event| should be blocked.
bool IsEventBlocked(const x11::Event& xev);
- const std::vector<int>& master_pointers() const { return master_pointers_; }
+ const std::vector<x11::Input::DeviceId>& master_pointers() const {
+ return master_pointers_;
+ }
protected:
// DeviceHotplugEventObserver:
@@ -317,13 +320,15 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
// Updates a device based on a Valuator class info. Returns true if the
// device is a possible CMT device.
- bool UpdateValuatorClassDevice(XIValuatorClassInfo* valuator_class_info,
- x11::Atom* atoms,
- int deviceid);
+ bool UpdateValuatorClassDevice(
+ const x11::Input::DeviceClass::Valuator& valuator_class_info,
+ x11::Atom* atoms,
+ uint16_t deviceid);
// Updates a device based on a Scroll class info.
- void UpdateScrollClassDevice(XIScrollClassInfo* scroll_class_info,
- int deviceid);
+ void UpdateScrollClassDevice(
+ const x11::Input::DeviceClass::Scroll& scroll_class_info,
+ uint16_t deviceid);
// Normalize the scroll amount according to the increment size.
// *value /= increment
@@ -334,22 +339,19 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
DeviceDataManagerX11::ScrollInfo::AxisInfo* axis,
double valuator) const;
- static const int kMaxXIEventType = XI_LASTEVENT + 1;
+ static const int kMaxXIEventType = 32;
static const int kMaxSlotNum = 10;
// Major opcode for the XInput extension. Used to identify XInput events.
int xi_opcode_;
- // A quick lookup table for determining if the XI event is an XIDeviceEvent.
- std::bitset<kMaxXIEventType> xi_device_event_types_;
-
// A quick lookup table for determining if events from the pointer device
// should be processed.
std::bitset<kMaxDeviceNum> cmt_devices_;
std::bitset<kMaxDeviceNum> touchpads_;
// List of the master pointer devices.
- std::vector<int> master_pointers_;
+ std::vector<x11::Input::DeviceId> master_pointers_;
// A quick lookup table for determining if events from the XI device
// should be blocked.
@@ -387,10 +389,9 @@ class EVENTS_DEVICES_X11_EXPORT DeviceDataManagerX11
// Map that stores meta-data for blocked keyboards. This is needed to restore
// devices when they are re-enabled.
- std::map<int, ui::InputDevice> blocked_keyboard_devices_;
+ std::map<x11::Input::DeviceId, ui::InputDevice> blocked_keyboard_devices_;
- unsigned char button_map_[256];
- int button_map_count_;
+ std::vector<uint8_t> button_map_;
DISALLOW_COPY_AND_ASSIGN(DeviceDataManagerX11);
};
diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11_unittest.cc b/chromium/ui/events/devices/x11/device_data_manager_x11_unittest.cc
deleted file mode 100644
index 96bb913a957..00000000000
--- a/chromium/ui/events/devices/x11/device_data_manager_x11_unittest.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/events/devices/x11/device_data_manager_x11.h"
-
-#include <vector>
-
-#include "base/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/devices/device_hotplug_event_observer.h"
-#include "ui/events/devices/input_device.h"
-#include "ui/events/devices/input_device_event_observer.h"
-#include "ui/events/devices/touchscreen_device.h"
-
-namespace ui {
-namespace test {
-namespace {
-
-class TestInputDeviceObserver : public InputDeviceEventObserver {
- public:
- explicit TestInputDeviceObserver(DeviceDataManagerX11* manager)
- : manager_(manager), change_notified_(false) {
- if (manager_)
- manager_->AddObserver(this);
- }
-
- ~TestInputDeviceObserver() override {
- if (manager_)
- manager_->RemoveObserver(this);
- }
-
- // InputDeviceEventObserver implementation.
- void OnInputDeviceConfigurationChanged(uint8_t) override {
- change_notified_ = true;
- }
-
- int change_notified() const { return change_notified_; }
- void Reset() { change_notified_ = false; }
-
- private:
- DeviceDataManager* manager_;
- bool change_notified_;
-
- DISALLOW_COPY_AND_ASSIGN(TestInputDeviceObserver);
-};
-
-} // namespace
-
-class DeviceDataManagerX11Test : public testing::Test {
- public:
- DeviceDataManagerX11Test() {}
- ~DeviceDataManagerX11Test() override {}
-
- void SetUp() override { DeviceDataManagerX11::CreateInstance(); }
-
- void TearDown() override { SetKeyboardDevices(std::vector<InputDevice>()); }
-
- virtual void SetKeyboardDevices(const std::vector<InputDevice>& devices) {
- DeviceHotplugEventObserver* manager = DeviceDataManagerX11::GetInstance();
- manager->OnKeyboardDevicesUpdated(devices);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DeviceDataManagerX11Test);
-};
-
-// Tests that the the device data manager notifies observers when a device is
-// disabled and re-enabled.
-TEST_F(DeviceDataManagerX11Test, NotifyOnDisable) {
- DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
- TestInputDeviceObserver observer(manager);
- std::vector<ui::InputDevice> keyboards;
- keyboards.push_back(ui::InputDevice(
- 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- keyboards.push_back(ui::InputDevice(
- 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- SetKeyboardDevices(keyboards);
- EXPECT_TRUE(observer.change_notified());
- std::vector<InputDevice> devices = manager->GetKeyboardDevices();
- EXPECT_EQ(keyboards.size(), devices.size());
- observer.Reset();
- // Disable the device, should be notified that the device list contains one
- // less device.
- manager->DisableDevice(2);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(1u, devices.size());
- InputDevice device = devices.front();
- EXPECT_EQ(1, device.id);
- observer.Reset();
- // Reenable the device, should be notified that the device list contains one
- // more device.
- manager->EnableDevice(2);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(keyboards.size(), devices.size());
-}
-
-// Tests blocking multiple devices.
-TEST_F(DeviceDataManagerX11Test, TestMultipleDisable) {
- DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
- TestInputDeviceObserver observer(manager);
- std::vector<ui::InputDevice> keyboards;
- keyboards.push_back(ui::InputDevice(
- 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- keyboards.push_back(ui::InputDevice(
- 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- SetKeyboardDevices(keyboards);
- EXPECT_TRUE(observer.change_notified());
- std::vector<InputDevice> devices = manager->GetKeyboardDevices();
- EXPECT_EQ(keyboards.size(), devices.size());
- observer.Reset();
- // Disable the device, should be notified that the device list contains one
- // less device.
- manager->DisableDevice(1);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(1u, devices.size());
- observer.Reset();
- // Disable the second device, should be notified that the device list empty.
- manager->DisableDevice(2);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(0u, devices.size());
- observer.Reset();
- // Enable the first device, should be notified that one device present.
- manager->EnableDevice(1);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(1u, devices.size());
- observer.Reset();
- // Enable the second device, should be notified that both devices present.
- manager->EnableDevice(2);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(2u, devices.size());
-}
-
-TEST_F(DeviceDataManagerX11Test, UnblockOnDeviceUnplugged) {
- DeviceDataManagerX11* manager = DeviceDataManagerX11::GetInstance();
- TestInputDeviceObserver observer(manager);
- std::vector<ui::InputDevice> all_keyboards;
- all_keyboards.push_back(ui::InputDevice(
- 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- all_keyboards.push_back(ui::InputDevice(
- 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- SetKeyboardDevices(all_keyboards);
- EXPECT_TRUE(observer.change_notified());
- std::vector<InputDevice> devices = manager->GetKeyboardDevices();
- EXPECT_EQ(all_keyboards.size(), devices.size());
- observer.Reset();
- // Expect to be notified that the device is no longer available.
- manager->DisableDevice(2);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- EXPECT_EQ(1u, devices.size());
- observer.Reset();
- // Unplug the disabled device. Should not be notified, since the active list
- // did not change.
- std::vector<ui::InputDevice> subset_keyboards;
- subset_keyboards.push_back(ui::InputDevice(
- 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard"));
- SetKeyboardDevices(subset_keyboards);
- EXPECT_FALSE(observer.change_notified());
- // Replug in the first device. Should be notified of the new device.
- SetKeyboardDevices(all_keyboards);
- EXPECT_TRUE(observer.change_notified());
- devices = manager->GetKeyboardDevices();
- // Both devices now present.
- EXPECT_EQ(2u, devices.size());
-}
-
-} // namespace test
-} // namespace ui
diff --git a/chromium/ui/events/devices/x11/device_list_cache_x11.cc b/chromium/ui/events/devices/x11/device_list_cache_x11.cc
index 2d471870cb8..e0f7e19b6c3 100644
--- a/chromium/ui/events/devices/x11/device_list_cache_x11.cc
+++ b/chromium/ui/events/devices/x11/device_list_cache_x11.cc
@@ -9,58 +9,39 @@
#include "base/memory/singleton.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
-namespace {
-
-bool IsXI2Available() {
-#if defined(USE_AURA)
- return ui::DeviceDataManagerX11::GetInstance()->IsXInput2Available();
-#else
- return false;
-#endif
-}
-
-}
-
namespace ui {
-DeviceListCacheX11::DeviceListCacheX11() {
-}
+DeviceListCacheX11::DeviceListCacheX11() = default;
-DeviceListCacheX11::~DeviceListCacheX11() {
-}
+DeviceListCacheX11::~DeviceListCacheX11() = default;
DeviceListCacheX11* DeviceListCacheX11::GetInstance() {
return base::Singleton<DeviceListCacheX11>::get();
}
-void DeviceListCacheX11::UpdateDeviceList(Display* display) {
- XDeviceList& new_x_dev_list = x_dev_list_;
- new_x_dev_list.devices.reset(
- XListInputDevices(display, &new_x_dev_list.count));
-
- XIDeviceList& new_xi_dev_list = xi_dev_list_;
- new_xi_dev_list.devices.reset(
- IsXI2Available()
- ? XIQueryDevice(display, XIAllDevices, &new_xi_dev_list.count)
- : nullptr);
+void DeviceListCacheX11::UpdateDeviceList(x11::Connection* connection) {
+ auto x_future = connection->xinput().ListInputDevices({});
+ auto xi_future = connection->xinput().XIQueryDevice({});
+ connection->Flush();
+ if (auto reply = x_future.Sync())
+ x_dev_list_ = reply->devices;
+ if (auto reply = xi_future.Sync())
+ xi_dev_list_ = reply->infos;
+ updated_ = true;
}
-const XDeviceList& DeviceListCacheX11::GetXDeviceList(Display* display) {
- XDeviceList& x_dev_list = x_dev_list_;
- // Note that the function can be called before any update has taken place.
- if (!x_dev_list.devices && !x_dev_list.count)
- x_dev_list.devices.reset(XListInputDevices(display, &x_dev_list.count));
- return x_dev_list;
+const XDeviceList& DeviceListCacheX11::GetXDeviceList(
+ x11::Connection* connection) {
+ if (!updated_)
+ UpdateDeviceList(connection);
+ return x_dev_list_;
}
-const XIDeviceList& DeviceListCacheX11::GetXI2DeviceList(Display* display) {
- XIDeviceList& xi_dev_list = xi_dev_list_;
- if (!xi_dev_list.devices && !xi_dev_list.count) {
- xi_dev_list.devices.reset(
- XIQueryDevice(display, XIAllDevices, &xi_dev_list.count));
- }
- return xi_dev_list;
+const XIDeviceList& DeviceListCacheX11::GetXI2DeviceList(
+ x11::Connection* connection) {
+ if (!updated_)
+ UpdateDeviceList(connection);
+ return xi_dev_list_;
}
} // namespace ui
-
diff --git a/chromium/ui/events/devices/x11/device_list_cache_x11.h b/chromium/ui/events/devices/x11/device_list_cache_x11.h
index a5810533f8d..b9c0c3dd2bc 100644
--- a/chromium/ui/events/devices/x11/device_list_cache_x11.h
+++ b/chromium/ui/events/devices/x11/device_list_cache_x11.h
@@ -10,47 +10,38 @@
#include "base/macros.h"
#include "ui/events/devices/x11/events_devices_x11_export.h"
-#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xinput.h"
namespace base {
-template <typename T> struct DefaultSingletonTraits;
+template <typename T>
+struct DefaultSingletonTraits;
}
-typedef struct _XDisplay Display;
-
-template <typename T, void (*D)(T*)>
-struct DeviceList {
- DeviceList() : count(0) {}
- T& operator[](int x) { return devices[x]; }
- const T& operator[](int x) const { return devices[x]; }
- std::unique_ptr<T[], gfx::XObjectDeleter<T, void, D>> devices;
- int count;
-};
-
-typedef struct DeviceList<XDeviceInfo, XFreeDeviceList> XDeviceList;
-typedef struct DeviceList<XIDeviceInfo, XIFreeDeviceInfo> XIDeviceList;
+using XDeviceList = std::vector<x11::Input::DeviceInfo>;
+using XIDeviceList = std::vector<x11::Input::XIDeviceInfo>;
namespace ui {
// A class to cache the current XInput device list. This minimized the
// round-trip time to the X server whenever such a device list is needed. The
-// update function will be called on each incoming XI_HierarchyChanged event.
+// update function will be called on each incoming
+// x11::Input::HierarchyEvent::opcode event.
class EVENTS_DEVICES_X11_EXPORT DeviceListCacheX11 {
public:
static DeviceListCacheX11* GetInstance();
- void UpdateDeviceList(Display* display);
+ void UpdateDeviceList(x11::Connection* connection);
// Returns the list of devices associated with |display|. Uses the old X11
// protocol to get the list of the devices.
- const XDeviceList& GetXDeviceList(Display* display);
+ const XDeviceList& GetXDeviceList(x11::Connection* connection);
// Returns the list of devices associated with |display|. Uses the newer
// XINPUT2 protocol to get the list of devices. Before making this call, make
// sure that XInput2 support is available (e.g. by calling
// IsXInput2Available()).
- const XIDeviceList& GetXI2DeviceList(Display* display);
+ const XIDeviceList& GetXI2DeviceList(x11::Connection* connection);
private:
friend struct base::DefaultSingletonTraits<DeviceListCacheX11>;
@@ -58,6 +49,7 @@ class EVENTS_DEVICES_X11_EXPORT DeviceListCacheX11 {
DeviceListCacheX11();
~DeviceListCacheX11();
+ bool updated_ = false;
XDeviceList x_dev_list_;
XIDeviceList xi_dev_list_;
@@ -67,4 +59,3 @@ class EVENTS_DEVICES_X11_EXPORT DeviceListCacheX11 {
} // namespace ui
#endif // UI_EVENTS_DEVICES_X11_DEVICE_LIST_CACHE_X11_H_
-
diff --git a/chromium/ui/events/devices/x11/touch_factory_x11.cc b/chromium/ui/events/devices/x11/touch_factory_x11.cc
index 7396743e7d6..9b431a65a13 100644
--- a/chromium/ui/events/devices/x11/touch_factory_x11.cc
+++ b/chromium/ui/events/devices/x11/touch_factory_x11.cc
@@ -17,7 +17,9 @@
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/devices/x11/device_list_cache_x11.h"
+#include "ui/events/devices/x11/xinput_util.h"
#include "ui/events/event_switches.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
namespace ui {
@@ -42,7 +44,6 @@ void AddPointerDevicesFromString(
TouchFactory::TouchFactory()
: pointer_device_lookup_(),
touch_device_list_(),
- virtual_core_keyboard_device_(-1),
id_generator_(0),
touch_screens_enabled_(true) {
// Ensure device data manager is properly initialized.
@@ -51,12 +52,10 @@ TouchFactory::TouchFactory()
if (!DeviceDataManagerX11::GetInstance()->IsXInput2Available())
return;
- XDisplay* display = gfx::GetXDisplay();
- UpdateDeviceList(display);
+ UpdateDeviceList(x11::Connection::Get());
}
-TouchFactory::~TouchFactory() {
-}
+TouchFactory::~TouchFactory() = default;
// static
TouchFactory* TouchFactory::GetInstance() {
@@ -80,7 +79,7 @@ void TouchFactory::SetTouchDeviceListFromCommandLine() {
ui::TouchFactory::GetInstance()->SetTouchDeviceList(devices);
}
-void TouchFactory::UpdateDeviceList(XDisplay* display) {
+void TouchFactory::UpdateDeviceList(x11::Connection* connection) {
// Detect touch devices.
touch_device_lookup_.reset();
touch_device_list_.clear();
@@ -103,59 +102,55 @@ void TouchFactory::UpdateDeviceList(XDisplay* display) {
// also select on the floating devices.
pointer_device_lookup_.reset();
const XIDeviceList& xi_dev_list =
- DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display);
- for (int i = 0; i < xi_dev_list.count; i++) {
- const XIDeviceInfo& devinfo = xi_dev_list[i];
- if (devinfo.use == XIFloatingSlave || devinfo.use == XIMasterPointer ||
- devinfo.use == XISlavePointer) {
- for (int k = 0; k < devinfo.num_classes; ++k) {
- XIAnyClassInfo* xiclassinfo = devinfo.classes[k];
- if (xiclassinfo->type != XITouchClass)
+ DeviceListCacheX11::GetInstance()->GetXI2DeviceList(connection);
+ for (const auto& devinfo : xi_dev_list) {
+ if (devinfo.type == x11::Input::DeviceType::FloatingSlave ||
+ devinfo.type == x11::Input::DeviceType::MasterPointer ||
+ devinfo.type == x11::Input::DeviceType::SlavePointer) {
+ for (const auto& xiclassinfo : devinfo.classes) {
+ if (!xiclassinfo.touch.has_value())
continue;
- XITouchClassInfo* tci =
- reinterpret_cast<XITouchClassInfo*>(xiclassinfo);
+ auto& tci = *xiclassinfo.touch;
// Only care direct touch device (such as touch screen) right now
- if (tci->mode != XIDirectTouch)
+ if (tci.mode != x11::Input::TouchMode::Direct)
continue;
- int master_id = devinfo.use == XISlavePointer ? devinfo.attachment
- : devinfo.deviceid;
+ auto master_id = devinfo.type == x11::Input::DeviceType::SlavePointer
+ ? devinfo.attachment
+ : devinfo.deviceid;
- if (!IsValidDevice(master_id))
+ if (!IsValidDevice(static_cast<uint16_t>(master_id)))
continue;
- touch_device_lookup_[master_id] = true;
+ touch_device_lookup_[static_cast<uint16_t>(master_id)] = true;
touch_device_list_[master_id] = {true, EventPointerType::kTouch};
- if (devinfo.use != XIMasterPointer)
+ if (devinfo.type != x11::Input::DeviceType::MasterPointer)
CacheTouchscreenIds(devinfo.deviceid);
- if (devinfo.use == XISlavePointer) {
+ if (devinfo.type == x11::Input::DeviceType::MasterPointer) {
device_master_id_list_[devinfo.deviceid] = master_id;
- touch_device_lookup_[devinfo.deviceid] = true;
+ touch_device_lookup_[static_cast<uint16_t>(devinfo.deviceid)] = true;
touch_device_list_[devinfo.deviceid] = {false,
EventPointerType::kTouch};
}
}
- pointer_device_lookup_[devinfo.deviceid] =
- (devinfo.use != XISlavePointer);
- } else if (devinfo.use == XIMasterKeyboard) {
+ pointer_device_lookup_[static_cast<uint16_t>(devinfo.deviceid)] =
+ (devinfo.type != x11::Input::DeviceType::SlavePointer);
+ } else if (devinfo.type == x11::Input::DeviceType::MasterKeyboard) {
virtual_core_keyboard_device_ = devinfo.deviceid;
}
}
}
-bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
- DCHECK_EQ(x11::GeGenericEvent::opcode, xev->type);
- XIEvent* event = static_cast<XIEvent*>(xev->xcookie.data);
- XIDeviceEvent* xiev = reinterpret_cast<XIDeviceEvent*>(event);
-
+bool TouchFactory::ShouldProcessDeviceEvent(
+ const x11::Input::DeviceEvent& xiev) {
const bool is_touch_disabled = !touch_screens_enabled_;
- if (event->evtype == XI_TouchBegin ||
- event->evtype == XI_TouchUpdate ||
- event->evtype == XI_TouchEnd) {
+ if (xiev.opcode == x11::Input::DeviceEvent::TouchBegin ||
+ xiev.opcode == x11::Input::DeviceEvent::TouchUpdate ||
+ xiev.opcode == x11::Input::DeviceEvent::TouchEnd) {
// Since SetupXI2ForXWindow() selects events from all devices, for a
// touchscreen attached to a master pointer device, X11 sends two
// events for each touch: one from the slave (deviceid == the id of
@@ -166,34 +161,35 @@ bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) {
// For a 'floating' touchscreen device, X11 sends only one event for
// each touch, with both deviceid and sourceid set to the id of the
// touchscreen device.
- bool is_from_master_or_float = touch_device_list_[xiev->deviceid].is_master;
- bool is_from_slave_device = !is_from_master_or_float
- && xiev->sourceid == xiev->deviceid;
- return !is_touch_disabled &&
- IsTouchDevice(xiev->deviceid) &&
+ bool is_from_master_or_float = touch_device_list_[xiev.deviceid].is_master;
+ bool is_from_slave_device =
+ !is_from_master_or_float && xiev.sourceid == xiev.deviceid;
+ return !is_touch_disabled && IsTouchDevice(xiev.deviceid) &&
!is_from_slave_device;
}
// Make sure only key-events from the virtual core keyboard are processed.
- if (event->evtype == XI_KeyPress || event->evtype == XI_KeyRelease) {
- return (virtual_core_keyboard_device_ < 0) ||
- (virtual_core_keyboard_device_ == xiev->deviceid);
+ if (xiev.opcode == x11::Input::DeviceEvent::KeyPress ||
+ xiev.opcode == x11::Input::DeviceEvent::KeyRelease) {
+ return !virtual_core_keyboard_device_.has_value() ||
+ *virtual_core_keyboard_device_ == xiev.deviceid;
}
- // Don't automatically accept XI_Enter or XI_Leave. They should be checked
- // against the pointer_device_lookup_ to prevent handling for slave devices.
- // This happens for unknown reasons when using xtest.
- // https://crbug.com/683434.
- if (event->evtype != XI_ButtonPress && event->evtype != XI_ButtonRelease &&
- event->evtype != XI_Enter && event->evtype != XI_Leave &&
- event->evtype != XI_Motion) {
+ return ShouldProcessEventForDevice(xiev.deviceid);
+}
+
+bool TouchFactory::ShouldProcessCrossingEvent(
+ const x11::Input::CrossingEvent& xiev) {
+ // Don't automatically accept x11::Input::CrossingEvent::Enter or
+ // x11::Input::CrossingEvent::Leave. They should be checked against the
+ // pointer_device_lookup_ to prevent handling for slave devices. This happens
+ // for unknown reasons when using xtest. https://crbug.com/683434.
+ if (xiev.opcode != x11::Input::CrossingEvent::Enter &&
+ xiev.opcode != x11::Input::CrossingEvent::Leave) {
return true;
}
- if (!pointer_device_lookup_[xiev->deviceid])
- return false;
-
- return IsTouchDevice(xiev->deviceid) ? !is_touch_disabled : true;
+ return ShouldProcessEventForDevice(xiev.deviceid);
}
void TouchFactory::SetupXI2ForXWindow(x11::Window window) {
@@ -204,42 +200,37 @@ void TouchFactory::SetupXI2ForXWindow(x11::Window window) {
// the events from uninteresting devices. We do the latter because that's
// simpler.
- XDisplay* display = gfx::GetXDisplay();
+ auto* connection = x11::Connection::Get();
- unsigned char mask[XIMaskLen(XI_LASTEVENT)];
- memset(mask, 0, sizeof(mask));
+ x11::Input::EventMask mask{};
+ mask.mask.push_back({});
+ auto* mask_data = mask.mask.data();
- XISetMask(mask, XI_Enter);
- XISetMask(mask, XI_Leave);
- XISetMask(mask, XI_FocusIn);
- XISetMask(mask, XI_FocusOut);
+ SetXinputMask(mask_data, x11::Input::CrossingEvent::Enter);
+ SetXinputMask(mask_data, x11::Input::CrossingEvent::Leave);
+ SetXinputMask(mask_data, x11::Input::CrossingEvent::FocusIn);
+ SetXinputMask(mask_data, x11::Input::CrossingEvent::FocusOut);
- XISetMask(mask, XI_TouchBegin);
- XISetMask(mask, XI_TouchUpdate);
- XISetMask(mask, XI_TouchEnd);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::TouchBegin);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::TouchUpdate);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::TouchEnd);
- XISetMask(mask, XI_ButtonPress);
- XISetMask(mask, XI_ButtonRelease);
- XISetMask(mask, XI_Motion);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::ButtonPress);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::ButtonRelease);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::Motion);
// HierarchyChanged and DeviceChanged allow X11EventSource to still pick up
// these events.
- XISetMask(mask, XI_HierarchyChanged);
- XISetMask(mask, XI_DeviceChanged);
+ SetXinputMask(mask_data, x11::Input::HierarchyEvent::opcode);
+ SetXinputMask(mask_data, x11::Input::DeviceChangedEvent::opcode);
#if defined(OS_CHROMEOS)
- // XGrabKey() must be replaced with XI2 keyboard grab if XI2 key events are
- // enabled on desktop Linux.
if (base::SysInfo::IsRunningOnChromeOS()) {
- XISetMask(mask, XI_KeyPress);
- XISetMask(mask, XI_KeyRelease);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::KeyPress);
+ SetXinputMask(mask_data, x11::Input::DeviceEvent::KeyRelease);
}
#endif
- XIEventMask evmask;
- evmask.deviceid = XIAllDevices;
- evmask.mask_len = sizeof(mask);
- evmask.mask = mask;
- XISelectEvents(display, static_cast<uint32_t>(window), &evmask, 1);
- XFlush(display);
+ connection->xinput().XISelectEvents({window, {mask}});
+ connection->Flush();
}
void TouchFactory::SetTouchDeviceList(
@@ -247,36 +238,41 @@ void TouchFactory::SetTouchDeviceList(
touch_device_lookup_.reset();
touch_device_list_.clear();
for (auto& device : devices) {
- int deviceid = device.first;
+ int device_int = device.first;
+ auto device_id = static_cast<x11::Input::DeviceId>(device_int);
EventPointerType type = device.second;
- DCHECK(IsValidDevice(deviceid));
- touch_device_lookup_[deviceid] = true;
- touch_device_list_[deviceid] = {false, type};
- if (device_master_id_list_.find(deviceid) != device_master_id_list_.end()) {
+ DCHECK(IsValidDevice(device_int));
+ touch_device_lookup_[device_int] = true;
+ touch_device_list_[device_id] = {false, type};
+ if (device_master_id_list_.find(device_id) !=
+ device_master_id_list_.end()) {
// When we set the device through the "--touch-devices" flag to slave
// touch device, we also set its master device to be touch device.
- touch_device_lookup_[device_master_id_list_[deviceid]] = true;
- touch_device_list_[device_master_id_list_[deviceid]] = {false, type};
+ touch_device_lookup_[static_cast<uint16_t>(
+ device_master_id_list_[device_id])] = true;
+ touch_device_list_[device_master_id_list_[device_id]] = {false, type};
}
}
}
bool TouchFactory::IsValidDevice(int deviceid) const {
- return (deviceid >= 0) &&
- (static_cast<size_t>(deviceid) < touch_device_lookup_.size());
+ return deviceid >= 0 &&
+ static_cast<size_t>(deviceid) < touch_device_lookup_.size();
}
-bool TouchFactory::IsTouchDevice(int deviceid) const {
- return IsValidDevice(deviceid) ? touch_device_lookup_[deviceid] : false;
+bool TouchFactory::IsTouchDevice(x11::Input::DeviceId deviceid) const {
+ return IsValidDevice(static_cast<uint16_t>(deviceid)) &&
+ touch_device_lookup_[static_cast<uint16_t>(deviceid)];
}
-bool TouchFactory::IsMultiTouchDevice(int deviceid) const {
- return (IsValidDevice(deviceid) && touch_device_lookup_[deviceid])
- ? touch_device_list_.find(deviceid)->second.is_master
- : false;
+bool TouchFactory::IsMultiTouchDevice(x11::Input::DeviceId deviceid) const {
+ return IsValidDevice(static_cast<uint16_t>(deviceid)) &&
+ touch_device_lookup_[static_cast<uint16_t>(deviceid)] &&
+ touch_device_list_.find(deviceid)->second.is_master;
}
-EventPointerType TouchFactory::GetTouchDevicePointerType(int deviceid) const {
+EventPointerType TouchFactory::GetTouchDevicePointerType(
+ x11::Input::DeviceId deviceid) const {
DCHECK(IsTouchDevice(deviceid));
return touch_device_list_.find(deviceid)->second.pointer_type;
}
@@ -297,8 +293,7 @@ void TouchFactory::ReleaseSlot(int slot) {
}
bool TouchFactory::IsTouchDevicePresent() {
- return touch_screens_enabled_ &&
- touch_device_lookup_.any();
+ return touch_screens_enabled_ && touch_device_lookup_.any();
}
void TouchFactory::ResetForTest() {
@@ -310,24 +305,22 @@ void TouchFactory::ResetForTest() {
SetTouchscreensEnabled(true);
}
-void TouchFactory::SetTouchDeviceForTest(
- const std::vector<int>& devices) {
+void TouchFactory::SetTouchDeviceForTest(const std::vector<int>& devices) {
touch_device_lookup_.reset();
touch_device_list_.clear();
- for (auto iter = devices.begin(); iter != devices.end(); ++iter) {
- DCHECK(IsValidDevice(*iter));
- touch_device_lookup_[*iter] = true;
- touch_device_list_[*iter] = {true, EventPointerType::kTouch};
+ for (int device_id : devices) {
+ auto device = static_cast<x11::Input::DeviceId>(device_id);
+ DCHECK(IsValidDevice(device_id));
+ touch_device_lookup_[device_id] = true;
+ touch_device_list_[device] = {true, EventPointerType::kTouch};
}
SetTouchscreensEnabled(true);
}
-void TouchFactory::SetPointerDeviceForTest(
- const std::vector<int>& devices) {
+void TouchFactory::SetPointerDeviceForTest(const std::vector<int>& devices) {
pointer_device_lookup_.reset();
- for (auto iter = devices.begin(); iter != devices.end(); ++iter) {
- pointer_device_lookup_[*iter] = true;
- }
+ for (int device : devices)
+ pointer_device_lookup_[device] = true;
}
void TouchFactory::SetTouchscreensEnabled(bool enabled) {
@@ -335,7 +328,7 @@ void TouchFactory::SetTouchscreensEnabled(bool enabled) {
DeviceDataManager::GetInstance()->SetTouchscreensEnabled(enabled);
}
-void TouchFactory::CacheTouchscreenIds(int device_id) {
+void TouchFactory::CacheTouchscreenIds(x11::Input::DeviceId device_id) {
if (!DeviceDataManager::HasInstance())
return;
std::vector<TouchscreenDevice> touchscreens =
@@ -343,11 +336,19 @@ void TouchFactory::CacheTouchscreenIds(int device_id) {
const auto it =
std::find_if(touchscreens.begin(), touchscreens.end(),
[device_id](const TouchscreenDevice& touchscreen) {
- return touchscreen.id == device_id;
+ return touchscreen.id == static_cast<int>(device_id);
});
// Internal displays will have a vid and pid of 0. Ignore them.
if (it != touchscreens.end() && it->vendor_id && it->product_id)
touchscreen_ids_.emplace(it->vendor_id, it->product_id);
}
+bool TouchFactory::ShouldProcessEventForDevice(
+ x11::Input::DeviceId device_id) const {
+ if (!pointer_device_lookup_[static_cast<uint16_t>(device_id)])
+ return false;
+
+ return IsTouchDevice(device_id) ? touch_screens_enabled_ : true;
+}
+
} // namespace ui
diff --git a/chromium/ui/events/devices/x11/touch_factory_x11.h b/chromium/ui/events/devices/x11/touch_factory_x11.h
index 13482a6f707..053631eeca8 100644
--- a/chromium/ui/events/devices/x11/touch_factory_x11.h
+++ b/chromium/ui/events/devices/x11/touch_factory_x11.h
@@ -18,10 +18,11 @@
#include "ui/events/event_constants.h"
#include "ui/gfx/sequential_id_generator.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xinput.h"
namespace base {
-
-template <typename T> struct DefaultSingletonTraits;
+template <typename T>
+struct DefaultSingletonTraits;
}
typedef unsigned long Cursor;
@@ -43,11 +44,12 @@ class EVENTS_DEVICES_X11_EXPORT TouchFactory {
static void SetTouchDeviceListFromCommandLine();
// Updates the list of devices.
- void UpdateDeviceList(XDisplay* display);
+ void UpdateDeviceList(x11::Connection* connection);
// Checks whether an XI2 event should be processed or not (i.e. if the event
// originated from a device we are interested in).
- bool ShouldProcessXI2Event(XEvent* xevent);
+ bool ShouldProcessDeviceEvent(const x11::Input::DeviceEvent& event);
+ bool ShouldProcessCrossingEvent(const x11::Input::CrossingEvent& event);
// Setup an X Window for XInput2 events.
void SetupXI2ForXWindow(x11::Window window);
@@ -62,14 +64,15 @@ class EVENTS_DEVICES_X11_EXPORT TouchFactory {
bool IsValidDevice(int deviceid) const;
// Is the device a touch-device?
- bool IsTouchDevice(int deviceid) const;
+ bool IsTouchDevice(x11::Input::DeviceId deviceid) const;
// Is the device a real multi-touch-device? (see doc. for |touch_device_list_|
// below for more explanation.)
- bool IsMultiTouchDevice(int deviceid) const;
+ bool IsMultiTouchDevice(x11::Input::DeviceId deviceid) const;
// Gets the pointer type for touch-device.
- EventPointerType GetTouchDevicePointerType(int deviceid) const;
+ EventPointerType GetTouchDevicePointerType(
+ x11::Input::DeviceId deviceid) const;
// Tries to find an existing slot ID mapping to tracking ID. Returns true
// if the slot is found and it is saved in |slot|, false if no such slot
@@ -87,7 +90,7 @@ class EVENTS_DEVICES_X11_EXPORT TouchFactory {
bool IsTouchDevicePresent();
// Pairs of <vendor id, product id> of external touch screens.
- const std::set<std::pair<int, int> >& GetTouchscreenIds() const {
+ const std::set<std::pair<int, int>>& GetTouchscreenIds() const {
return touchscreen_ids_;
}
@@ -111,7 +114,9 @@ class EVENTS_DEVICES_X11_EXPORT TouchFactory {
// Requirement for Singleton
friend struct base::DefaultSingletonTraits<TouchFactory>;
- void CacheTouchscreenIds(int id);
+ void CacheTouchscreenIds(x11::Input::DeviceId id);
+
+ bool ShouldProcessEventForDevice(x11::Input::DeviceId device_id) const;
// NOTE: To keep track of touch devices, we currently maintain a lookup table
// to quickly decide if a device is a touch device or not. We also maintain a
@@ -138,18 +143,18 @@ class EVENTS_DEVICES_X11_EXPORT TouchFactory {
bool is_master = false;
EventPointerType pointer_type = EventPointerType::kTouch;
};
- std::map<int, TouchDeviceDetails> touch_device_list_;
+ std::map<x11::Input::DeviceId, TouchDeviceDetails> touch_device_list_;
// Touch screen <vid, pid>s.
- std::set<std::pair<int, int> > touchscreen_ids_;
+ std::set<std::pair<int, int>> touchscreen_ids_;
// Device ID of the virtual core keyboard.
- int virtual_core_keyboard_device_;
+ base::Optional<x11::Input::DeviceId> virtual_core_keyboard_device_;
SequentialIDGenerator id_generator_;
// Associate each device ID with its master device ID.
- std::map<int, int> device_master_id_list_;
+ std::map<x11::Input::DeviceId, x11::Input::DeviceId> device_master_id_list_;
// The status of the touch screens devices themselves.
bool touch_screens_enabled_;
diff --git a/chromium/ui/events/devices/x11/xinput_util.h b/chromium/ui/events/devices/x11/xinput_util.h
new file mode 100644
index 00000000000..665d14b9113
--- /dev/null
+++ b/chromium/ui/events/devices/x11/xinput_util.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_DEVICES_X11_XINPUT_UTIL_H_
+#define UI_EVENTS_DEVICES_X11_XINPUT_UTIL_H_
+
+#include <cstdint>
+
+#include "ui/events/devices/x11/events_devices_x11_export.h"
+
+namespace ui {
+
+EVENTS_DEVICES_X11_EXPORT
+inline void SetXinputMask(void* mask, unsigned int opcode) {
+ const auto bit = 1 << (opcode & 7);
+ static_cast<uint8_t*>(mask)[opcode / 8] |= bit;
+}
+
+EVENTS_DEVICES_X11_EXPORT
+inline bool IsXinputMaskSet(const void* mask, unsigned int opcode) {
+ const auto bit = 1 << (opcode & 7);
+ return static_cast<const uint8_t*>(mask)[opcode / 8] & bit;
+}
+
+} // namespace ui
+
+#endif // UI_EVENTS_DEVICES_X11_XINPUT_UTIL_H_
diff --git a/chromium/ui/events/event.cc b/chromium/ui/events/event.cc
index e60e7095cb0..8def7a53166 100644
--- a/chromium/ui/events/event.cc
+++ b/chromium/ui/events/event.cc
@@ -15,6 +15,7 @@
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
@@ -26,11 +27,11 @@
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/point_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h" // nogncheck
#include "ui/events/ozone/layout/keyboard_layout_engine.h" // nogncheck
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" // nogncheck
#endif
@@ -346,9 +347,11 @@ Event::Event(const PlatformEvent& native_event, EventType type, int flags)
ComputeEventLatencyOS(native_event);
#if defined(USE_OZONE)
- source_device_id_ = native_event->source_device_id();
- if (auto* properties = native_event->properties())
- properties_ = std::make_unique<Properties>(*properties);
+ if (features::IsUsingOzonePlatform()) {
+ source_device_id_ = native_event->source_device_id();
+ if (auto* properties = native_event->properties())
+ properties_ = std::make_unique<Properties>(*properties);
+ }
#endif
}
@@ -636,8 +639,8 @@ MouseWheelEvent::MouseWheelEvent(const PlatformEvent& native_event)
MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event)
: MouseEvent(scroll_event),
- offset_(gfx::ToRoundedInt(scroll_event.x_offset()),
- gfx::ToRoundedInt(scroll_event.y_offset())) {
+ offset_(base::ClampRound(scroll_event.x_offset()),
+ base::ClampRound(scroll_event.y_offset())) {
SetType(ET_MOUSEWHEEL);
}
@@ -789,7 +792,7 @@ float TouchEvent::ComputeRotationAngle() const {
// static
KeyEvent* KeyEvent::last_key_event_ = nullptr;
-#if defined(USE_X11)
+#if defined(USE_X11) || defined(USE_OZONE)
KeyEvent* KeyEvent::last_ibus_key_event_ = nullptr;
#endif
@@ -911,21 +914,25 @@ void KeyEvent::ApplyLayout() const {
}
}
KeyboardCode dummy_key_code;
-#if defined(OS_WIN)
-// Native Windows character events always have is_char_ == true,
-// so this is a synthetic or native keystroke event.
-// Therefore, perform only the fallback action.
-#elif defined(USE_OZONE)
- if (KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
+
+#if defined(USE_OZONE)
+ if (features::IsUsingOzonePlatform() &&
+ KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
code, flags(), &key_, &dummy_key_code)) {
return;
}
-#else
+#endif
+
+#if !defined(OS_WIN)
+ // Native Windows character events always have is_char_ == true,
+ // so this is a synthetic or native keystroke event.
+ // Therefore, perform only the fallback action.
if (native_event()) {
DCHECK(EventTypeFromNative(native_event()) == ET_KEY_PRESSED ||
EventTypeFromNative(native_event()) == ET_KEY_RELEASED);
}
#endif
+
if (!DomCodeToUsLayoutDomKey(code, flags(), &key_, &dummy_key_code))
key_ = DomKey::UNIDENTIFIED;
}
@@ -988,7 +995,7 @@ bool KeyEvent::IsRepeated(KeyEvent** last_key_event) {
}
KeyEvent** KeyEvent::GetLastKeyEvent() {
-#if defined(USE_X11)
+#if defined(USE_X11) || defined(USE_OZONE)
// Use a different static variable for key events that have non standard
// state masks as it may be reposted by an IME. IBUS-GTK uses this field
// to detect the re-posted event for example. crbug.com/385873.
diff --git a/chromium/ui/events/event.h b/chromium/ui/events/event.h
index d91fcdc5f82..f7bb613105b 100644
--- a/chromium/ui/events/event.h
+++ b/chromium/ui/events/event.h
@@ -909,7 +909,7 @@ class EVENTS_EXPORT KeyEvent : public Event {
mutable DomKey key_ = DomKey::NONE;
static KeyEvent* last_key_event_;
-#if defined(USE_X11)
+#if defined(USE_X11) || defined(USE_OZONE)
static KeyEvent* last_ibus_key_event_;
#endif
};
diff --git a/chromium/ui/events/event_constants.h b/chromium/ui/events/event_constants.h
index 89ae83d5fea..0b60cb443ed 100644
--- a/chromium/ui/events/event_constants.h
+++ b/chromium/ui/events/event_constants.h
@@ -44,7 +44,7 @@ enum EventFlags {
// An artificial value used to bridge platform differences.
// Many commands on Mac as Cmd+Key are the counterparts of
// Ctrl+Key on other platforms.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EF_PLATFORM_ACCELERATOR = EF_COMMAND_DOWN,
#else
EF_PLATFORM_ACCELERATOR = EF_CONTROL_DOWN,
diff --git a/chromium/ui/events/event_switches.cc b/chromium/ui/events/event_switches.cc
index e02e8161a2a..0bdb883d00a 100644
--- a/chromium/ui/events/event_switches.cc
+++ b/chromium/ui/events/event_switches.cc
@@ -14,7 +14,7 @@ namespace switches {
const char kCompensateForUnstablePinchZoom[] =
"compensate-for-unstable-pinch-zoom";
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Tells chrome to interpret events from these devices as touch events. Only
// available with XInput 2 (i.e. X server 1.8 or above). The id's of the
// devices can be retrieved from 'xinput list'.
diff --git a/chromium/ui/events/event_switches.h b/chromium/ui/events/event_switches.h
index 7b39b66832d..d94309b4edc 100644
--- a/chromium/ui/events/event_switches.h
+++ b/chromium/ui/events/event_switches.h
@@ -13,7 +13,7 @@ namespace switches {
EVENTS_BASE_EXPORT extern const char kCompensateForUnstablePinchZoom[];
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EVENTS_BASE_EXPORT extern const char kTouchDevices[];
EVENTS_BASE_EXPORT extern const char kPenDevices[];
#endif
diff --git a/chromium/ui/events/event_unittest.cc b/chromium/ui/events/event_unittest.cc
index c8dc4af45f0..cbfc3e9ddf6 100644
--- a/chromium/ui/events/event_unittest.cc
+++ b/chromium/ui/events/event_unittest.cc
@@ -14,6 +14,7 @@
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
@@ -43,11 +44,14 @@ TEST(EventTest, NativeEvent) {
MSG native_event = {nullptr, WM_KEYUP, VKEY_A, 0};
KeyEvent keyev(native_event);
EXPECT_TRUE(keyev.HasNativeEvent());
-#elif defined(USE_X11)
- ScopedXI2Event event;
- event.InitKeyEvent(ET_KEY_RELEASED, VKEY_A, EF_NONE);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_FALSE(keyev->HasNativeEvent());
+#endif
+#if defined(USE_X11)
+ if (!features::IsUsingOzonePlatform()) {
+ ScopedXI2Event event;
+ event.InitKeyEvent(ET_KEY_RELEASED, VKEY_A, EF_NONE);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_FALSE(keyev->HasNativeEvent());
+ }
#endif
}
@@ -62,15 +66,17 @@ TEST(EventTest, GetCharacter) {
EXPECT_EQ(13, keyev2.GetCharacter());
#if defined(USE_X11)
- // For X11, test the functions with native_event() as well. crbug.com/107837
- ScopedXI2Event event;
- event.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_CONTROL_DOWN);
- auto keyev3 = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(10, keyev3->GetCharacter());
-
- event.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_NONE);
- auto keyev4 = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(13, keyev4->GetCharacter());
+ if (!features::IsUsingOzonePlatform()) {
+ // For X11, test the functions with native_event() as well. crbug.com/107837
+ ScopedXI2Event event;
+ event.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_CONTROL_DOWN);
+ auto keyev3 = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(10, keyev3->GetCharacter());
+
+ event.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_NONE);
+ auto keyev4 = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(13, keyev4->GetCharacter());
+ }
#endif
// Check if expected Unicode character was returned for a key combination
@@ -291,37 +297,39 @@ TEST(EventTest, KeyEventDirectUnicode) {
TEST(EventTest, NormalizeKeyEventFlags) {
#if defined(USE_X11)
- // Normalize flags when KeyEvent is created from XEvent.
- ScopedXI2Event event;
- {
- event.InitKeyEvent(ET_KEY_PRESSED, VKEY_SHIFT, EF_SHIFT_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_SHIFT_DOWN, keyev->flags());
- }
- {
- event.InitKeyEvent(ET_KEY_RELEASED, VKEY_SHIFT, EF_SHIFT_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_NONE, keyev->flags());
- }
- {
- event.InitKeyEvent(ET_KEY_PRESSED, VKEY_CONTROL, EF_CONTROL_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_CONTROL_DOWN, keyev->flags());
- }
- {
- event.InitKeyEvent(ET_KEY_RELEASED, VKEY_CONTROL, EF_CONTROL_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_NONE, keyev->flags());
- }
- {
- event.InitKeyEvent(ET_KEY_PRESSED, VKEY_MENU, EF_ALT_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_ALT_DOWN, keyev->flags());
- }
- {
- event.InitKeyEvent(ET_KEY_RELEASED, VKEY_MENU, EF_ALT_DOWN);
- auto keyev = ui::BuildKeyEventFromXEvent(*event);
- EXPECT_EQ(EF_NONE, keyev->flags());
+ if (!features::IsUsingOzonePlatform()) {
+ // Normalize flags when KeyEvent is created from XEvent.
+ ScopedXI2Event event;
+ {
+ event.InitKeyEvent(ET_KEY_PRESSED, VKEY_SHIFT, EF_SHIFT_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_SHIFT_DOWN, keyev->flags());
+ }
+ {
+ event.InitKeyEvent(ET_KEY_RELEASED, VKEY_SHIFT, EF_SHIFT_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_NONE, keyev->flags());
+ }
+ {
+ event.InitKeyEvent(ET_KEY_PRESSED, VKEY_CONTROL, EF_CONTROL_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_CONTROL_DOWN, keyev->flags());
+ }
+ {
+ event.InitKeyEvent(ET_KEY_RELEASED, VKEY_CONTROL, EF_CONTROL_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_NONE, keyev->flags());
+ }
+ {
+ event.InitKeyEvent(ET_KEY_PRESSED, VKEY_MENU, EF_ALT_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_ALT_DOWN, keyev->flags());
+ }
+ {
+ event.InitKeyEvent(ET_KEY_RELEASED, VKEY_MENU, EF_ALT_DOWN);
+ auto keyev = ui::BuildKeyEventFromXEvent(*event);
+ EXPECT_EQ(EF_NONE, keyev->flags());
+ }
}
#endif
@@ -394,7 +402,7 @@ TEST(EventTest, KeyEventCode) {
EXPECT_EQ(kCodeForSpace, key.GetCodeString());
}
#if defined(USE_X11)
- {
+ if (!features::IsUsingOzonePlatform()) {
// KeyEvent converts from the native keycode (XKB) to the code.
ScopedXI2Event xevent;
xevent.InitKeyEvent(ET_KEY_PRESSED, VKEY_SPACE, kNativeCodeSpace);
@@ -436,19 +444,19 @@ namespace {
void SetKeyEventTimestamp(x11::Event* event, int64_t time64) {
uint32_t time = time64 & UINT32_MAX;
- event->xlib_event().xkey.time = time;
event->As<x11::KeyEvent>()->time = static_cast<x11::Time>(time);
}
void AdvanceKeyEventTimestamp(x11::Event* event) {
- uint32_t time = event->xlib_event().xkey.time + 1;
- event->xlib_event().xkey.time = time;
+ auto time = static_cast<uint32_t>(event->As<x11::KeyEvent>()->time) + 1;
event->As<x11::KeyEvent>()->time = static_cast<x11::Time>(time);
}
} // namespace
TEST(EventTest, AutoRepeat) {
+ if (features::IsUsingOzonePlatform())
+ return;
const uint16_t kNativeCodeA =
ui::KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A);
const uint16_t kNativeCodeB =
@@ -473,8 +481,7 @@ TEST(EventTest, AutoRepeat) {
// IBUS-GTK uses the mask (1 << 25) to detect reposted event.
{
x11::Event& event = *native_event_a_pressed_nonstandard_state;
- int mask = event.xlib_event().xkey.state | 1 << 25;
- event.xlib_event().xkey.state = mask;
+ int mask = static_cast<int>(event.As<x11::KeyEvent>()->state) | 1 << 25;
event.As<x11::KeyEvent>()->state = static_cast<x11::KeyButMask>(mask);
}
@@ -786,6 +793,8 @@ TEST(EventTest, MouseWheelEventLatencyUIComponentExists) {
// and TOUCH_RELEASED histograms are computed properly.
#if defined(USE_X11)
TEST(EventTest, EventLatencyOSTouchHistograms) {
+ if (features::IsUsingOzonePlatform())
+ return;
base::HistogramTester histogram_tester;
ScopedXI2Event scoped_xevent;
@@ -796,13 +805,16 @@ TEST(EventTest, EventLatencyOSTouchHistograms) {
ui::SetUpTouchDevicesForTest(devices);
// Init touch begin, update, and end events with tracking id 5, touch id 0.
- scoped_xevent.InitTouchEvent(0, XI_TouchBegin, 5, gfx::Point(10, 10), {});
+ scoped_xevent.InitTouchEvent(0, x11::Input::DeviceEvent::TouchBegin, 5,
+ gfx::Point(10, 10), {});
auto touch_begin = ui::BuildTouchEventFromXEvent(*scoped_xevent);
histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_PRESSED", 1);
- scoped_xevent.InitTouchEvent(0, XI_TouchUpdate, 5, gfx::Point(20, 20), {});
+ scoped_xevent.InitTouchEvent(0, x11::Input::DeviceEvent::TouchUpdate, 5,
+ gfx::Point(20, 20), {});
auto touch_update = ui::BuildTouchEventFromXEvent(*scoped_xevent);
histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_MOVED", 1);
- scoped_xevent.InitTouchEvent(0, XI_TouchEnd, 5, gfx::Point(30, 30), {});
+ scoped_xevent.InitTouchEvent(0, x11::Input::DeviceEvent::TouchEnd, 5,
+ gfx::Point(30, 30), {});
auto touch_end = ui::BuildTouchEventFromXEvent(*scoped_xevent);
histogram_tester.ExpectTotalCount("Event.Latency.OS.TOUCH_RELEASED", 1);
}
@@ -815,7 +827,10 @@ TEST(EventTest, EventLatencyOSMouseWheelHistogram) {
MSG event = {nullptr, WM_MOUSEWHEEL, 0, 0};
MouseWheelEvent mouseWheelEvent(event);
histogram_tester.ExpectTotalCount("Event.Latency.OS.MOUSE_WHEEL", 1);
-#elif defined(USE_X11)
+#endif
+#if defined(USE_X11)
+ if (features::IsUsingOzonePlatform())
+ return;
base::HistogramTester histogram_tester;
DeviceDataManagerX11::CreateInstance();
diff --git a/chromium/ui/events/event_utils.cc b/chromium/ui/events/event_utils.cc
index a6be130c6aa..3d0fc645fd0 100644
--- a/chromium/ui/events/event_utils.cc
+++ b/chromium/ui/events/event_utils.cc
@@ -89,7 +89,7 @@ void ComputeEventLatencyOS(const PlatformEvent& native_event) {
EventType type = EventTypeFromNative(native_event);
switch (type) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, ET_SCROLL and ET_MOUSEWHEEL represent the same class of events.
case ET_SCROLL:
#endif
diff --git a/chromium/ui/events/fraction_of_time_without_user_input_recorder.cc b/chromium/ui/events/fraction_of_time_without_user_input_recorder.cc
index 63026261b82..d00e95c2534 100644
--- a/chromium/ui/events/fraction_of_time_without_user_input_recorder.cc
+++ b/chromium/ui/events/fraction_of_time_without_user_input_recorder.cc
@@ -63,8 +63,7 @@ void FractionOfTimeWithoutUserInputRecorder::RecordActiveInterval(
if (end_time < window_end_time)
break;
- RecordToUma(current_window_active_time_.InMillisecondsF() /
- window_size_.InMillisecondsF());
+ RecordToUma(current_window_active_time_ / window_size_);
current_window_active_time_ = base::TimeDelta();
window_start_time_ = window_end_time;
diff --git a/chromium/ui/events/gesture_detection/motion_event_buffer.cc b/chromium/ui/events/gesture_detection/motion_event_buffer.cc
index 88d228ac1e3..c6cdb471fdd 100644
--- a/chromium/ui/events/gesture_detection/motion_event_buffer.cc
+++ b/chromium/ui/events/gesture_detection/motion_event_buffer.cc
@@ -117,8 +117,7 @@ std::unique_ptr<MotionEventGeneric> ResampleMotionEvent(
DCHECK(time0 < time1);
DCHECK(time0 <= resample_time);
- const float alpha = (resample_time - time0).InMillisecondsF() /
- (time1 - time0).InMillisecondsF();
+ const float alpha = (resample_time - time0) / (time1 - time0);
std::unique_ptr<MotionEventGeneric> event;
const size_t pointer_count = event0.GetPointerCount();
diff --git a/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc b/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc
index 4645758c092..7ce6d0a51f2 100644
--- a/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc
+++ b/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc
@@ -4,6 +4,7 @@
#include <stddef.h>
+#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/gesture_detection/motion_event_buffer.h"
@@ -171,8 +172,8 @@ class MotionEventBufferTest : public testing::Test,
base::TimeTicks max_event_time =
event_time + base::TimeDelta::FromSecondsD(0.5f);
const size_t min_expected_events =
- static_cast<size_t>((max_event_time - flush_time) /
- std::max(event_time_delta, flush_time_delta));
+ base::ClampFloor<size_t>((max_event_time - flush_time) /
+ std::max(event_time_delta, flush_time_delta));
MotionEventBuffer buffer(this, true);
@@ -719,8 +720,8 @@ TEST_F(MotionEventBufferTest, Interpolation) {
// There should only be one flushed event, with the event interpolated between
// the two events. The second event should remain buffered.
- float alpha = (interpolated_time - move0.GetEventTime()).InMillisecondsF() /
- (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
+ const float alpha = (interpolated_time - move0.GetEventTime()) /
+ (move1.GetEventTime() - move0.GetEventTime());
MockMotionEvent interpolated_event(
MotionEvent::Action::MOVE, interpolated_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * alpha,
@@ -770,9 +771,8 @@ TEST_F(MotionEventBufferTest, Extrapolation) {
// determining the extrapolated event.
base::TimeTicks expected_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime()) / 2;
- float expected_alpha =
- (expected_time - move0.GetEventTime()).InMillisecondsF() /
- (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
+ const float expected_alpha = (expected_time - move0.GetEventTime()) /
+ (move1.GetEventTime() - move0.GetEventTime());
MockMotionEvent extrapolated_event(
MotionEvent::Action::MOVE, expected_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
@@ -815,9 +815,8 @@ TEST_F(MotionEventBufferTest, ExtrapolationHorizonLimited) {
// Note that the maximum extrapolation is limited by 8 ms.
base::TimeTicks expected_time =
move1.GetEventTime() + base::TimeDelta::FromMilliseconds(8);
- float expected_alpha =
- (expected_time - move0.GetEventTime()).InMillisecondsF() /
- (move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
+ const float expected_alpha = (expected_time - move0.GetEventTime()) /
+ (move1.GetEventTime() - move0.GetEventTime());
MockMotionEvent extrapolated_event(
MotionEvent::Action::MOVE, expected_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
@@ -832,48 +831,33 @@ TEST_F(MotionEventBufferTest, ExtrapolationHorizonLimited) {
}
TEST_F(MotionEventBufferTest, Resampling30to60) {
- base::TimeDelta flush_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
- base::TimeDelta event_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 30.);
-
- RunResample(flush_time_delta, event_time_delta);
+ constexpr auto kFlushTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ constexpr auto kEventTimeDelta = base::TimeDelta::FromSeconds(1) / 30;
+ RunResample(kFlushTimeDelta, kEventTimeDelta);
}
TEST_F(MotionEventBufferTest, Resampling60to60) {
- base::TimeDelta flush_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
- base::TimeDelta event_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
-
- RunResample(flush_time_delta, event_time_delta);
+ constexpr auto kFlushTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ constexpr auto kEventTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ RunResample(kFlushTimeDelta, kEventTimeDelta);
}
TEST_F(MotionEventBufferTest, Resampling100to60) {
- base::TimeDelta flush_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
- base::TimeDelta event_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 100.);
-
- RunResample(flush_time_delta, event_time_delta);
+ constexpr auto kFlushTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ constexpr auto kEventTimeDelta = base::TimeDelta::FromSeconds(1) / 100;
+ RunResample(kFlushTimeDelta, kEventTimeDelta);
}
TEST_F(MotionEventBufferTest, Resampling120to60) {
- base::TimeDelta flush_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
- base::TimeDelta event_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 120.);
-
- RunResample(flush_time_delta, event_time_delta);
+ constexpr auto kFlushTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ constexpr auto kEventTimeDelta = base::TimeDelta::FromSeconds(1) / 120;
+ RunResample(kFlushTimeDelta, kEventTimeDelta);
}
TEST_F(MotionEventBufferTest, Resampling150to60) {
- base::TimeDelta flush_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 60.);
- base::TimeDelta event_time_delta =
- base::TimeDelta::FromMillisecondsD(1000. / 150.);
-
- RunResample(flush_time_delta, event_time_delta);
+ constexpr auto kFlushTimeDelta = base::TimeDelta::FromSeconds(1) / 60;
+ constexpr auto kEventTimeDelta = base::TimeDelta::FromSeconds(1) / 150;
+ RunResample(kFlushTimeDelta, kEventTimeDelta);
}
} // namespace ui
diff --git a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc
index 4037df3ba7e..1a73efbfb7b 100644
--- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc
+++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc
@@ -15,7 +15,6 @@
#include "ui/events/gestures/fling_curve.h"
#include "ui/events/gestures/physics_based_fling_curve.h"
#include "ui/events/mobile_scroller.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#if defined(OS_WIN)
#include "ui/display/win/screen_win.h"
diff --git a/chromium/ui/events/gestures/physics_based_fling_curve.cc b/chromium/ui/events/gestures/physics_based_fling_curve.cc
index 9a715080918..d480d7c4970 100644
--- a/chromium/ui/events/gestures/physics_based_fling_curve.cc
+++ b/chromium/ui/events/gestures/physics_based_fling_curve.cc
@@ -4,6 +4,8 @@
#include "ui/events/gestures/physics_based_fling_curve.h"
+#include <cmath>
+
namespace {
// These constants are defined based on UX experiment.
@@ -15,24 +17,21 @@ const float kDefaultPixelDeceleration = 2300.0f;
const float kMaxCurveDurationForFling = 2.0f;
const float kDefaultPhysicalDeceleration = 2.7559e-5f; // inch/ms^2.
-inline gfx::Vector2dF GetPositionAtTime(const gfx::Vector2dF& end_point,
- double progress) {
+inline gfx::Vector2dF GetPositionAtValue(const gfx::Vector2dF& end_point,
+ double progress) {
return gfx::ScaleVector2d(end_point, progress);
}
inline gfx::Vector2dF GetVelocityAtTime(const gfx::Vector2dF& current_offset,
const gfx::Vector2dF& prev_offset,
- double delta) {
- return gfx::ScaleVector2d(current_offset - prev_offset, (1 / delta));
+ base::TimeDelta delta) {
+ return gfx::ScaleVector2d(current_offset - prev_offset, delta.ToHz());
}
float GetOffset(float velocity, float deceleration, float duration) {
- float position =
+ const float position =
(std::abs(velocity) - deceleration * duration * 0.5) * duration;
- if (velocity < 0.0f)
- return -position;
-
- return position;
+ return std::copysign(position, velocity);
}
gfx::Vector2dF GetDecelerationInPixelsPerMs2(
@@ -101,7 +100,8 @@ PhysicsBasedFlingCurve::PhysicsBasedFlingCurve(
bezier_(p1_.x(), p1_.y(), p2_.x(), p2_.y()),
previous_time_delta_(base::TimeDelta()) {
DCHECK(!velocity.IsZero());
- CHECK(!std::isnan(velocity.x()) && !std::isnan(velocity.y()));
+ DCHECK(!std::isnan(velocity.x()));
+ DCHECK(!std::isnan(velocity.y()));
}
PhysicsBasedFlingCurve::~PhysicsBasedFlingCurve() = default;
@@ -111,40 +111,35 @@ bool PhysicsBasedFlingCurve::ComputeScrollOffset(base::TimeTicks time,
gfx::Vector2dF* velocity) {
DCHECK(offset);
DCHECK(velocity);
- base::TimeDelta elapsed_time = time - start_timestamp_;
+
+ const base::TimeDelta elapsed_time = time - start_timestamp_;
if (elapsed_time < base::TimeDelta()) {
*offset = gfx::Vector2dF();
*velocity = gfx::Vector2dF();
return true;
}
- bool still_active = true;
- double x = elapsed_time.InSecondsF() / curve_duration_;
- if (x < 1.0f) {
- double progress = bezier_.Solve(x);
- *offset = GetPositionAtTime(distance_, progress);
- *velocity =
- GetVelocityAtTime(*offset, prev_offset_,
- (elapsed_time - previous_time_delta_).InSecondsF());
+ const double x = elapsed_time / curve_duration_;
+ const bool still_active = x < 1.0f;
+ if (still_active) {
+ const double progress = bezier_.Solve(x);
+ *offset = GetPositionAtValue(distance_, progress);
+ *velocity = GetVelocityAtTime(*offset, prev_offset_,
+ elapsed_time - previous_time_delta_);
prev_offset_ = *offset;
previous_time_delta_ = elapsed_time;
- still_active = true;
} else {
- // At the end of animation, we should have travel distance equal to
- // distance_
+ // At the end of animation, we should have traveled a distance equal to
+ // |distance_|.
*offset = distance_;
*velocity = gfx::Vector2dF();
- still_active = false;
}
return still_active;
}
-// This method calculate the curve duration and generate the control points for
-// bezier curve based on velocity and |distance_|. It calculate the slope based
-// on the input velocity (initial velocity), curve duration and |distance_|.
-// Slope is then used to configure the value of control points for curve.
-float PhysicsBasedFlingCurve::CalculateDurationAndConfigureControlPoints(
+base::TimeDelta
+PhysicsBasedFlingCurve::CalculateDurationAndConfigureControlPoints(
const gfx::Vector2dF& velocity) {
float fling_velocity = std::max(fabs(velocity.x()), fabs(velocity.y()));
float duration = std::min(kMaxCurveDurationForFling,
@@ -162,6 +157,6 @@ float PhysicsBasedFlingCurve::CalculateDurationAndConfigureControlPoints(
p1_.set_x(p1_.y() / slope);
}
- return duration;
+ return base::TimeDelta::FromSecondsD(duration);
}
} // namespace ui \ No newline at end of file
diff --git a/chromium/ui/events/gestures/physics_based_fling_curve.h b/chromium/ui/events/gestures/physics_based_fling_curve.h
index 4d4bfc7b0c4..c467ecf0856 100644
--- a/chromium/ui/events/gestures/physics_based_fling_curve.h
+++ b/chromium/ui/events/gestures/physics_based_fling_curve.h
@@ -37,7 +37,9 @@ class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
gfx::Vector2dF* offset,
gfx::Vector2dF* velocity) override;
- float curve_duration() const { return curve_duration_; }
+ // TODO(crbug.com/1028501): Use base::TimeDelta for curve_duration()
+ // once crrev.com/c/1865928 is merged.
+ float curve_duration() const { return curve_duration_.InSecondsF(); }
const gfx::PointF& p1_for_testing() const { return p1_; }
const gfx::PointF& p2_for_testing() const { return p2_; }
static int default_bounds_multiplier_for_testing() {
@@ -45,6 +47,17 @@ class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
}
private:
+ // Default value used to scale the viewport when it is passed in as a
+ // parameter in the generation of a physics based fling curve. This value
+ // increases the upper bound of the scroll distance for a fling.
+ constexpr static int kDefaultBoundsMultiplier = 3;
+
+ // Calculates the curve duration and generates the control points for a bezier
+ // curve. The slope is based on the input initial |velocity|, calculated curve
+ // duration, and |distance_|. Returns the duration.
+ base::TimeDelta CalculateDurationAndConfigureControlPoints(
+ const gfx::Vector2dF& velocity);
+
// Time when fling curve is generated.
const base::TimeTicks start_timestamp_;
// Cubic bezier curve control points.
@@ -52,26 +65,14 @@ class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
gfx::PointF p2_;
// Distance it can scroll with input velocity.
const gfx::Vector2dF distance_;
- // Time in seconds, till which fling can remain active relative to
- // |start_timestamp_|.
- // TODO (sarsha): Use base::TimeDelta for |curve_duration_| once
- // crrev.com/c/1865928 is merged.
- // crbug.com/1028501
- const float curve_duration_;
-
- // Default value used to scale the viewport when it is passed in as a
- // parameter in the generation of a physics based fling curve. This value
- // increases the upper bound of the scroll distance for a fling.
- constexpr static int kDefaultBoundsMultiplier = 3;
+ // Time until which fling can remain active relative to |start_timestamp_|.
+ const base::TimeDelta curve_duration_;
const gfx::CubicBezier bezier_;
base::TimeDelta previous_time_delta_;
gfx::Vector2dF cumulative_scroll_;
gfx::Vector2dF prev_offset_;
- float CalculateDurationAndConfigureControlPoints(
- const gfx::Vector2dF& velocity);
-
DISALLOW_COPY_AND_ASSIGN(PhysicsBasedFlingCurve);
};
diff --git a/chromium/ui/events/ipc/BUILD.gn b/chromium/ui/events/ipc/BUILD.gn
index 378eca46614..f82291ca2a5 100644
--- a/chromium/ui/events/ipc/BUILD.gn
+++ b/chromium/ui/events/ipc/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("ipc") {
+component("ipc") {
output_name = "ui_events_ipc"
sources = [
diff --git a/chromium/ui/events/keyboard_hook_base.cc b/chromium/ui/events/keyboard_hook_base.cc
index c212acc4423..909b112f2c5 100644
--- a/chromium/ui/events/keyboard_hook_base.cc
+++ b/chromium/ui/events/keyboard_hook_base.cc
@@ -27,6 +27,11 @@ bool KeyboardHookBase::IsKeyLocked(DomCode dom_code) const {
return ShouldCaptureKeyEvent(dom_code);
}
+bool KeyboardHookBase::RegisterHook() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
bool KeyboardHookBase::ShouldCaptureKeyEvent(DomCode dom_code) const {
if (dom_code == DomCode::NONE)
return false;
diff --git a/chromium/ui/events/keyboard_hook_base.h b/chromium/ui/events/keyboard_hook_base.h
index 29740813551..c5a7f2dd9cd 100644
--- a/chromium/ui/events/keyboard_hook_base.h
+++ b/chromium/ui/events/keyboard_hook_base.h
@@ -24,6 +24,8 @@ class KeyboardHookBase : public KeyboardHook {
// KeyboardHook implementation.
bool IsKeyLocked(DomCode dom_code) const override;
+ virtual bool RegisterHook();
+
protected:
// Indicates whether |dom_code| should be intercepted by the keyboard hook.
bool ShouldCaptureKeyEvent(DomCode dom_code) const;
diff --git a/chromium/ui/events/keyboard_hook_linux.cc b/chromium/ui/events/keyboard_hook_linux.cc
new file mode 100644
index 00000000000..45ee6ee3de0
--- /dev/null
+++ b/chromium/ui/events/keyboard_hook_linux.cc
@@ -0,0 +1,56 @@
+// 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 "ui/events/keyboard_hook_base.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/gfx/native_widget_types.h"
+
+#if defined(USE_X11)
+#include "ui/events/x/keyboard_hook_x11.h"
+#endif
+
+#if defined(USE_OZONE)
+#include "ui/events/ozone/keyboard_hook_ozone.h"
+#endif
+
+namespace ui {
+
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
+ base::Optional<base::flat_set<DomCode>> dom_codes,
+ gfx::AcceleratedWidget accelerated_widget,
+ KeyEventCallback callback) {
+ std::unique_ptr<KeyboardHookBase> keyboard_hook;
+#if defined(USE_OZONE)
+ if (features::IsUsingOzonePlatform()) {
+ keyboard_hook = std::make_unique<KeyboardHookOzone>(std::move(dom_codes),
+ std::move(callback));
+ }
+#endif
+#if defined(USE_X11)
+ if (!keyboard_hook) {
+ keyboard_hook = std::make_unique<KeyboardHookX11>(
+ std::move(dom_codes), accelerated_widget, std::move(callback));
+ }
+#endif
+ DCHECK(keyboard_hook);
+ if (!keyboard_hook->RegisterHook())
+ return nullptr;
+
+ return keyboard_hook;
+}
+
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+ KeyEventCallback callback) {
+ return nullptr;
+}
+
+} // namespace ui
diff --git a/chromium/ui/events/keycodes/BUILD.gn b/chromium/ui/events/keycodes/BUILD.gn
index 0068238c9f9..12f3abf9243 100644
--- a/chromium/ui/events/keycodes/BUILD.gn
+++ b/chromium/ui/events/keycodes/BUILD.gn
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/base/ui_features.gni")
import("//ui/ozone/ozone.gni")
-jumbo_source_set("xkb") {
+source_set("xkb") {
sources = [
"keyboard_code_conversion_xkb.cc",
"keyboard_code_conversion_xkb.h",
@@ -28,7 +27,7 @@ jumbo_source_set("xkb") {
}
if (use_x11 || ozone_platform_x11) {
- jumbo_component("x11") {
+ component("x11") {
output_name = "keycodes_x11"
sources = [
diff --git a/chromium/ui/events/keycodes/DEPS b/chromium/ui/events/keycodes/DEPS
index 200ea4afa12..0186a15eeb0 100644
--- a/chromium/ui/events/keycodes/DEPS
+++ b/chromium/ui/events/keycodes/DEPS
@@ -8,7 +8,6 @@ include_rules = [
"-ui",
"+ui/base/buildflags.h", # Doesn't bring in all of UI.
- "+ui/gfx/x/x11.h", # Wrapper on <X11/*>.
- "+ui/gfx/x/x11_types.h", # <X11/*> replacement.
+ "+ui/gfx/x",
"+ui/events",
]
diff --git a/chromium/ui/events/keycodes/dom/keycode_converter.cc b/chromium/ui/events/keycodes/dom/keycode_converter.cc
index e5550482576..1840608f730 100644
--- a/chromium/ui/events/keycodes/dom/keycode_converter.cc
+++ b/chromium/ui/events/keycodes/dom/keycode_converter.cc
@@ -20,17 +20,17 @@ namespace {
#if defined(OS_WIN)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, win, code }
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, xkb, code }
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, mac, code }
#elif defined(OS_ANDROID)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, evdev, code }
#elif defined(OS_FUCHSIA)
-// TODO(https://bugs.fuchsia.com/23982): Fuchsia currently delivers events
+// TODO(https://crbug.com/1107418): Fuchsia currently delivers events
// with a USB Code but no Page specified, so only map |native_keycode| for
// Keyboard Usage Page codes, for now.
inline constexpr uint32_t CodeIfOnKeyboardPage(uint32_t usage) {
@@ -269,7 +269,7 @@ int KeycodeConverter::UsbKeycodeToNativeKeycode(uint32_t usb_keycode) {
// Deal with some special-cases that don't fit the 1:1 mapping.
if (usb_keycode == 0x070032) // non-US hash.
usb_keycode = 0x070031; // US backslash.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (usb_keycode == 0x070046) // PrintScreen.
usb_keycode = 0x070068; // F13.
#endif
diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc b/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc
index 3a5dac167ba..fb88d4c5328 100644
--- a/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc
+++ b/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc
@@ -18,6 +18,9 @@
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/keycodes/keysym_to_unicode.h"
#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xinput.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xproto_types.h"
#define VKEY_UNSUPPORTED VKEY_UNKNOWN
@@ -552,10 +555,27 @@ bool IsTtyFunctionOrSpaceKey(KeySym keysym) {
return false;
}
+::KeySym TranslateKey(uint32_t keycode, uint32_t modifiers) {
+ auto* connection = x11::Connection::Get();
+ return static_cast<::KeySym>(connection->KeycodeToKeysym(keycode, modifiers));
+}
+
+void GetKeycodeAndModifiers(const x11::Event& event,
+ uint32_t* keycode,
+ uint32_t* modifiers) {
+ if (auto* dev = event.As<x11::Input::DeviceEvent>()) {
+ *keycode = dev->detail;
+ *modifiers = dev->mods.effective;
+ } else if (auto* key = event.As<x11::KeyEvent>()) {
+ *keycode = static_cast<uint32_t>(key->detail);
+ *modifiers = static_cast<uint32_t>(key->state);
+ }
+}
+
} // namespace
// Get an ui::KeyboardCode from an X keyevent
-KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
+KeyboardCode KeyboardCodeFromXKeyEvent(const x11::Event& xev) {
// Gets correct VKEY code from XEvent is performed as the following steps:
// 1. Gets the keysym without modifier states.
// 2. For [a-z] & [0-9] cases, returns the VKEY code accordingly.
@@ -569,21 +589,12 @@ KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
// mainly for non-letter keys.
// 8. If not found, fallback to find with the hardware code in US layout.
- KeySym keysym = NoSymbol;
- XEvent xkeyevent;
- xkeyevent.xkey = {};
- if (xev->type == x11::GeGenericEvent::opcode) {
- // Convert the XI2 key event into a core key event so that we can
- // continue to use XLookupString() until crbug.com/367732 is complete.
- InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
- } else {
- xkeyevent.xkey = xev->xkey;
- }
+ uint32_t keysym = 0;
+ uint32_t xkeycode = 0;
+ uint32_t modifiers = 0;
+ GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
KeyboardCode keycode = VKEY_UNKNOWN;
- XKeyEvent* xkey = &xkeyevent.xkey;
- // XLookupKeysym does not take into consideration the state of the lock/shift
- // etc. keys. So it is necessary to use XLookupString instead.
- XLookupString(xkey, nullptr, 0, &keysym, nullptr);
+ keysym = TranslateKey(xkeycode, modifiers);
if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym) ||
IsCursorKey(keysym) || IsPFKey(keysym) || IsFunctionKey(keysym) ||
IsModifierKey(keysym) || IsTtyFunctionOrSpaceKey(keysym)) {
@@ -593,9 +604,9 @@ KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
// If |xkey| has modifiers set, other than NumLock, then determine the
// un-modified KeySym and use that to map, so that e.g. Ctrl+D correctly
// generates VKEY_D.
- if (xkey->state & 0xFF & ~Mod2Mask) {
- xkey->state &= (~0xFF | Mod2Mask);
- XLookupString(xkey, nullptr, 0, &keysym, nullptr);
+ if (modifiers & 0xFF & ~Mod2Mask) {
+ modifiers &= (~0xFF | Mod2Mask);
+ keysym = TranslateKey(xkeycode, modifiers);
}
// [a-z] cases.
@@ -614,24 +625,24 @@ KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
if (keycode != VKEY_UNKNOWN)
return keycode;
- MAP1 key1 = {keysym & 0xFFFF, xkey->keycode, 0};
+ MAP1 key1 = {keysym & 0xFFFF, xkeycode, 0};
keycode = FindVK(key1, map1, base::size(map1));
if (keycode != VKEY_UNKNOWN)
return keycode;
KeySym keysym_shift = NoSymbol;
- xkey->state |= ShiftMask;
- XLookupString(xkey, nullptr, 0, &keysym_shift, nullptr);
- MAP2 key2 = {keysym & 0xFFFF, xkey->keycode, keysym_shift & 0xFFFF, 0};
+ modifiers |= ShiftMask;
+ keysym_shift = TranslateKey(xkeycode, modifiers);
+ MAP2 key2 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF, 0};
keycode = FindVK(key2, map2, base::size(map2));
if (keycode != VKEY_UNKNOWN)
return keycode;
KeySym keysym_altgr = NoSymbol;
- xkey->state &= ~ShiftMask;
- xkey->state |= Mod1Mask;
- XLookupString(xkey, nullptr, 0, &keysym_altgr, nullptr);
- MAP3 key3 = {keysym & 0xFFFF, xkey->keycode, keysym_shift & 0xFFFF,
+ modifiers &= ~ShiftMask;
+ modifiers |= Mod1Mask;
+ keysym_altgr = TranslateKey(xkeycode, modifiers);
+ MAP3 key3 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF,
keysym_altgr & 0xFFFF, 0};
keycode = FindVK(key3, map3, base::size(map3));
if (keycode != VKEY_UNKNOWN)
@@ -640,7 +651,7 @@ KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
// On Linux some keys has AltGr char but not on Windows.
// So if cannot find VKEY with (ch0+sc+ch1+ch2) in map3, tries to fallback
// to just find VKEY with (ch0+sc+ch1). This is the best we could do.
- MAP3 key4 = {keysym & 0xFFFF, xkey->keycode, keysym_shift & 0xFFFF, 0, 0};
+ MAP3 key4 = {keysym & 0xFFFF, xkeycode, keysym_shift & 0xFFFF, 0, 0};
const MAP3* p =
std::lower_bound(map3, map3 + base::size(map3), key4, MAP3());
if (p != map3 + base::size(map3) && p->ch0 == key4.ch0 &&
@@ -652,7 +663,7 @@ KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev) {
if (keycode == VKEY_UNKNOWN && !IsModifierKey(keysym)) {
// Modifier keys should not fall back to the hardware-keycode-based US
// layout. See crbug.com/402320
- keycode = DefaultKeyboardCodeFromHardwareKeycode(xkey->keycode);
+ keycode = DefaultKeyboardCodeFromHardwareKeycode(xkeycode);
}
return keycode;
@@ -943,42 +954,31 @@ KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
return VKEY_UNKNOWN;
}
-DomCode CodeFromXEvent(const XEvent* xev) {
- int keycode = (xev->type == x11::GeGenericEvent::opcode)
- ? static_cast<XIDeviceEvent*>(xev->xcookie.data)->detail
- : xev->xkey.keycode;
+DomCode CodeFromXEvent(const x11::Event& xev) {
+ auto* device = xev.As<x11::Input::DeviceEvent>();
+ int keycode = 0;
+ if (device) {
+ keycode = device->detail;
+ } else {
+ auto* key = xev.As<x11::KeyEvent>();
+ DCHECK(key);
+ keycode = static_cast<uint8_t>(key->detail);
+ }
return ui::KeycodeConverter::NativeKeycodeToDomCode(keycode);
}
-uint16_t GetCharacterFromXEvent(const XEvent* xev) {
- XEvent xkeyevent;
- xkeyevent.xkey = {};
- const XKeyEvent* xkey = nullptr;
- if (xev->type == x11::GeGenericEvent::opcode) {
- // Convert the XI2 key event into a core key event so that we can
- // continue to use XLookupString() until crbug.com/367732 is complete.
- InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
- xkey = &xkeyevent.xkey;
- } else {
- xkey = &xev->xkey;
- }
- KeySym keysym = XK_VoidSymbol;
- XLookupString(const_cast<XKeyEvent*>(xkey), nullptr, 0, &keysym, nullptr);
+uint16_t GetCharacterFromXEvent(const x11::Event& xev) {
+ uint32_t xkeycode = 0;
+ uint32_t modifiers = 0;
+ GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
+ KeySym keysym = TranslateKey(xkeycode, modifiers);
return GetUnicodeCharacterFromXKeySym(keysym);
}
-DomKey GetDomKeyFromXEvent(const XEvent* xev) {
- XEvent xkeyevent;
- xkeyevent.xkey = {};
- XKeyEvent xkey;
- if (xev->type == x11::GeGenericEvent::opcode) {
- // Convert the XI2 key event into a core key event so that we can
- // continue to use XLookupString() until crbug.com/367732 is complete.
- InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
- xkey = xkeyevent.xkey;
- } else {
- xkey = xev->xkey;
- }
+DomKey GetDomKeyFromXEvent(const x11::Event& xev) {
+ uint32_t xkeycode = 0;
+ uint32_t modifiers = 0;
+ GetKeycodeAndModifiers(xev, &xkeycode, &modifiers);
// There is no good way to check whether a key combination will print a
// character on screen.
// e.g. On Linux US keyboard with French layout, |XLookupString()|
@@ -988,9 +988,8 @@ DomKey GetDomKeyFromXEvent(const XEvent* xev) {
// The solution is to take out ctrl modifier directly, as according to XKB map
// no keyboard combinations with ctrl key are mapped to printable character.
// https://crbug.com/633838
- xkey.state &= ~ControlMask;
- KeySym keysym = XK_VoidSymbol;
- XLookupString(&xkey, nullptr, 0, &keysym, nullptr);
+ modifiers &= ~ControlMask;
+ KeySym keysym = TranslateKey(xkeycode, modifiers);
base::char16 ch = GetUnicodeCharacterFromXKeySym(keysym);
return XKeySymToDomKey(keysym, ch);
}
@@ -1434,38 +1433,9 @@ int XKeysymForWindowsKeyCode(KeyboardCode keycode, bool shift) {
}
}
-void InitXKeyEventFromXIDeviceEvent(const XEvent& src, XEvent* xkeyevent) {
- DCHECK(src.type == x11::GeGenericEvent::opcode);
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(src.xcookie.data);
- switch (xievent->evtype) {
- case XI_KeyPress:
- xkeyevent->type = x11::KeyEvent::Press;
- break;
- case XI_KeyRelease:
- xkeyevent->type = x11::KeyEvent::Release;
- break;
- default:
- NOTREACHED();
- }
- xkeyevent->xkey.serial = xievent->serial;
- xkeyevent->xkey.send_event = xievent->send_event;
- xkeyevent->xkey.display = xievent->display;
- xkeyevent->xkey.window = xievent->event;
- xkeyevent->xkey.root = xievent->root;
- xkeyevent->xkey.subwindow = xievent->child;
- xkeyevent->xkey.time = xievent->time;
- xkeyevent->xkey.x = xievent->event_x;
- xkeyevent->xkey.y = xievent->event_y;
- xkeyevent->xkey.x_root = xievent->root_x;
- xkeyevent->xkey.y_root = xievent->root_y;
- xkeyevent->xkey.state = xievent->mods.effective;
- xkeyevent->xkey.keycode = xievent->detail;
- xkeyevent->xkey.same_screen = 1;
-}
-
unsigned int XKeyCodeForWindowsKeyCode(ui::KeyboardCode key_code,
int flags,
- XDisplay* display) {
+ x11::Connection* connection) {
// SHIFT state is ignored in the call to XKeysymForWindowsKeyCode() here
// because we map the XKeysym back to a keycode, i.e. a physical key position.
// Using a SHIFT-modified XKeysym would sometimes yield X keycodes that,
@@ -1486,7 +1456,9 @@ unsigned int XKeyCodeForWindowsKeyCode(ui::KeyboardCode key_code,
// crbug.com/386066 and crbug.com/390263 are examples of problems
// associated with this.
//
- return XKeysymToKeycode(display, XKeysymForWindowsKeyCode(key_code, false));
+ auto keysym =
+ static_cast<x11::KeySym>(XKeysymForWindowsKeyCode(key_code, false));
+ return static_cast<unsigned int>(connection->KeysymToKeycode(keysym));
}
} // namespace ui
diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_x.h b/chromium/ui/events/keycodes/keyboard_code_conversion_x.h
index f2373916a22..2e5c6dce472 100644
--- a/chromium/ui/events/keycodes/keyboard_code_conversion_x.h
+++ b/chromium/ui/events/keycodes/keyboard_code_conversion_x.h
@@ -11,25 +11,24 @@
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/keycodes/keycodes_x_export.h"
-
-typedef union _XEvent XEvent;
-typedef struct _XDisplay XDisplay;
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/event.h"
namespace ui {
enum class DomCode;
-KEYCODES_X_EXPORT KeyboardCode KeyboardCodeFromXKeyEvent(const XEvent* xev);
+KEYCODES_X_EXPORT KeyboardCode KeyboardCodeFromXKeyEvent(const x11::Event& xev);
KEYCODES_X_EXPORT KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym);
-KEYCODES_X_EXPORT DomCode CodeFromXEvent(const XEvent* xev);
+KEYCODES_X_EXPORT DomCode CodeFromXEvent(const x11::Event& xev);
// Returns a character on a standard US PC keyboard from an XEvent.
-KEYCODES_X_EXPORT uint16_t GetCharacterFromXEvent(const XEvent* xev);
+KEYCODES_X_EXPORT uint16_t GetCharacterFromXEvent(const x11::Event& xev);
// Returns DomKey and character from an XEvent.
-KEYCODES_X_EXPORT DomKey GetDomKeyFromXEvent(const XEvent* xev);
+KEYCODES_X_EXPORT DomKey GetDomKeyFromXEvent(const x11::Event& xev);
// Converts a KeyboardCode into an X KeySym.
KEYCODES_X_EXPORT int XKeysymForWindowsKeyCode(KeyboardCode keycode,
@@ -39,18 +38,15 @@ KEYCODES_X_EXPORT int XKeysymForWindowsKeyCode(KeyboardCode keycode,
// are usually not injective, so inverse mapping should be avoided when
// practical. A round-trip keycode -> KeyboardCode -> keycode will not
// necessarily return the original keycode.
-KEYCODES_X_EXPORT unsigned int XKeyCodeForWindowsKeyCode(KeyboardCode key_code,
- int flags,
- XDisplay* display);
+KEYCODES_X_EXPORT unsigned int XKeyCodeForWindowsKeyCode(
+ KeyboardCode key_code,
+ int flags,
+ x11::Connection* connection);
// Converts an X keycode into ui::KeyboardCode.
KEYCODES_X_EXPORT KeyboardCode
DefaultKeyboardCodeFromHardwareKeycode(unsigned int hardware_code);
-// Initializes a core XKeyEvent from an XI2 key event.
-KEYCODES_X_EXPORT void InitXKeyEventFromXIDeviceEvent(const XEvent& src,
- XEvent* dst);
-
} // namespace ui
#endif // UI_EVENTS_KEYCODES_KEYBOARD_CODE_CONVERSION_X_H_
diff --git a/chromium/ui/events/ozone/BUILD.gn b/chromium/ui/events/ozone/BUILD.gn
index 7b2fb8283ee..3f08cf23a9c 100644
--- a/chromium/ui/events/ozone/BUILD.gn
+++ b/chromium/ui/events/ozone/BUILD.gn
@@ -70,7 +70,7 @@ source_set("unittests") {
sources += [ "chromeos/cursor_controller_unittest.cc" ]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//ui/events/ozone/evdev:unittests" ]
}
}
diff --git a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc
index 9bd6ddd1ee5..9473353f186 100644
--- a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc
+++ b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc
@@ -7,9 +7,9 @@
#include <stddef.h>
#include "base/logging.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "ui/events/ozone/device/device_event.h"
#include "ui/events/ozone/device/device_event_observer.h"
@@ -95,7 +95,7 @@ void DeviceManagerUdev::CreateMonitor() {
if (monitor_) {
int fd = device::udev_monitor_get_fd(monitor_.get());
CHECK_GT(fd, 0);
- base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+ base::CurrentUIThread::Get()->WatchFileDescriptor(
fd, true, base::MessagePumpForUI::WATCH_READ, &controller_, this);
}
}
diff --git a/chromium/ui/events/ozone/evdev/BUILD.gn b/chromium/ui/events/ozone/evdev/BUILD.gn
index 22f19b93df9..2d6f2ec500b 100644
--- a/chromium/ui/events/ozone/evdev/BUILD.gn
+++ b/chromium/ui/events/ozone/evdev/BUILD.gn
@@ -6,7 +6,7 @@ import("//build/config/chromeos/args.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
-assert(use_ozone && is_linux)
+assert(use_ozone && (is_linux || is_chromeos))
visibility = [ ":*" ]
diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc
index fe6e641c873..5d11c7dca9a 100644
--- a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc
+++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc
@@ -10,7 +10,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/device_util_linux.h"
@@ -51,7 +51,7 @@ EventConverterEvdev::~EventConverterEvdev() {
}
void EventConverterEvdev::Start() {
- base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+ base::CurrentUIThread::Get()->WatchFileDescriptor(
fd_, true, base::MessagePumpForUI::WATCH_READ, &controller_, this);
watching_ = true;
}
@@ -135,6 +135,20 @@ std::vector<ui::GamepadDevice::Axis> EventConverterEvdev::GetGamepadAxes()
return std::vector<ui::GamepadDevice::Axis>();
}
+bool EventConverterEvdev::GetGamepadRumbleCapability() const {
+ NOTREACHED();
+ return false;
+}
+
+void EventConverterEvdev::PlayVibrationEffect(uint8_t amplitude,
+ uint16_t duration_millis) {
+ NOTREACHED();
+}
+
+void EventConverterEvdev::StopVibration() {
+ NOTREACHED();
+}
+
int EventConverterEvdev::GetTouchPoints() const {
NOTREACHED();
return 0;
diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.h b/chromium/ui/events/ozone/evdev/event_converter_evdev.h
index 717410f2ebc..7712a4cc7d3 100644
--- a/chromium/ui/events/ozone/evdev/event_converter_evdev.h
+++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.h
@@ -14,7 +14,7 @@
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
#include "ui/events/devices/gamepad_device.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/ozone/evdev/event_dispatch_callback.h"
@@ -102,7 +102,10 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev
// Returns information for all axes if the converter is used for a gamepad
// device.
- virtual std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const;
+ virtual std::vector<GamepadDevice::Axis> GetGamepadAxes() const;
+
+ // Returns whether the gamepad device supports rumble type force feedback.
+ virtual bool GetGamepadRumbleCapability() const;
// Sets which keyboard keys should be processed. If |enable_filter| is
// false, all keys are allowed and |allowed_keys| is ignored.
@@ -122,6 +125,10 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev
// Helper to generate a base::TimeTicks from an input_event's time
static base::TimeTicks TimeTicksFromInputEvent(const input_event& event);
+ // Handle gamepad force feedback effects.
+ virtual void PlayVibrationEffect(uint8_t amplitude, uint16_t duration_millis);
+ virtual void StopVibration();
+
protected:
// base::MessagePumpForUI::FdWatcher:
void OnFileCanWriteWithoutBlocking(int fd) override;
diff --git a/chromium/ui/events/ozone/evdev/event_device_info.cc b/chromium/ui/events/ozone/evdev/event_device_info.cc
index 473847f55d1..130ae0dba1f 100644
--- a/chromium/ui/events/ozone/evdev/event_device_info.cc
+++ b/chromium/ui/events/ozone/evdev/event_device_info.cc
@@ -32,7 +32,21 @@ constexpr struct {
uint16_t vendor;
uint16_t product_id;
} kKeyboardBlocklist[] = {
- {0x045e, 0x0b05}, // Xbox One Elite Series 2 gamepad
+ {0x03f0, 0xa407}, // HP X4000 Wireless Mouse
+ {0x045e, 0x0745}, // Microsoft Wireless Mobile Mouse 6000
+ {0x045e, 0x0821}, // Microsoft Surface Precision Mouse
+ {0x045e, 0x082a}, // Microsoft Pro IntelliMouse
+ {0x045e, 0x082f}, // Microsoft Bluetooth Mouse
+ {0x045e, 0x0b05}, // Xbox One Elite Series 2 gamepad
+ {0x046d, 0x4069}, // Logitech MX Master 2S (Unifying)
+ {0x046d, 0xb016}, // Logitech M535
+ {0x046d, 0xb019}, // Logitech MX Master 2S (Bluetooth)
+ {0x046d, 0xc093}, // Logitech M500s
+ {0x046d, 0xc534}, // Logitech M185/M187
+ {0x056e, 0x0134}, // Elecom Enelo IR LED Mouse 350
+ {0x056e, 0x0141}, // Elecom EPRIM Blue LED 5 button mouse 228
+ {0x056e, 0x0159}, // Elecom Blue LED Mouse 203
+ {0x1bcf, 0x08a0}, // Kensington Pro Fit Full-size
};
constexpr struct {
@@ -135,17 +149,17 @@ void AssignBitset(const unsigned long* src,
memset(&dst[src_len], 0, (dst_len - src_len) * sizeof(unsigned long));
}
-bool IsBlacklistedAbsoluteMouseDevice(const input_id& id) {
+bool IsDenylistedAbsoluteMouseDevice(const input_id& id) {
static constexpr struct {
uint16_t vid;
uint16_t pid;
- } kUSBLegacyBlackListedDevices[] = {
+ } kUSBLegacyDenyListedDevices[] = {
{0x222a, 0x0001}, // ILITEK ILITEK-TP
};
- for (size_t i = 0; i < base::size(kUSBLegacyBlackListedDevices); ++i) {
- if (id.vendor == kUSBLegacyBlackListedDevices[i].vid &&
- id.product == kUSBLegacyBlackListedDevices[i].pid) {
+ for (size_t i = 0; i < base::size(kUSBLegacyDenyListedDevices); ++i) {
+ if (id.vendor == kUSBLegacyDenyListedDevices[i].vid &&
+ id.product == kUSBLegacyDenyListedDevices[i].pid) {
return true;
}
}
@@ -163,6 +177,7 @@ EventDeviceInfo::EventDeviceInfo() {
memset(msc_bits_, 0, sizeof(msc_bits_));
memset(sw_bits_, 0, sizeof(sw_bits_));
memset(led_bits_, 0, sizeof(led_bits_));
+ memset(ff_bits_, 0, sizeof(ff_bits_));
memset(prop_bits_, 0, sizeof(prop_bits_));
memset(abs_info_, 0, sizeof(abs_info_));
}
@@ -191,6 +206,9 @@ bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) {
if (!GetEventBits(fd, path, EV_LED, led_bits_, sizeof(led_bits_)))
return false;
+ if (!GetEventBits(fd, path, EV_FF, ff_bits_, sizeof(ff_bits_)))
+ return false;
+
if (!GetPropBits(fd, path, prop_bits_, sizeof(prop_bits_)))
return false;
@@ -260,6 +278,10 @@ void EventDeviceInfo::SetLedEvents(const unsigned long* led_bits, size_t len) {
AssignBitset(led_bits, len, led_bits_, base::size(led_bits_));
}
+void EventDeviceInfo::SetFfEvents(const unsigned long* ff_bits, size_t len) {
+ AssignBitset(ff_bits, len, ff_bits_, base::size(ff_bits_));
+}
+
void EventDeviceInfo::SetProps(const unsigned long* prop_bits, size_t len) {
AssignBitset(prop_bits, len, prop_bits_, base::size(prop_bits_));
}
@@ -343,6 +365,12 @@ bool EventDeviceInfo::HasLedEvent(unsigned int code) const {
return EvdevBitIsSet(led_bits_, code);
}
+bool EventDeviceInfo::HasFfEvent(unsigned int code) const {
+ if (code > FF_MAX)
+ return false;
+ return EvdevBitIsSet(ff_bits_, code);
+}
+
bool EventDeviceInfo::HasProp(unsigned int code) const {
if (code > INPUT_PROP_MAX)
return false;
@@ -522,6 +550,10 @@ bool EventDeviceInfo::HasGamepad() const {
return support_gamepad_btn && !HasTablet() && !HasKeyboard();
}
+bool EventDeviceInfo::SupportsRumble() const {
+ return HasEventType(EV_FF) && HasFfEvent(FF_RUMBLE);
+}
+
// static
ui::InputDeviceType EventDeviceInfo::GetInputDeviceTypeFromId(input_id id) {
static constexpr struct {
@@ -577,7 +609,7 @@ EventDeviceInfo::ProbeLegacyAbsoluteDevice() const {
// ABS_Z mitigation for extra device on some Elo devices.
if (HasKeyEvent(BTN_LEFT) && !HasAbsEvent(ABS_Z) &&
- !IsBlacklistedAbsoluteMouseDevice(input_id_))
+ !IsDenylistedAbsoluteMouseDevice(input_id_))
return LegacyAbsoluteDeviceType::TOUCHSCREEN;
return LegacyAbsoluteDeviceType::NONE;
diff --git a/chromium/ui/events/ozone/evdev/event_device_info.h b/chromium/ui/events/ozone/evdev/event_device_info.h
index da6637aee7c..6760d82616e 100644
--- a/chromium/ui/events/ozone/evdev/event_device_info.h
+++ b/chromium/ui/events/ozone/evdev/event_device_info.h
@@ -64,6 +64,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
void SetMscEvents(const unsigned long* msc_bits, size_t len);
void SetSwEvents(const unsigned long* sw_bits, size_t len);
void SetLedEvents(const unsigned long* led_bits, size_t len);
+ void SetFfEvents(const unsigned long* ff_bits, size_t len);
void SetProps(const unsigned long* prop_bits, size_t len);
void SetAbsInfo(unsigned int code, const input_absinfo& absinfo);
void SetAbsMtSlots(unsigned int code, const std::vector<int32_t>& values);
@@ -80,6 +81,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
bool HasMscEvent(unsigned int code) const;
bool HasSwEvent(unsigned int code) const;
bool HasLedEvent(unsigned int code) const;
+ bool HasFfEvent(unsigned int code) const;
// Properties of absolute axes.
int32_t GetAbsMinimum(unsigned int code) const;
@@ -148,6 +150,9 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
// Determine whether there's a gamepad on this device.
bool HasGamepad() const;
+ // Determine whether the device supports rumble.
+ bool SupportsRumble() const;
+
// Determine if this is a dedicated device for a stylus button.
bool IsStylusButtonDevice() const;
@@ -177,6 +182,7 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo {
unsigned long sw_bits_[EVDEV_BITS_TO_LONGS(SW_CNT)];
unsigned long led_bits_[EVDEV_BITS_TO_LONGS(LED_CNT)];
unsigned long prop_bits_[EVDEV_BITS_TO_LONGS(INPUT_PROP_CNT)];
+ unsigned long ff_bits_[EVDEV_BITS_TO_LONGS(FF_CNT)];
struct input_absinfo abs_info_[ABS_CNT];
diff --git a/chromium/ui/events/ozone/evdev/event_device_test_util.cc b/chromium/ui/events/ozone/evdev/event_device_test_util.cc
index c87016b02e0..dd1dc0be9ff 100644
--- a/chromium/ui/events/ozone/evdev/event_device_test_util.cc
+++ b/chromium/ui/events/ozone/evdev/event_device_test_util.cc
@@ -1090,6 +1090,11 @@ bool CapabilitiesToDeviceInfo(const DeviceCapabilities& capabilities,
return false;
devinfo->SetLedEvents(&led_bits[0], led_bits.size());
+ std::vector<unsigned long> ff_bits;
+ if (!ParseBitfield(capabilities.ff, FF_CNT, &ff_bits))
+ return false;
+ devinfo->SetFfEvents(&ff_bits[0], ff_bits.size());
+
std::vector<unsigned long> prop_bits;
if (!ParseBitfield(capabilities.prop, INPUT_PROP_CNT, &prop_bits))
return false;
diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
index 6ca3e8ff946..46129d638b0 100644
--- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
+++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
@@ -7,14 +7,21 @@
#include <errno.h>
#include <linux/input.h>
#include <stddef.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
#include "base/trace_event/trace_event.h"
#include "ui/events/event_utils.h"
#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
#include "ui/events/ozone/gamepad/gamepad_event.h"
#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
+namespace {
+constexpr int kInvalidEffectId = -1;
+}
+
namespace ui {
GamepadEventConverterEvdev::GamepadEventConverterEvdev(
@@ -34,7 +41,8 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev(
devinfo.version()),
will_send_frame_(false),
input_device_fd_(std::move(fd)),
- dispatcher_(dispatcher) {
+ dispatcher_(dispatcher),
+ effect_id_(-1) {
input_absinfo abs_info;
for (int code = 0; code < ABS_CNT; ++code) {
abs_info = devinfo.GetAbsInfoByCode(code);
@@ -49,6 +57,7 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev(
axes_.push_back(axis);
}
}
+ supports_rumble_ = devinfo.SupportsRumble();
}
GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
@@ -59,6 +68,102 @@ bool GamepadEventConverterEvdev::HasGamepad() const {
return true;
}
+void GamepadEventConverterEvdev::PlayVibrationEffect(uint8_t amplitude,
+ uint16_t duration_millis) {
+ constexpr uint16_t kRumbleMagnitudeMax = 0xFFFF;
+ constexpr uint8_t kAmplitudeMax = 0xFF;
+
+ // only rumble type force feedback is supported at the moment
+ if (!supports_rumble_) {
+ LOG(ERROR) << "Device doesn't support rumble, but SetVibration is called.";
+ return;
+ }
+
+ float amplitude_ratio = static_cast<float>(amplitude) / kAmplitudeMax;
+ uint16_t magnitude_scaled =
+ static_cast<uint16_t>(amplitude_ratio * kRumbleMagnitudeMax);
+
+ effect_id_ = StoreRumbleEffect(input_device_fd_, effect_id_, duration_millis,
+ 0, magnitude_scaled, magnitude_scaled);
+
+ if (effect_id_ == kInvalidEffectId) {
+ LOG(ERROR) << "SetVibration is called with an invalid effect ID.";
+ return;
+ }
+ StartOrStopEffect(input_device_fd_, effect_id_, true);
+}
+
+void GamepadEventConverterEvdev::StopVibration() {
+ if (!supports_rumble_) {
+ LOG(ERROR)
+ << "Device doesn't support rumble, but SetZeroVibration is called.";
+ return;
+ }
+ if (effect_id_ == kInvalidEffectId) {
+ LOG(ERROR) << "SetZeroVibration is called with an invalid effect ID.";
+ return;
+ }
+ StartOrStopEffect(input_device_fd_, effect_id_, false);
+}
+
+int GamepadEventConverterEvdev::StoreRumbleEffect(const base::ScopedFD& fd,
+ int effect_id,
+ uint16_t duration,
+ uint16_t start_delay,
+ uint16_t strong_magnitude,
+ uint16_t weak_magnitude) {
+ struct ff_effect effect;
+ memset(&effect, 0, sizeof(effect));
+ effect.type = FF_RUMBLE;
+ effect.id = effect_id;
+ effect.replay.length = duration;
+ effect.replay.delay = start_delay;
+ effect.u.rumble.strong_magnitude = strong_magnitude;
+ effect.u.rumble.weak_magnitude = weak_magnitude;
+
+ return UploadFfEffect(fd, &effect);
+}
+
+void GamepadEventConverterEvdev::StartOrStopEffect(const base::ScopedFD& fd,
+ int effect_id,
+ bool do_start) {
+ struct input_event start_stop;
+ memset(&start_stop, 0, sizeof(start_stop));
+ start_stop.type = EV_FF;
+ start_stop.code = effect_id;
+ start_stop.value = do_start ? 1 : 0;
+ ssize_t nbytes = WriteEvent(fd, start_stop);
+
+ if (nbytes != sizeof(start_stop)) {
+ PLOG(ERROR) << "Error writing the event in StartOrStopEffect";
+ }
+}
+
+void GamepadEventConverterEvdev::DestroyEffect(const base::ScopedFD& fd,
+ int effect_id) {
+ if (HANDLE_EINTR(ioctl(fd.get(), EVIOCRMFF, effect_id)) < 0) {
+ PLOG(ERROR) << "Error destroying rumble effect.";
+ }
+ effect_id_ = kInvalidEffectId;
+}
+
+ssize_t GamepadEventConverterEvdev::WriteEvent(
+ const base::ScopedFD& fd,
+ const struct input_event& event) {
+ return HANDLE_EINTR(
+ write(fd.get(), static_cast<const void*>(&event), sizeof(event)));
+}
+
+int GamepadEventConverterEvdev::UploadFfEffect(const base::ScopedFD& fd,
+ struct ff_effect* effect) {
+ if (HANDLE_EINTR(ioctl(fd.get(), EVIOCSFF, effect)) < 0) {
+ PLOG(ERROR) << "Error storing rumble effect.";
+ return kInvalidEffectId;
+ }
+
+ return effect->id;
+}
+
void GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
TRACE_EVENT1("evdev",
"GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
@@ -90,6 +195,10 @@ GamepadEventConverterEvdev::GetGamepadAxes() const {
return axes_;
}
+bool GamepadEventConverterEvdev::GetGamepadRumbleCapability() const {
+ return supports_rumble_;
+}
+
void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) {
base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev);
// We may have missed Gamepad releases. Reset everything.
@@ -134,6 +243,9 @@ void GamepadEventConverterEvdev::ProcessEvdevAbs(
void GamepadEventConverterEvdev::ResetGamepad() {
base::TimeTicks timestamp = ui::EventTimeForNow();
+ if (effect_id_ != kInvalidEffectId) {
+ DestroyEffect(input_device_fd_, effect_id_);
+ }
// Reset all the buttons.
for (unsigned int code : pressed_buttons_)
ProcessEvdevKey(code, 0, timestamp);
diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
index 32d3ab81427..28960b800a9 100644
--- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
+++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
@@ -39,10 +39,18 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
bool HasGamepad() const override;
void OnDisabled() override;
std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const override;
+ bool GetGamepadRumbleCapability() const override;
// This function processes one input_event from evdev.
void ProcessEvent(const struct input_event& input);
+ // This function sends a vibration effect to the gamepaddevice.
+ void PlayVibrationEffect(uint8_t amplitude,
+ uint16_t duration_millis) override;
+
+ // This function stops the gamepad device's vibration effect.
+ void StopVibration() override;
+
private:
// This function processes EV_KEY event from gamepad device.
void ProcessEvdevKey(uint16_t code,
@@ -62,11 +70,43 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
void OnSync(const base::TimeTicks& timestamp);
+ // This function uploads the rumble force feedback effect to the gamepad
+ // device and returns the new effect id. If we already created an effect on
+ // this device, then the existing id is reused and returned.
+ int StoreRumbleEffect(const base::ScopedFD& fd,
+ int effect_id,
+ uint16_t duration,
+ uint16_t start_delay,
+ uint16_t strong_magnitude,
+ uint16_t weak_magnitude);
+
+ // This function controls the playback of the effect on the gamepad device.
+ void StartOrStopEffect(const base::ScopedFD& fd,
+ int effect_id,
+ bool do_start);
+
+ // This function removes the effect from the gamepad device.
+ void DestroyEffect(const base::ScopedFD& fd, int effect_id);
+
+ // This function writes the input_event into the kernel and returns the result
+ // of the write.
+ virtual ssize_t WriteEvent(const base::ScopedFD& fd,
+ const struct input_event& input);
+
+ // This function uploads the ff_effect to the gamepad device and returns the
+ // unique id assigned by the driver.
+ virtual int UploadFfEffect(const base::ScopedFD& fd,
+ struct ff_effect* effect);
+
// Sometimes, we want to drop abs values, when we do so, we no longer want to
// send gamepad frame event when we see next sync. This flag is set to false
// when each frame is sent. It is set to true when Btn or Abs event is sent.
bool will_send_frame_;
+ // This flag is set to true if the gamepad supports force feedback of type
+ // FF_RUMBLE.
+ bool supports_rumble_;
+
std::vector<ui::GamepadDevice::Axis> axes_;
// Evdev scancodes of pressed buttons.
@@ -78,6 +118,10 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev
// Callbacks for dispatching events.
DeviceEventDispatcherEvdev* const dispatcher_;
+ // The effect id is needed to keep track of effects that are uploaded and
+ // stored in the gamepad device.
+ int effect_id_;
+
DISALLOW_COPY_AND_ASSIGN(GamepadEventConverterEvdev);
};
diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc
index ceec22806e1..56dd97d039a 100644
--- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc
+++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc
@@ -60,6 +60,33 @@ class TestGamepadObserver : public ui::GamepadObserver {
} // namespace
namespace ui {
+
+class TestGamepadEventConverterEvdev : public ui::GamepadEventConverterEvdev {
+ public:
+ TestGamepadEventConverterEvdev(base::ScopedFD fd,
+ base::FilePath path,
+ int id,
+ const EventDeviceInfo& info,
+ DeviceEventDispatcherEvdev* dispatcher)
+ : GamepadEventConverterEvdev(std::move(fd), path, id, info, dispatcher) {}
+
+ int UploadFfEffect(const base::ScopedFD& fd,
+ struct ff_effect* effect) override {
+ uploaded_ff_effects_.push_back(*effect);
+ return kEffectId;
+ }
+
+ ssize_t WriteEvent(const base::ScopedFD& fd,
+ const struct input_event& event) override {
+ written_input_events_.push_back(event);
+ return 0;
+ }
+
+ int kEffectId = 0;
+ std::vector<ff_effect> uploaded_ff_effects_;
+ std::vector<input_event> written_input_events_;
+};
+
class GamepadEventConverterEvdevTest : public testing::Test {
public:
GamepadEventConverterEvdevTest() {}
@@ -77,7 +104,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
ui::CreateDeviceEventDispatcherEvdevForTest(event_factory_.get());
}
- std::unique_ptr<ui::GamepadEventConverterEvdev> CreateDevice(
+ std::unique_ptr<ui::TestGamepadEventConverterEvdev> CreateDevice(
const ui::DeviceCapabilities& caps) {
int evdev_io[2];
if (pipe(evdev_io))
@@ -87,7 +114,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
ui::EventDeviceInfo devinfo;
CapabilitiesToDeviceInfo(caps, &devinfo);
- return std::make_unique<ui::GamepadEventConverterEvdev>(
+ return std::make_unique<ui::TestGamepadEventConverterEvdev>(
std::move(events_in), base::FilePath(kTestDevicePath), 1, devinfo,
dispatcher_.get());
}
@@ -95,7 +122,7 @@ class GamepadEventConverterEvdevTest : public testing::Test {
private:
void DispatchEventForTest(ui::Event* event) {}
- std::unique_ptr<ui::GamepadEventConverterEvdev> gamepad_evdev_;
+ std::unique_ptr<ui::TestGamepadEventConverterEvdev> gamepad_evdev_;
std::unique_ptr<ui::DeviceManager> device_manager_;
std::unique_ptr<ui::KeyboardLayoutEngine> keyboard_layout_engine_;
std::unique_ptr<ui::EventFactoryEvdev> event_factory_;
@@ -110,9 +137,20 @@ struct ExpectedEvent {
double value;
};
+struct ExpectedVibrationEvent {
+ uint16_t code;
+ double value;
+};
+
+struct ExpectedVibrationEffect {
+ uint16_t duration;
+ double strong_magnitude;
+ double weak_magnitude;
+};
+
TEST_F(GamepadEventConverterEvdevTest, XboxGamepadEvents) {
TestGamepadObserver observer;
- std::unique_ptr<ui::GamepadEventConverterEvdev> dev =
+ std::unique_ptr<ui::TestGamepadEventConverterEvdev> dev =
CreateDevice(kXboxGamepad);
struct input_event mock_kernel_queue[] = {
@@ -180,4 +218,34 @@ TEST_F(GamepadEventConverterEvdevTest, XboxGamepadEvents) {
}
}
+TEST_F(GamepadEventConverterEvdevTest, XboxGamepadVibrationEvents) {
+ TestGamepadObserver observer;
+ std::unique_ptr<ui::TestGamepadEventConverterEvdev> dev =
+ CreateDevice(kXboxGamepad);
+
+ struct ExpectedVibrationEvent expected_events[] = {{dev->kEffectId, 1},
+ {dev->kEffectId, 0}};
+ struct ExpectedVibrationEffect expected_effect = {10000, 0x8080, 0x8080};
+
+ dev->PlayVibrationEffect(0x80, 10000);
+ EXPECT_EQ(1UL, dev->written_input_events_.size());
+
+ input_event received_vibration_event = dev->written_input_events_[0];
+ EXPECT_EQ(expected_events[0].code, received_vibration_event.code);
+ EXPECT_EQ(expected_events[0].value, received_vibration_event.value);
+
+ ff_effect received_effect = dev->uploaded_ff_effects_[0];
+ EXPECT_EQ(expected_effect.duration, received_effect.replay.length);
+ EXPECT_EQ(expected_effect.strong_magnitude,
+ received_effect.u.rumble.strong_magnitude);
+ EXPECT_EQ(expected_effect.weak_magnitude,
+ received_effect.u.rumble.weak_magnitude);
+
+ dev->StopVibration();
+ EXPECT_EQ(2UL, dev->written_input_events_.size());
+ input_event received_cancel_event = dev->written_input_events_[1];
+ EXPECT_EQ(expected_events[1].code, received_cancel_event.code);
+ EXPECT_EQ(expected_events[1].value, received_cancel_event.value);
+}
+
} // namespace ui
diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc
index c7f564820bc..d85cfa72275 100644
--- a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc
+++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc
@@ -257,4 +257,18 @@ void InputControllerEvdev::UpdateCapsLockLed() {
caps_lock_led_state_ = caps_lock_state;
}
+void InputControllerEvdev::PlayVibrationEffect(int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) {
+ if (!input_device_factory_)
+ return;
+ input_device_factory_->PlayVibrationEffect(id, amplitude, duration_millis);
+}
+
+void InputControllerEvdev::StopVibration(int id) {
+ if (!input_device_factory_)
+ return;
+ input_device_factory_->StopVibration(id);
+}
+
} // namespace ui
diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.h b/chromium/ui/events/ozone/evdev/input_controller_evdev.h
index 2e49e1c6b97..68544a0cb0c 100644
--- a/chromium/ui/events/ozone/evdev/input_controller_evdev.h
+++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.h
@@ -79,6 +79,10 @@ class COMPONENT_EXPORT(EVDEV) InputControllerEvdev : public InputController {
void GetGesturePropertiesService(
mojo::PendingReceiver<ozone::mojom::GesturePropertiesService> receiver)
override;
+ void PlayVibrationEffect(int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) override;
+ void StopVibration(int id) override;
private:
// Post task to update settings.
diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc
index bf9ac94b72b..dd1cf2a9c58 100644
--- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -107,12 +107,9 @@ std::unique_ptr<EventConverterEvdev> CreateConverter(
// Touchscreen: use TouchEventConverterEvdev.
if (devinfo.HasTouchscreen()) {
- std::unique_ptr<TouchEventConverterEvdev> converter(
- new TouchEventConverterEvdev(std::move(fd), params.path, params.id,
- devinfo, params.shared_palm_state,
- params.dispatcher));
- converter->Initialize(devinfo);
- return std::move(converter);
+ return TouchEventConverterEvdev::Create(
+ std::move(fd), params.path, params.id, devinfo,
+ params.shared_palm_state, params.dispatcher);
}
// Graphics tablet
@@ -356,9 +353,7 @@ void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() {
SetBoolPropertyForOneType(
DT_MOUSE, "Mouse Reverse Scrolling",
input_device_settings_.mouse_reverse_scroll_enabled);
- SetBoolPropertyForOneType(
- DT_MOUSE, "Mouse High Resolution Scrolling",
- base::FeatureList::IsEnabled(ui::kEnableHighResolutionMouseScrolling));
+ SetBoolPropertyForOneType(DT_MOUSE, "Mouse High Resolution Scrolling", true);
SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused",
input_device_settings_.tap_to_click_paused);
@@ -397,6 +392,26 @@ void InputDeviceFactoryEvdev::ApplyCapsLockLed() {
}
}
+void InputDeviceFactoryEvdev::PlayVibrationEffect(int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) {
+ for (const auto& it : converters_) {
+ if (it.second->id() == id) {
+ it.second->PlayVibrationEffect(amplitude, duration_millis);
+ return;
+ }
+ }
+}
+
+void InputDeviceFactoryEvdev::StopVibration(int id) {
+ for (const auto& it : converters_) {
+ if (it.second->id() == id) {
+ it.second->StopVibration();
+ return;
+ }
+ }
+}
+
bool InputDeviceFactoryEvdev::IsDeviceEnabled(
const EventConverterEvdev* converter) {
if (!input_device_settings_.enable_internal_touchpad &&
@@ -515,7 +530,8 @@ void InputDeviceFactoryEvdev::NotifyGamepadDevicesUpdated() {
for (auto it = converters_.begin(); it != converters_.end(); ++it) {
if (it->second->HasGamepad()) {
gamepads.emplace_back(it->second->input_device(),
- it->second->GetGamepadAxes());
+ it->second->GetGamepadAxes(),
+ it->second->GetGamepadRumbleCapability());
}
}
diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h
index b04fbc5628b..91f7b8578d2 100644
--- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h
+++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h
@@ -62,6 +62,10 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev {
// LED state.
void SetCapsLockLed(bool enabled);
+ // Handle gamepad force feedback effects.
+ void PlayVibrationEffect(int id, uint8_t amplitude, uint16_t duration_millis);
+ void StopVibration(int id);
+
// Bits from InputController that have to be answered on IO.
void UpdateInputDeviceSettings(const InputDeviceSettingsEvdev& settings);
void GetTouchDeviceStatus(InputController::GetTouchDeviceStatusReply reply);
diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
index 46dcccbd3e1..9767cbdfd18 100644
--- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
+++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
@@ -108,4 +108,20 @@ void InputDeviceFactoryEvdevProxy::GetGesturePropertiesService(
input_device_factory_, std::move(receiver)));
}
+void InputDeviceFactoryEvdevProxy::PlayVibrationEffect(
+ int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&InputDeviceFactoryEvdev::PlayVibrationEffect,
+ input_device_factory_, id, amplitude, duration_millis));
+}
+
+void InputDeviceFactoryEvdevProxy::StopVibration(int id) {
+ task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(&InputDeviceFactoryEvdev::StopVibration,
+ input_device_factory_, id));
+}
+
} // namespace ui
diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
index 1811014e14d..94dc5cb92c8 100644
--- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
+++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
@@ -49,6 +49,8 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdevProxy {
InputController::GetTouchEventLogReply reply);
void GetGesturePropertiesService(
mojo::PendingReceiver<ozone::mojom::GesturePropertiesService> receiver);
+ void PlayVibrationEffect(int id, uint8_t amplitude, uint16_t duration_millis);
+ void StopVibration(int id);
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc
index afab037632d..3daca1f98bf 100644
--- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc
+++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -138,6 +138,27 @@ TouchEventConverterEvdev::TouchEventConverterEvdev(
TouchEventConverterEvdev::~TouchEventConverterEvdev() {
}
+// static
+std::unique_ptr<TouchEventConverterEvdev> TouchEventConverterEvdev::Create(
+ base::ScopedFD fd,
+ base::FilePath path,
+ int id,
+ const EventDeviceInfo& devinfo,
+ SharedPalmDetectionFilterState* shared_palm_state,
+ DeviceEventDispatcherEvdev* dispatcher) {
+ auto converter = std::make_unique<TouchEventConverterEvdev>(
+ std::move(fd), std::move(path), id, devinfo, shared_palm_state,
+ dispatcher);
+ converter->Initialize(devinfo);
+ if (!converter->GetTouchscreenSize().GetCheckedArea().IsValid()) {
+ LOG(WARNING) << "Ignoring touchscreen \"" << converter->input_device().name
+ << "\" reporting invalid size "
+ << converter->GetTouchscreenSize().ToString();
+ return nullptr;
+ }
+ return converter;
+}
+
void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
has_mt_ = info.HasMultitouch();
has_pen_ = info.HasKeyEvent(BTN_TOOL_PEN);
diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h
index 82da9b0f875..276d71e2c9f 100644
--- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h
+++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h
@@ -50,6 +50,14 @@ class COMPONENT_EXPORT(EVDEV) TouchEventConverterEvdev
DeviceEventDispatcherEvdev* dispatcher);
~TouchEventConverterEvdev() override;
+ static std::unique_ptr<TouchEventConverterEvdev> Create(
+ base::ScopedFD fd,
+ base::FilePath path,
+ int id,
+ const EventDeviceInfo& devinfo,
+ SharedPalmDetectionFilterState* shared_palm_state,
+ DeviceEventDispatcherEvdev* dispatcher);
+
// EventConverterEvdev:
bool HasTouchscreen() const override;
bool HasPen() const override;
diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index 5d59d97117c..4b743598283 100644
--- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -13,6 +13,7 @@
#include <vector>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
@@ -2290,4 +2291,18 @@ TEST_F(TouchEventConverterEvdevTest, FingerSizeWithResolution) {
EXPECT_FLOAT_EQ(14.f, in_progress_event.major);
EXPECT_FLOAT_EQ(11.f, in_progress_event.minor);
}
+
+// b/162596241
+TEST_F(TouchEventConverterEvdevTest, InvalidDimensions) {
+ EventDeviceInfo devinfo;
+ input_absinfo absinfo = {};
+ absinfo.maximum = 1 << 16;
+ devinfo.SetAbsInfo(ABS_X, absinfo);
+ devinfo.SetAbsInfo(ABS_MT_POSITION_X, absinfo);
+ devinfo.SetAbsInfo(ABS_Y, absinfo);
+ devinfo.SetAbsInfo(ABS_MT_POSITION_Y, absinfo);
+ EXPECT_FALSE(
+ TouchEventConverterEvdev::Create({}, base::FilePath(kTestDevicePath), 0,
+ devinfo, shared_palm_state(), nullptr));
+}
} // namespace ui
diff --git a/chromium/ui/events/ozone/features.cc b/chromium/ui/events/ozone/features.cc
index 0c160ac930e..b7ca1671c76 100644
--- a/chromium/ui/events/ozone/features.cc
+++ b/chromium/ui/events/ozone/features.cc
@@ -9,9 +9,6 @@ namespace ui {
const base::Feature kEnableHeuristicPalmDetectionFilter{
"EnableHeuristicPalmDetectionFilter", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kEnableHighResolutionMouseScrolling{
- "EnableHighResolutionMouseScrolling", base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kEnableNeuralPalmDetectionFilter{
"EnableNeuralPalmDetectionFilter", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromium/ui/events/ozone/features.h b/chromium/ui/events/ozone/features.h
index 9832b90bdac..8237ff40996 100644
--- a/chromium/ui/events/ozone/features.h
+++ b/chromium/ui/events/ozone/features.h
@@ -14,9 +14,6 @@ COMPONENT_EXPORT(EVENTS_OZONE)
extern const base::Feature kEnableHeuristicPalmDetectionFilter;
COMPONENT_EXPORT(EVENTS_OZONE)
-extern const base::Feature kEnableHighResolutionMouseScrolling;
-
-COMPONENT_EXPORT(EVENTS_OZONE)
extern const base::Feature kEnableNeuralPalmDetectionFilter;
COMPONENT_EXPORT(EVENTS_OZONE)
diff --git a/chromium/ui/events/ozone/keyboard_hook_ozone.cc b/chromium/ui/events/ozone/keyboard_hook_ozone.cc
index 991b7d720d9..f7853ef4a42 100644
--- a/chromium/ui/events/ozone/keyboard_hook_ozone.cc
+++ b/chromium/ui/events/ozone/keyboard_hook_ozone.cc
@@ -2,34 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/events/keyboard_hook_base.h"
+#include "ui/events/ozone/keyboard_hook_ozone.h"
#include <utility>
#include "base/callback.h"
#include "base/macros.h"
#include "base/optional.h"
+#include "build/build_config.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/gfx/native_widget_types.h"
namespace ui {
-namespace {
-
-// A default implementation for Ozone platform.
-class KeyboardHookOzone : public KeyboardHookBase {
- public:
- KeyboardHookOzone(base::Optional<base::flat_set<DomCode>> dom_codes,
- KeyEventCallback callback);
- ~KeyboardHookOzone() override;
-
- bool Register();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(KeyboardHookOzone);
-};
-
KeyboardHookOzone::KeyboardHookOzone(
base::Optional<base::flat_set<DomCode>> dom_codes,
KeyEventCallback callback)
@@ -37,14 +23,13 @@ KeyboardHookOzone::KeyboardHookOzone(
KeyboardHookOzone::~KeyboardHookOzone() = default;
-bool KeyboardHookOzone::Register() {
+bool KeyboardHookOzone::RegisterHook() {
// TODO(680809): Implement system-level keyboard lock feature for ozone.
// Return true to enable browser-level keyboard lock for ozone platform.
return true;
}
-} // namespace
-
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// static
std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
base::Optional<base::flat_set<DomCode>> dom_codes,
@@ -54,7 +39,7 @@ std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
std::make_unique<KeyboardHookOzone>(std::move(dom_codes),
std::move(callback));
- if (!keyboard_hook->Register())
+ if (!keyboard_hook->RegisterHook())
return nullptr;
return keyboard_hook;
@@ -65,5 +50,6 @@ std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
KeyEventCallback callback) {
return nullptr;
}
+#endif
} // namespace ui
diff --git a/chromium/ui/events/ozone/keyboard_hook_ozone.h b/chromium/ui/events/ozone/keyboard_hook_ozone.h
new file mode 100644
index 00000000000..5da0678b466
--- /dev/null
+++ b/chromium/ui/events/ozone/keyboard_hook_ozone.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_OZONE_KEYBOARD_HOOK_OZONE_H_
+#define UI_EVENTS_OZONE_KEYBOARD_HOOK_OZONE_H_
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "ui/events/keyboard_hook_base.h"
+
+namespace ui {
+
+// A default implementation for Ozone platform.
+class KeyboardHookOzone : public KeyboardHookBase {
+ public:
+ KeyboardHookOzone(base::Optional<base::flat_set<DomCode>> dom_codes,
+ KeyEventCallback callback);
+ KeyboardHookOzone(const KeyboardHookOzone&) = delete;
+ KeyboardHookOzone& operator=(const KeyboardHookOzone&) = delete;
+ ~KeyboardHookOzone() override;
+
+ // KeyboardHookBase:
+ bool RegisterHook() override;
+};
+
+} // namespace ui
+
+#endif // UI_EVENTS_OZONE_KEYBOARD_HOOK_OZONE_H_
diff --git a/chromium/ui/events/platform/BUILD.gn b/chromium/ui/events/platform/BUILD.gn
index dfceb31d1de..64efb0bbc34 100644
--- a/chromium/ui/events/platform/BUILD.gn
+++ b/chromium/ui/events/platform/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
-jumbo_component("platform") {
+component("platform") {
sources = [
# Allow this target to include events_export.h without depending on the
# events target (which would be circular).
diff --git a/chromium/ui/events/platform/x11/BUILD.gn b/chromium/ui/events/platform/x11/BUILD.gn
index 20ea6d8a4d9..7fcea67f705 100644
--- a/chromium/ui/events/platform/x11/BUILD.gn
+++ b/chromium/ui/events/platform/x11/BUILD.gn
@@ -2,13 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
assert(use_x11 || ozone_platform_x11)
-jumbo_component("x11") {
+component("x11") {
output_name = "x11_events_platform"
sources = [
@@ -34,6 +33,7 @@ jumbo_component("x11") {
deps = [
"//base",
+ "//ui/base:features",
"//ui/events/keycodes:x11",
]
@@ -54,4 +54,8 @@ jumbo_component("x11") {
"x11_event_watcher_fdwatch.h",
]
}
+
+ if (use_ozone) {
+ deps += [ "//ui/base:features" ]
+ }
}
diff --git a/chromium/ui/events/platform/x11/DEPS b/chromium/ui/events/platform/x11/DEPS
index 8d590992a5c..c334b82f33b 100644
--- a/chromium/ui/events/platform/x11/DEPS
+++ b/chromium/ui/events/platform/x11/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+ui/base/x",
+ "+ui/base/ui_base_features.h",
]
diff --git a/chromium/ui/events/platform/x11/OWNERS b/chromium/ui/events/platform/x11/OWNERS
new file mode 100644
index 00000000000..280ba478dca
--- /dev/null
+++ b/chromium/ui/events/platform/x11/OWNERS
@@ -0,0 +1 @@
+thomasanderson@chromium.org
diff --git a/chromium/ui/events/platform/x11/x11_event_source.cc b/chromium/ui/events/platform/x11/x11_event_source.cc
index 4922e59c450..403c75981a5 100644
--- a/chromium/ui/events/platform/x11/x11_event_source.cc
+++ b/chromium/ui/events/platform/x11/x11_event_source.cc
@@ -16,6 +16,7 @@
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/memory/free_deleter.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/metrics/histogram_macros.h"
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
@@ -27,8 +28,10 @@
#include "ui/events/x/x11_event_translation.h"
#include "ui/events/x/x11_window_event_manager.h"
#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/gfx/x/xkb.h"
#include "ui/gfx/x/xproto.h"
#if defined(USE_GLIB)
@@ -41,70 +44,83 @@
#include "ui/events/ozone/chromeos/cursor_controller.h"
#endif
+#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
+#endif
+
namespace ui {
namespace {
-bool InitializeXkb(XDisplay* display) {
- if (!display)
- return false;
+void InitializeXkb(x11::Connection* connection) {
+ if (!connection)
+ return;
- int opcode, event, error;
- int major = XkbMajorVersion;
- int minor = XkbMinorVersion;
- if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
- DVLOG(1) << "Xkb extension not available.";
- return false;
- }
+ auto& xkb = connection->xkb();
+
+ xkb.UseExtension({x11::Xkb::major_version, x11::Xkb::minor_version})
+ .OnResponse(base::BindOnce([](x11::Xkb::UseExtensionResponse response) {
+ if (!response || !response->supported)
+ DVLOG(1) << "Xkb extension not available.";
+ }));
// Ask the server not to send KeyRelease event when the user holds down a key.
// crbug.com/138092
- x11::Bool supported_return;
- if (!XkbSetDetectableAutoRepeat(display, x11::True, &supported_return)) {
- DVLOG(1) << "XKB not supported in the server.";
- return false;
- }
-
- return true;
+ xkb
+ .PerClientFlags({
+ .deviceSpec =
+ static_cast<x11::Xkb::DeviceSpec>(x11::Xkb::Id::UseCoreKbd),
+ .change = x11::Xkb::PerClientFlag::DetectableAutoRepeat,
+ .value = x11::Xkb::PerClientFlag::DetectableAutoRepeat,
+ })
+ .OnResponse(base::BindOnce([](x11::Xkb::PerClientFlagsResponse response) {
+ if (!response ||
+ !static_cast<bool>(response->supported &
+ x11::Xkb::PerClientFlag::DetectableAutoRepeat)) {
+ DVLOG(1) << "Could not set XKB auto repeat flag.";
+ }
+ }));
+
+ constexpr auto kXkbAllMapPartMask = static_cast<x11::Xkb::MapPart>(0xff);
+ xkb.SelectEvents({
+ .deviceSpec = static_cast<x11::Xkb::DeviceSpec>(x11::Xkb::Id::UseCoreKbd),
+ .affectWhich = x11::Xkb::EventType::NewKeyboardNotify,
+ .selectAll = x11::Xkb::EventType::NewKeyboardNotify,
+ .affectMap = kXkbAllMapPartMask,
+ });
}
-Time ExtractTimeFromXEvent(const x11::Event& x11_event) {
- const XEvent& xevent = x11_event.xlib_event();
-
- switch (xevent.type) {
- case x11::KeyEvent::Press:
- case x11::KeyEvent::Release:
- return xevent.xkey.time;
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release:
- return xevent.xbutton.time;
- case x11::MotionNotifyEvent::opcode:
- return xevent.xmotion.time;
- case x11::CrossingEvent::EnterNotify:
- case x11::CrossingEvent::LeaveNotify:
- return xevent.xcrossing.time;
- case x11::PropertyNotifyEvent::opcode:
- return xevent.xproperty.time;
- case x11::SelectionClearEvent::opcode:
- return xevent.xselectionclear.time;
- case x11::SelectionRequestEvent::opcode:
- return xevent.xselectionrequest.time;
- case x11::SelectionNotifyEvent::opcode:
- return xevent.xselection.time;
- case x11::GeGenericEvent::opcode:
- if (DeviceDataManagerX11::GetInstance()->IsXIDeviceEvent(x11_event))
- return static_cast<XIDeviceEvent*>(xevent.xcookie.data)->time;
- else
- break;
- }
- return x11::CurrentTime;
+x11::Time ExtractTimeFromXEvent(const x11::Event& xev) {
+ if (auto* key = xev.As<x11::KeyEvent>())
+ return key->time;
+ if (auto* button = xev.As<x11::ButtonEvent>())
+ return button->time;
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>())
+ return motion->time;
+ if (auto* crossing = xev.As<x11::CrossingEvent>())
+ return crossing->time;
+ if (auto* prop = xev.As<x11::PropertyNotifyEvent>())
+ return prop->time;
+ if (auto* sel_clear = xev.As<x11::SelectionClearEvent>())
+ return sel_clear->time;
+ if (auto* sel_req = xev.As<x11::SelectionRequestEvent>())
+ return sel_req->time;
+ if (auto* sel_notify = xev.As<x11::SelectionNotifyEvent>())
+ return sel_notify->time;
+ if (auto* dev_changed = xev.As<x11::Input::DeviceChangedEvent>())
+ return dev_changed->time;
+ if (auto* device = xev.As<x11::Input::DeviceEvent>())
+ return device->time;
+ if (auto* xi_crossing = xev.As<x11::Input::CrossingEvent>())
+ return xi_crossing->time;
+ return x11::Time::CurrentTime;
}
void UpdateDeviceList() {
- XDisplay* display = gfx::GetXDisplay();
- DeviceListCacheX11::GetInstance()->UpdateDeviceList(display);
- TouchFactory::GetInstance()->UpdateDeviceList(display);
- DeviceDataManagerX11::GetInstance()->UpdateDeviceList(display);
+ auto* connection = x11::Connection::Get();
+ DeviceListCacheX11::GetInstance()->UpdateDeviceList(connection);
+ TouchFactory::GetInstance()->UpdateDeviceList(connection);
+ DeviceDataManagerX11::GetInstance()->UpdateDeviceList(connection);
}
} // namespace
@@ -115,39 +131,32 @@ using X11EventWatcherImpl = X11EventWatcherGlib;
using X11EventWatcherImpl = X11EventWatcherFdWatch;
#endif
-X11EventSource* X11EventSource::instance_ = nullptr;
-
-X11EventSource::X11EventSource(XDisplay* display)
+X11EventSource::X11EventSource(x11::Connection* connection)
: watcher_(std::make_unique<X11EventWatcherImpl>(this)),
- display_(display),
+ connection_(connection),
dispatching_event_(nullptr),
dummy_initialized_(false),
distribution_(0, 999) {
- DCHECK(!instance_);
- instance_ = this;
-
- DCHECK(display_);
+ DCHECK(connection_);
DeviceDataManagerX11::CreateInstance();
- InitializeXkb(display_);
+ InitializeXkb(connection_);
watcher_->StartWatching();
}
X11EventSource::~X11EventSource() {
- DCHECK_EQ(this, instance_);
- instance_ = nullptr;
if (dummy_initialized_)
- XDestroyWindow(display_, static_cast<uint32_t>(dummy_window_));
+ connection_->DestroyWindow({dummy_window_});
}
+// static
bool X11EventSource::HasInstance() {
- return instance_;
+ return GetInstance();
}
// static
X11EventSource* X11EventSource::GetInstance() {
- DCHECK(instance_);
- return instance_;
+ return static_cast<X11EventSource*>(PlatformEventSource::GetInstance());
}
////////////////////////////////////////////////////////////////////////////////
@@ -155,16 +164,22 @@ X11EventSource* X11EventSource::GetInstance() {
void X11EventSource::DispatchXEvents() {
continue_stream_ = true;
- x11::Connection::Get()->Dispatch(this);
+ connection_->Dispatch(this);
}
-Time X11EventSource::GetCurrentServerTime() {
- DCHECK(display_);
+x11::Time X11EventSource::GetCurrentServerTime() {
+ DCHECK(connection_);
if (!dummy_initialized_) {
// Create a new Window and Atom that will be used for the property change.
- dummy_window_ = static_cast<x11::Window>(XCreateSimpleWindow(
- display_, DefaultRootWindow(display_), 0, 0, 1, 1, 0, 0, 0));
+ dummy_window_ = connection_->GenerateId<x11::Window>();
+ connection_->CreateWindow({
+ .wid = dummy_window_,
+ .parent = connection_->default_root(),
+ .width = 1,
+ .height = 1,
+ .override_redirect = x11::Bool32(true),
+ });
dummy_atom_ = gfx::GetAtom("CHROMIUM_TIMESTAMP");
dummy_window_events_ = std::make_unique<XScopedEventSelector>(
dummy_window_, PropertyChangeMask);
@@ -180,47 +195,46 @@ Time X11EventSource::GetCurrentServerTime() {
start = base::TimeTicks::Now();
// Make a no-op property change on |dummy_window_|.
- auto* connection = x11::Connection::Get();
- connection->ChangeProperty({
+ std::vector<uint8_t> data{0};
+ connection_->ChangeProperty({
.window = static_cast<x11::Window>(dummy_window_),
.property = dummy_atom_,
.type = x11::Atom::STRING,
.format = CHAR_BIT,
.data_len = 1,
- .data = std::vector<uint8_t>{0},
+ .data = base::RefCountedBytes::TakeVector(&data),
});
// Observe the resulting PropertyNotify event to obtain the timestamp.
- connection->Sync();
+ connection_->Sync();
if (measure_rtt) {
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Linux.X11.ServerRTT",
(base::TimeTicks::Now() - start).InMicroseconds(), 1,
base::TimeDelta::FromMilliseconds(50).InMicroseconds(), 50);
}
- connection->ReadResponses();
+ connection_->ReadResponses();
- Time time = x11::CurrentTime;
+ auto time = x11::Time::CurrentTime;
auto pred = [&](const x11::Event& event) {
- if (event.xlib_event().type == x11::PropertyNotifyEvent::opcode &&
- event.xlib_event().xproperty.window ==
- static_cast<uint32_t>(dummy_window_)) {
- time = event.xlib_event().xproperty.time;
+ auto* prop = event.As<x11::PropertyNotifyEvent>();
+ if (prop && prop->window == dummy_window_) {
+ time = prop->time;
return true;
}
return false;
};
- auto& events = connection->events();
+ auto& events = connection_->events();
events.erase(std::remove_if(events.begin(), events.end(), pred),
events.end());
return time;
}
-Time X11EventSource::GetTimestamp() {
+x11::Time X11EventSource::GetTimestamp() {
if (dispatching_event_) {
- Time timestamp = ExtractTimeFromXEvent(*dispatching_event_);
- if (timestamp != x11::CurrentTime)
+ auto timestamp = ExtractTimeFromXEvent(*dispatching_event_);
+ if (timestamp != x11::Time::CurrentTime)
return timestamp;
}
DVLOG(1) << "Making a round trip to get a recent server timestamp.";
@@ -233,29 +247,25 @@ X11EventSource::GetRootCursorLocationFromCurrentEvent() const {
return base::nullopt;
DCHECK(dispatching_event_);
- XEvent* event = &dispatching_event_->xlib_event();
+ x11::Event* event = dispatching_event_;
- bool is_xi2_event = event->type == x11::GeGenericEvent::opcode;
- int event_type = is_xi2_event
- ? reinterpret_cast<XIDeviceEvent*>(event)->evtype
- : event->type;
+ auto* device = event->As<x11::Input::DeviceEvent>();
+ auto* crossing = event->As<x11::Input::CrossingEvent>();
+ auto* touch_factory = ui::TouchFactory::GetInstance();
bool is_valid_event = false;
- static_assert(XI_ButtonPress == x11::ButtonEvent::Press, "");
- static_assert(XI_ButtonRelease == x11::ButtonEvent::Release, "");
- static_assert(XI_Motion == x11::MotionNotifyEvent::opcode, "");
- static_assert(XI_Enter == x11::CrossingEvent::EnterNotify, "");
- static_assert(XI_Leave == x11::CrossingEvent::LeaveNotify, "");
- switch (event_type) {
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release:
- case x11::MotionNotifyEvent::opcode:
- case x11::CrossingEvent::EnterNotify:
- case x11::CrossingEvent::LeaveNotify:
- is_valid_event =
- is_xi2_event
- ? ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(event)
- : true;
+ if (event->As<x11::ButtonEvent>() || event->As<x11::MotionNotifyEvent>() ||
+ event->As<x11::CrossingEvent>()) {
+ is_valid_event = true;
+ } else if (device &&
+ (device->opcode == x11::Input::DeviceEvent::ButtonPress ||
+ device->opcode == x11::Input::DeviceEvent::ButtonRelease ||
+ device->opcode == x11::Input::DeviceEvent::Motion)) {
+ is_valid_event = touch_factory->ShouldProcessDeviceEvent(*device);
+ } else if (crossing &&
+ (crossing->opcode == x11::Input::CrossingEvent::Enter ||
+ crossing->opcode == x11::Input::CrossingEvent::Leave)) {
+ is_valid_event = touch_factory->ShouldProcessCrossingEvent(*crossing);
}
if (is_valid_event)
@@ -388,21 +398,16 @@ void X11EventSource::ProcessXEvent(x11::Event* xevent) {
// X11EventSource, protected
void X11EventSource::PostDispatchEvent(x11::Event* x11_event) {
- XEvent* xevent = &x11_event->xlib_event();
bool should_update_device_list = false;
- if (xevent->type == x11::GeGenericEvent::opcode) {
- if (xevent->xgeneric.evtype == XI_HierarchyChanged) {
+ if (x11_event->As<x11::Input::HierarchyEvent>()) {
+ should_update_device_list = true;
+ } else if (auto* device = x11_event->As<x11::Input::DeviceChangedEvent>()) {
+ if (device->reason == x11::Input::ChangeReason::DeviceChange) {
should_update_device_list = true;
- } else if (xevent->xgeneric.evtype == XI_DeviceChanged) {
- XIDeviceChangedEvent* xev =
- static_cast<XIDeviceChangedEvent*>(xevent->xcookie.data);
- if (xev->reason == XIDeviceChange) {
- should_update_device_list = true;
- } else if (xev->reason == XISlaveSwitch) {
- ui::DeviceDataManagerX11::GetInstance()->InvalidateScrollClasses(
- xev->sourceid);
- }
+ } else if (device->reason == x11::Input::ChangeReason::SlaveSwitch) {
+ ui::DeviceDataManagerX11::GetInstance()->InvalidateScrollClasses(
+ device->sourceid);
}
}
@@ -411,13 +416,18 @@ void X11EventSource::PostDispatchEvent(x11::Event* x11_event) {
hotplug_event_handler_->OnHotplugEvent();
}
- if (xevent->type == x11::CrossingEvent::EnterNotify &&
- xevent->xcrossing.detail != NotifyInferior &&
- xevent->xcrossing.mode != NotifyUngrab) {
+ auto* crossing = x11_event->As<x11::CrossingEvent>();
+ if (crossing && crossing->opcode == x11::CrossingEvent::EnterNotify &&
+ crossing->detail != x11::NotifyDetail::Inferior &&
+ crossing->mode != x11::NotifyMode::Ungrab) {
// Clear stored scroll data
ui::DeviceDataManagerX11::GetInstance()->InvalidateScrollClasses(
DeviceDataManagerX11::kAllDevices);
}
+
+ auto* mapping = x11_event->As<x11::MappingNotifyEvent>();
+ if (mapping && mapping->request == x11::Mapping::Pointer)
+ DeviceDataManagerX11::GetInstance()->UpdateButtonMap();
}
void X11EventSource::StopCurrentEventStream() {
@@ -464,9 +474,13 @@ ScopedXEventDispatcher::~ScopedXEventDispatcher() {
}
// static
-#if !defined(USE_OZONE)
+#if defined(USE_X11)
std::unique_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() {
- return std::make_unique<X11EventSource>(gfx::GetXDisplay());
+#if defined(USE_OZONE)
+ if (features::IsUsingOzonePlatform())
+ return nullptr;
+#endif
+ return std::make_unique<X11EventSource>(x11::Connection::Get());
}
#endif
diff --git a/chromium/ui/events/platform/x11/x11_event_source.h b/chromium/ui/events/platform/x11/x11_event_source.h
index c8bb6e0c1ce..0e428776c52 100644
--- a/chromium/ui/events/platform/x11/x11_event_source.h
+++ b/chromium/ui/events/platform/x11/x11_event_source.h
@@ -19,9 +19,6 @@
#include "ui/gfx/x/event.h"
#include "ui/gfx/x/x11_types.h"
-using Time = unsigned long;
-using XEvent = union _XEvent;
-
namespace gfx {
class Point;
}
@@ -122,7 +119,7 @@ class EVENTS_EXPORT ScopedXEventDispatcher {
class EVENTS_EXPORT X11EventSource : public PlatformEventSource,
public x11::Connection::Delegate {
public:
- explicit X11EventSource(XDisplay* display);
+ explicit X11EventSource(x11::Connection* connection);
~X11EventSource() override;
static bool HasInstance();
@@ -132,12 +129,12 @@ class EVENTS_EXPORT X11EventSource : public PlatformEventSource,
// available X events.
void DispatchXEvents();
- XDisplay* display() { return display_; }
+ x11::Connection* connection() { return connection_; }
// Returns the timestamp of the event currently being dispatched. Falls back
// on GetCurrentServerTime() if there's no event being dispatched, or if the
// current event does not have a timestamp.
- Time GetTimestamp();
+ x11::Time GetTimestamp();
// Returns the root pointer location only if there is an event being
// dispatched that contains that information.
@@ -145,7 +142,7 @@ class EVENTS_EXPORT X11EventSource : public PlatformEventSource,
// Explicitly asks the X11 server for the current timestamp, and updates
// |last_seen_server_time_| with this value.
- Time GetCurrentServerTime();
+ x11::Time GetCurrentServerTime();
// Adds a x11::Event dispatcher to the x11::Event dispatcher list.
// Also calls XEventDispatcher::GetPlatformEventDispatcher
@@ -203,12 +200,10 @@ class EVENTS_EXPORT X11EventSource : public PlatformEventSource,
void RestoreOverridenXEventDispatcher();
- static X11EventSource* instance_;
-
std::unique_ptr<X11EventWatcher> watcher_;
// The connection to the X11 server used to receive the events.
- XDisplay* display_;
+ x11::Connection* connection_;
// Event currently being dispatched.
x11::Event* dispatching_event_;
diff --git a/chromium/ui/events/platform/x11/x11_event_watcher_fdwatch.cc b/chromium/ui/events/platform/x11/x11_event_watcher_fdwatch.cc
index e5042b52d4d..b71d1ca8180 100644
--- a/chromium/ui/events/platform/x11/x11_event_watcher_fdwatch.cc
+++ b/chromium/ui/events/platform/x11/x11_event_watcher_fdwatch.cc
@@ -4,7 +4,7 @@
#include "ui/events/platform/x11/x11_event_watcher_fdwatch.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
#include "ui/gfx/x/x11.h"
namespace ui {
@@ -17,13 +17,13 @@ X11EventWatcherFdWatch::~X11EventWatcherFdWatch() {
}
void X11EventWatcherFdWatch::StartWatching() {
- if (started_ || !base::MessageLoopCurrent::Get())
+ if (started_ || !base::CurrentThread::Get())
return;
- DCHECK(event_source_->display()) << "Unable to get connection to X server";
+ DCHECK(event_source_->connection()) << "Unable to get connection to X server";
- int fd = ConnectionNumber(event_source_->display());
- base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+ int fd = ConnectionNumber(event_source_->connection()->display());
+ base::CurrentUIThread::Get()->WatchFileDescriptor(
fd, true, base::MessagePumpForUI::WATCH_READ, &watcher_controller_, this);
started_ = true;
}
diff --git a/chromium/ui/events/platform/x11/x11_event_watcher_glib.cc b/chromium/ui/events/platform/x11/x11_event_watcher_glib.cc
index bedf678b167..17d738954da 100644
--- a/chromium/ui/events/platform/x11/x11_event_watcher_glib.cc
+++ b/chromium/ui/events/platform/x11/x11_event_watcher_glib.cc
@@ -60,8 +60,9 @@ void X11EventWatcherGlib::StartWatching() {
if (started_)
return;
- DCHECK(event_source_->display()) << "Unable to get connection to X server";
- Display* display = event_source_->display();
+ XDisplay* display = event_source_->connection()->display();
+ if (!display)
+ return;
x_poll_ = std::make_unique<GPollFD>();
x_poll_->fd = ConnectionNumber(display);
@@ -77,7 +78,10 @@ void X11EventWatcherGlib::StartWatching() {
g_source_add_poll(x_source_, x_poll_.get());
g_source_set_can_recurse(x_source_, TRUE);
g_source_set_callback(x_source_, nullptr, event_source_, nullptr);
- g_source_attach(x_source_, g_main_context_default());
+ auto* context = g_main_context_get_thread_default();
+ if (!context)
+ context = g_main_context_default();
+ g_source_attach(x_source_, context);
started_ = true;
}
diff --git a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc
index 1a4c8dc35b7..30a5bb7f46c 100644
--- a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc
+++ b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc
@@ -8,6 +8,7 @@
#include <algorithm>
#include <cmath>
+#include <limits>
#include <set>
#include <string>
#include <vector>
@@ -15,6 +16,7 @@
#include "base/bind.h"
#include "base/check.h"
#include "base/command_line.h"
+#include "base/files/file_path.h"
#include "base/location.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
@@ -28,14 +30,11 @@
#include "ui/events/devices/device_util_linux.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/touchscreen_device.h"
+#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_types.h"
-#ifndef XI_PROP_PRODUCT_ID
-#define XI_PROP_PRODUCT_ID "Device Product ID"
-#endif
-
namespace ui {
namespace {
@@ -74,71 +73,71 @@ struct UiCallbacks {
base::OnceClosure hotplug_finished_callback;
};
-// Stores a copy of the XIValuatorClassInfo values so X11 device processing can
-// happen on a worker thread. This is needed since X11 structs are not copyable.
+// Identical to FP3232_TO_DOUBLE from libxi's XExtInt.c
+double Fp3232ToDouble(const x11::Input::Fp3232& x) {
+ return static_cast<double>(x.integral) +
+ static_cast<double>(x.frac) / (1ULL << 32);
+}
+
+// Stores a copy of the x11::Input::ValuatorClass values so X11 device
+// processing can happen on a worker thread. This is needed since X11 structs
+// are not copyable.
struct ValuatorClassInfo {
- explicit ValuatorClassInfo(const XIValuatorClassInfo& info)
+ explicit ValuatorClassInfo(const x11::Input::ValuatorClass& info)
: label(static_cast<x11::Atom>(info.label)),
- max(info.max),
- min(info.min),
+ max(Fp3232ToDouble(info.max)),
+ min(Fp3232ToDouble(info.min)),
mode(info.mode),
number(info.number) {}
x11::Atom label;
double max;
double min;
- int mode;
- int number;
+ x11::Input::ValuatorMode mode;
+ uint16_t number;
};
// Stores a copy of the XITouchClassInfo values so X11 device processing can
// happen on a worker thread. This is needed since X11 structs are not copyable.
struct TouchClassInfo {
- TouchClassInfo() : mode(0), num_touches(0) {}
+ TouchClassInfo() = default;
- explicit TouchClassInfo(const XITouchClassInfo& info)
+ explicit TouchClassInfo(const x11::Input::DeviceClass::Touch& info)
: mode(info.mode), num_touches(info.num_touches) {}
- int mode;
- int num_touches;
+ x11::Input::TouchMode mode{};
+ int num_touches = 0;
};
struct DeviceInfo {
- DeviceInfo(const XIDeviceInfo& device,
+ DeviceInfo(const x11::Input::XIDeviceInfo& device,
DeviceType type,
const base::FilePath& path)
: id(device.deviceid),
name(device.name),
- use(device.use),
+ use(device.type),
type(type),
path(path) {
- for (int i = 0; i < device.num_classes; ++i) {
- switch (device.classes[i]->type) {
- case XIValuatorClass:
- valuator_class_infos.push_back(ValuatorClassInfo(
- *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
- break;
- case XITouchClass:
- // A device can have at most one XITouchClassInfo. Ref:
- // http://manpages.ubuntu.com/manpages/saucy/man3/XIQueryDevice.3.html
- DCHECK(!touch_class_info.mode);
- touch_class_info = TouchClassInfo(
- *reinterpret_cast<XITouchClassInfo*>(device.classes[i]));
- break;
- default:
- break;
+ for (const auto& device_class : device.classes) {
+ if (device_class.valuator.has_value()) {
+ valuator_class_infos.emplace_back(*device_class.valuator);
+ } else if (device_class.touch.has_value()) {
+ // A device can have at most one XITouchClassInfo. Ref:
+ // http://manpages.ubuntu.com/manpages/saucy/man3/XIQueryDevice.3.html
+ DCHECK(!touch_class_info.num_touches);
+ touch_class_info = TouchClassInfo(*device_class.touch);
}
}
}
// Unique device identifier.
- int id;
+ x11::Input::DeviceId id;
// Internal device name.
std::string name;
// Device type (ie: XIMasterPointer)
- int use;
+ x11::Input::DeviceType use;
// Specifies the type of the device.
DeviceType type;
@@ -146,7 +145,7 @@ struct DeviceInfo {
// Path to the actual device (ie: /dev/input/eventXX)
base::FilePath path;
- std::vector<ValuatorClassInfo> valuator_class_infos;
+ std::vector<x11::Input::DeviceClass::Valuator> valuator_class_infos;
TouchClassInfo touch_class_info;
};
@@ -176,10 +175,12 @@ bool IsTestDevice(const std::string& name) {
return name.find("XTEST") != std::string::npos;
}
-base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
+base::FilePath GetDevicePath(x11::Connection* connection,
+ const x11::Input::XIDeviceInfo& device) {
// Skip the main pointer and keyboard since XOpenDevice() generates a
// BadDevice error when passed these devices.
- if (device.use == XIMasterPointer || device.use == XIMasterKeyboard)
+ if (device.type == x11::Input::DeviceType::MasterPointer ||
+ device.type == x11::Input::DeviceType::MasterKeyboard)
return base::FilePath();
// Input device has a property "Device Node" pointing to its dev input node,
@@ -188,33 +189,30 @@ base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) {
if (device_node == x11::Atom::None)
return base::FilePath();
- Atom actual_type;
- int actual_format;
- unsigned long nitems, bytes_after;
- unsigned char* data;
- XDevice* dev = XOpenDevice(dpy, device.deviceid);
-
- // Sometimes XOpenDevice() doesn't return null but the contents aren't valid.
- // Calling XGetDeviceProperty() when dev->device_id is invalid triggers a
- // BadDevice error. Return early to avoid a crash. http://crbug.com/659261
- if (!dev || dev->device_id != base::checked_cast<XID>(device.deviceid))
+ auto deviceid = static_cast<uint16_t>(device.deviceid);
+ if (deviceid > std::numeric_limits<uint8_t>::max())
+ return base::FilePath();
+ if (connection->xinput().OpenDevice({deviceid}).Sync().error)
return base::FilePath();
- if (XGetDeviceProperty(dpy, dev, static_cast<uint32_t>(device_node), 0, 1000,
- x11::False, AnyPropertyType, &actual_type,
- &actual_format, &nitems, &bytes_after,
- &data) != x11::Success) {
- XCloseDevice(dpy, dev);
+ x11::Input::GetDevicePropertyRequest req{
+ .property = device_node,
+ .type = x11::Atom::Any,
+ .offset = 0,
+ .len = std::numeric_limits<uint32_t>::max(),
+ .device_id = deviceid,
+ .c_delete = false,
+ };
+ auto reply = connection->xinput().GetDeviceProperty(req).Sync();
+ if (!reply || reply->type != x11::Atom::STRING || !reply->data8.has_value()) {
+ connection->xinput().CloseDevice({deviceid});
return base::FilePath();
}
- std::string path;
- // Make sure the returned value is a string.
- if (actual_type == XA_STRING && actual_format == 8)
- path = reinterpret_cast<char*>(data);
+ std::string path(reinterpret_cast<char*>(reply->data8->data()),
+ reply->data8->size());
- XFree(data);
- XCloseDevice(dpy, dev);
+ connection->xinput().CloseDevice({deviceid});
return base::FilePath(path);
}
@@ -229,13 +227,13 @@ void HandleKeyboardDevicesInWorker(const std::vector<DeviceInfo>& device_infos,
for (const DeviceInfo& device_info : device_infos) {
if (device_info.type != DEVICE_TYPE_KEYBOARD)
continue;
- if (device_info.use != XISlaveKeyboard)
+ if (device_info.use != x11::Input::DeviceType::SlaveKeyboard)
continue; // Assume all keyboards are keyboard slaves
if (IsKnownInvalidKeyboardDevice(device_info.name))
continue; // Skip invalid devices.
InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
- InputDevice keyboard(device_info.id, type, device_info.name);
- devices.push_back(keyboard);
+ devices.emplace_back(static_cast<uint16_t>(device_info.id), type,
+ device_info.name);
}
reply_runner->PostTask(FROM_HERE,
@@ -250,12 +248,13 @@ void HandleMouseDevicesInWorker(const std::vector<DeviceInfo>& device_infos,
std::vector<InputDevice> devices;
for (const DeviceInfo& device_info : device_infos) {
if (device_info.type != DEVICE_TYPE_MOUSE ||
- device_info.use != XISlavePointer) {
+ device_info.use != x11::Input::DeviceType::SlavePointer) {
continue;
}
InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
- devices.push_back(InputDevice(device_info.id, type, device_info.name));
+ devices.emplace_back(static_cast<uint16_t>(device_info.id), type,
+ device_info.name);
}
reply_runner->PostTask(FROM_HERE,
@@ -270,12 +269,13 @@ void HandleTouchpadDevicesInWorker(const std::vector<DeviceInfo>& device_infos,
std::vector<InputDevice> devices;
for (const DeviceInfo& device_info : device_infos) {
if (device_info.type != DEVICE_TYPE_TOUCHPAD ||
- device_info.use != XISlavePointer) {
+ device_info.use != x11::Input::DeviceType::SlavePointer) {
continue;
}
InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path);
- devices.push_back(InputDevice(device_info.id, type, device_info.name));
+ devices.emplace_back(static_cast<uint16_t>(device_info.id), type,
+ device_info.name);
}
reply_runner->PostTask(FROM_HERE,
@@ -296,30 +296,32 @@ void HandleTouchscreenDevicesInWorker(
for (const DeviceInfo& device_info : device_infos) {
if (device_info.type != DEVICE_TYPE_TOUCHSCREEN ||
- (device_info.use != XIFloatingSlave &&
- device_info.use != XISlavePointer)) {
+ (device_info.use != x11::Input::DeviceType::FloatingSlave &&
+ device_info.use != x11::Input::DeviceType::SlavePointer)) {
continue;
}
// Touchscreens should be direct touch devices.
- if (device_info.touch_class_info.mode != XIDirectTouch)
+ if (device_info.touch_class_info.mode != x11::Input::TouchMode::Direct)
continue;
double max_x = -1.0;
double max_y = -1.0;
- for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) {
+ for (const auto& valuator : device_info.valuator_class_infos) {
if (display_state.mt_position_x == valuator.label) {
// Ignore X axis valuator with unexpected properties
- if (valuator.number == 0 && valuator.mode == Absolute &&
- valuator.min == 0.0) {
- max_x = valuator.max;
+ if (valuator.number == 0 &&
+ valuator.mode == x11::Input::ValuatorMode::Absolute &&
+ Fp3232ToDouble(valuator.min) == 0.0) {
+ max_x = Fp3232ToDouble(valuator.max);
}
} else if (display_state.mt_position_y == valuator.label) {
// Ignore Y axis valuator with unexpected properties
- if (valuator.number == 1 && valuator.mode == Absolute &&
- valuator.min == 0.0) {
- max_y = valuator.max;
+ if (valuator.number == 1 &&
+ valuator.mode == x11::Input::ValuatorMode::Absolute &&
+ Fp3232ToDouble(valuator.min) == 0.0) {
+ max_y = Fp3232ToDouble(valuator.max);
}
}
}
@@ -331,10 +333,10 @@ void HandleTouchscreenDevicesInWorker(
const bool has_stylus = false;
// |max_x| and |max_y| are inclusive values, so we need to add 1 to get
// the size.
- devices.push_back(TouchscreenDevice(
- device_info.id, type, device_info.name,
- gfx::Size(max_x + 1, max_y + 1),
- device_info.touch_class_info.num_touches, has_stylus));
+ devices.emplace_back(static_cast<uint16_t>(device_info.id), type,
+ device_info.name, gfx::Size(max_x + 1, max_y + 1),
+ device_info.touch_class_info.num_touches,
+ has_stylus);
}
}
@@ -390,45 +392,43 @@ X11HotplugEventHandler::X11HotplugEventHandler() = default;
X11HotplugEventHandler::~X11HotplugEventHandler() = default;
void X11HotplugEventHandler::OnHotplugEvent() {
- Display* display = gfx::GetXDisplay();
+ auto* connection = x11::Connection::Get();
const XDeviceList& device_list_xi =
- DeviceListCacheX11::GetInstance()->GetXDeviceList(display);
+ DeviceListCacheX11::GetInstance()->GetXDeviceList(connection);
const XIDeviceList& device_list_xi2 =
- DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display);
+ DeviceListCacheX11::GetInstance()->GetXI2DeviceList(connection);
const int kMaxDeviceNum = 128;
DeviceType device_types[kMaxDeviceNum];
for (auto& device_type : device_types)
device_type = DEVICE_TYPE_OTHER;
- for (int i = 0; i < device_list_xi.count; ++i) {
- int id = device_list_xi[i].id;
- if (id < 0 || id >= kMaxDeviceNum)
+ for (const auto& device : device_list_xi) {
+ uint8_t id = device.device_id;
+ if (id >= kMaxDeviceNum)
continue;
- x11::Atom type = static_cast<x11::Atom>(device_list_xi[i].type);
- if (type == gfx::GetAtom(XI_KEYBOARD))
+ x11::Atom type = device.device_type;
+ if (type == gfx::GetAtom("KEYBOARD"))
device_types[id] = DEVICE_TYPE_KEYBOARD;
- else if (type == gfx::GetAtom(XI_MOUSE))
+ else if (type == gfx::GetAtom("MOUSE"))
device_types[id] = DEVICE_TYPE_MOUSE;
- else if (type == gfx::GetAtom(XI_TOUCHPAD))
+ else if (type == gfx::GetAtom("TOUCHPAD"))
device_types[id] = DEVICE_TYPE_TOUCHPAD;
- else if (type == gfx::GetAtom(XI_TOUCHSCREEN))
+ else if (type == gfx::GetAtom("TOUCHSCREEN"))
device_types[id] = DEVICE_TYPE_TOUCHSCREEN;
}
std::vector<DeviceInfo> device_infos;
- for (int i = 0; i < device_list_xi2.count; ++i) {
- const XIDeviceInfo& device = device_list_xi2[i];
+ for (const auto& device : device_list_xi2) {
if (!device.enabled || IsTestDevice(device.name))
continue;
+ auto deviceid = static_cast<uint16_t>(device.deviceid);
DeviceType device_type =
- (device.deviceid >= 0 && device.deviceid < kMaxDeviceNum)
- ? device_types[device.deviceid]
- : DEVICE_TYPE_OTHER;
- device_infos.push_back(
- DeviceInfo(device, device_type, GetDevicePath(display, device)));
+ deviceid < kMaxDeviceNum ? device_types[deviceid] : DEVICE_TYPE_OTHER;
+ device_infos.emplace_back(device, device_type,
+ GetDevicePath(connection, device));
}
// X11 is not thread safe, so first get all the required state.
diff --git a/chromium/ui/events/platform_event.h b/chromium/ui/events/platform_event.h
index 72b12d0117d..8ece4b87d50 100644
--- a/chromium/ui/events/platform_event.h
+++ b/chromium/ui/events/platform_event.h
@@ -9,7 +9,7 @@
#if defined(OS_WIN)
#include <windows.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#if defined(__OBJC__)
@class NSEvent;
#else // __OBJC__
@@ -28,7 +28,7 @@ namespace ui {
using PlatformEvent = ui::Event*;
#elif defined(OS_WIN)
using PlatformEvent = MSG;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
using PlatformEvent = NSEvent*;
#else
using PlatformEvent = void*;
diff --git a/chromium/ui/events/x/BUILD.gn b/chromium/ui/events/x/BUILD.gn
index 05d291d5981..2abef26141a 100644
--- a/chromium/ui/events/x/BUILD.gn
+++ b/chromium/ui/events/x/BUILD.gn
@@ -20,6 +20,7 @@ component("x") {
deps = [
"//base",
"//skia",
+ "//ui/base:features",
"//ui/display",
"//ui/events:events_base",
"//ui/events/devices",
@@ -28,27 +29,3 @@ component("x") {
"//ui/gfx/x",
]
}
-
-# XEvent translation tests are kept in a separate source_set so that both
-# ozone_x11_unittests and aura/x11 events_unittests can depend on it.
-# TODO(crbug.com/789065): Move this completely into ozone/x11 unit tests once
-# X11 migration to Ozone is complete.
-source_set("unittests") {
- testonly = true
- visibility = [
- "//ui/events:events_unittests",
- "//ui/ozone/platform/x11:x11_unittests",
- ]
- sources = [
- "events_x_unittest.cc",
- "x11_event_translation_unittest.cc",
- ]
- deps = [
- "//base/test:test_support",
- "//testing/gtest",
- "//ui/events:test_support",
- "//ui/events/devices/x11",
- "//ui/events/x:x",
- "//ui/gfx/x",
- ]
-}
diff --git a/chromium/ui/events/x/OWNERS b/chromium/ui/events/x/OWNERS
new file mode 100644
index 00000000000..2f2cf8fe42d
--- /dev/null
+++ b/chromium/ui/events/x/OWNERS
@@ -0,0 +1,3 @@
+thomasanderson@chromium.org
+
+# COMPONENT: UI>Input
diff --git a/chromium/ui/events/x/events_x_unittest.cc b/chromium/ui/events/x/events_x_unittest.cc
deleted file mode 100644
index 6933f8fbba9..00000000000
--- a/chromium/ui/events/x/events_x_unittest.cc
+++ /dev/null
@@ -1,583 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-#include <xcb/xcb.h>
-
-#include <cstring>
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "base/stl_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/devices/x11/device_data_manager_x11.h"
-#include "ui/events/devices/x11/touch_factory_x11.h"
-#include "ui/events/event.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/test/events_test_utils.h"
-#include "ui/events/test/events_test_utils_x11.h"
-#include "ui/events/test/scoped_event_test_tick_clock.h"
-#include "ui/events/types/event_type.h"
-#include "ui/events/x/events_x_utils.h"
-#include "ui/events/x/x11_event_translation.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/x/connection.h"
-#include "ui/gfx/x/event.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/xproto.h"
-
-namespace ui {
-
-namespace {
-
-// Initializes the passed-in Xlib event.
-void InitButtonEvent(x11::Event* event,
- bool is_press,
- const gfx::Point& location,
- int button,
- int state) {
- xcb_generic_event_t generic_event;
- memset(&generic_event, 0, sizeof(generic_event));
- auto* button_event =
- reinterpret_cast<xcb_button_press_event_t*>(&generic_event);
-
- // We don't bother setting fields that the event code doesn't use, such as
- // x_root/y_root and window/root/subwindow.
- button_event->response_type =
- is_press ? x11::ButtonEvent::Press : x11::ButtonEvent::Release;
- button_event->event_x = location.x();
- button_event->event_y = location.y();
- button_event->detail = button;
- button_event->state = state;
-
- *event = x11::Event(&generic_event, x11::Connection::Get());
-}
-
-#if !defined(OS_CHROMEOS)
-// Initializes the passed-in Xlib event.
-void InitKeyEvent(Display* display,
- x11::Event* event,
- bool is_press,
- int keycode,
- int state) {
- xcb_generic_event_t generic_event;
- memset(&generic_event, 0, sizeof(generic_event));
- auto* key_event = reinterpret_cast<xcb_key_press_event_t*>(&generic_event);
-
- // We don't bother setting fields that the event code doesn't use, such as
- // x_root/y_root and window/root/subwindow.
- key_event->response_type =
- is_press ? x11::KeyEvent::Press : x11::KeyEvent::Release;
- key_event->detail = keycode;
- key_event->state = state;
-
- *event = x11::Event(&generic_event, x11::Connection::Get());
-}
-#endif
-
-float ComputeRotationAngle(float twist) {
- float rotation_angle = twist;
- while (rotation_angle < 0)
- rotation_angle += 180.f;
- while (rotation_angle >= 180)
- rotation_angle -= 180.f;
- return rotation_angle;
-}
-
-std::string FlooredEventLocationString(const x11::Event& xev) {
- return gfx::ToFlooredPoint(gfx::PointF(ui::EventLocationFromXEvent(xev)))
- .ToString();
-}
-
-} // namespace
-
-class EventsXTest : public testing::Test {
- public:
- EventsXTest() = default;
- ~EventsXTest() override = default;
-
- void SetUp() override {
- DeviceDataManagerX11::CreateInstance();
- ui::TouchFactory::GetInstance()->ResetForTest();
- ResetTimestampRolloverCountersForTesting();
- }
- void TearDown() override { ResetTimestampRolloverCountersForTesting(); }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(EventsXTest);
-};
-
-TEST_F(EventsXTest, ButtonEvents) {
- x11::Event event;
- gfx::Point location(5, 10);
- gfx::Vector2d offset;
-
- InitButtonEvent(&event, true, location, 1, 0);
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON,
- ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
-
- InitButtonEvent(&event, true, location, 2, Button1Mask | ShiftMask);
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(
- ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN,
- ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON,
- ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
-
- InitButtonEvent(&event, false, location, 3, 0);
- EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON,
- ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
-
- // Scroll up.
- InitButtonEvent(&event, true, location, 4, 0);
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(0, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_NONE, ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
- offset = ui::GetMouseWheelOffsetFromXEvent(event);
- EXPECT_GT(offset.y(), 0);
- EXPECT_EQ(0, offset.x());
-
- // Scroll down.
- InitButtonEvent(&event, true, location, 5, 0);
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(0, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_NONE, ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
- offset = ui::GetMouseWheelOffsetFromXEvent(event);
- EXPECT_LT(offset.y(), 0);
- EXPECT_EQ(0, offset.x());
-
- // Scroll left.
- InitButtonEvent(&event, true, location, 6, 0);
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(0, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_NONE, ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
- offset = ui::GetMouseWheelOffsetFromXEvent(event);
- EXPECT_EQ(0, offset.y());
- EXPECT_GT(offset.x(), 0);
-
- // Scroll right.
- InitButtonEvent(&event, true, location, 7, 0);
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromXEvent(event));
- EXPECT_EQ(0, ui::EventFlagsFromXEvent(event));
- EXPECT_EQ(ui::EF_NONE, ui::GetChangedMouseButtonFlagsFromXEvent(event));
- EXPECT_EQ(location, ui::EventLocationFromXEvent(event));
- offset = ui::GetMouseWheelOffsetFromXEvent(event);
- EXPECT_EQ(0, offset.y());
- EXPECT_LT(offset.x(), 0);
-
- // TODO(derat): Test XInput code.
-}
-
-TEST_F(EventsXTest, AvoidExtraEventsOnWheelRelease) {
- x11::Event event;
- gfx::Point location(5, 10);
-
- InitButtonEvent(&event, true, location, 4, 0);
- EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromXEvent(event));
-
- // We should return ET_UNKNOWN for the release event instead of returning
- // ET_MOUSEWHEEL; otherwise we'll scroll twice for each scrollwheel step.
- InitButtonEvent(&event, false, location, 4, 0);
- EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromXEvent(event));
-
- // TODO(derat): Test XInput code.
-}
-
-TEST_F(EventsXTest, EnterLeaveEvent) {
- auto* connection = x11::Connection::Get();
- xcb_generic_event_t ge;
- memset(&ge, 0, sizeof(ge));
- auto* enter = reinterpret_cast<xcb_enter_notify_event_t*>(&ge);
- enter->response_type = x11::CrossingEvent::EnterNotify;
- enter->event_x = 10;
- enter->event_y = 20;
- enter->root_x = 110;
- enter->root_y = 120;
- x11::Event event(&ge, connection);
-
- // Mouse enter events are converted to mouse move events to be consistent with
- // the way views handle mouse enter. See comments for EnterNotify case in
- // ui::EventTypeFromXEvent for more details.
- EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromXEvent(event));
- EXPECT_TRUE(ui::EventFlagsFromXEvent(event) & ui::EF_IS_SYNTHESIZED);
- EXPECT_EQ("10,20", ui::EventLocationFromXEvent(event).ToString());
- EXPECT_EQ("110,120", ui::EventSystemLocationFromXEvent(event).ToString());
-
- enter->response_type = x11::CrossingEvent::LeaveNotify;
- enter->event_x = 30;
- enter->event_y = 40;
- enter->root_x = 230;
- enter->root_y = 240;
- event = x11::Event(&ge, connection);
- EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromXEvent(event));
- EXPECT_EQ("30,40", ui::EventLocationFromXEvent(event).ToString());
- EXPECT_EQ("230,240", ui::EventSystemLocationFromXEvent(event).ToString());
-}
-
-TEST_F(EventsXTest, ClickCount) {
- x11::Event event;
- gfx::Point location(5, 10);
-
- base::TimeDelta time_stamp = base::TimeTicks::Now().since_origin() -
- base::TimeDelta::FromMilliseconds(10);
- for (int i = 1; i <= 3; ++i) {
- InitButtonEvent(&event, true, location, 1, 0);
- {
- uint32_t time = time_stamp.InMilliseconds() & UINT32_MAX;
- event.xlib_event().xbutton.time = time;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(time);
- auto mouseev = ui::BuildMouseEventFromXEvent(event);
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, mouseev->type());
- EXPECT_EQ(i, mouseev->GetClickCount());
- }
-
- InitButtonEvent(&event, false, location, 1, 0);
- {
- uint32_t time = time_stamp.InMilliseconds() & UINT32_MAX;
- event.xlib_event().xbutton.time = time;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(time);
- auto mouseev = ui::BuildMouseEventFromXEvent(event);
- EXPECT_EQ(ui::ET_MOUSE_RELEASED, mouseev->type());
- EXPECT_EQ(i, mouseev->GetClickCount());
- }
- time_stamp += base::TimeDelta::FromMilliseconds(1);
- }
-}
-
-TEST_F(EventsXTest, TouchEventBasic) {
- std::vector<int> devices;
- devices.push_back(0);
- ui::SetUpTouchDevicesForTest(devices);
- std::vector<Valuator> valuators;
-
- // Init touch begin with tracking id 5, touch id 0.
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_MAJOR, 20);
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_ORIENTATION, 0.3f);
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_PRESSURE, 100);
- ui::ScopedXI2Event scoped_xevent;
- scoped_xevent.InitTouchEvent(0, XI_TouchBegin, 5, gfx::Point(10, 10),
- valuators);
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, ui::EventTypeFromXEvent(*scoped_xevent));
- EXPECT_EQ("10,10", FlooredEventLocationString(*scoped_xevent));
- EXPECT_EQ(GetTouchIdFromXEvent(*scoped_xevent), 0);
- PointerDetails pointer_details =
- GetTouchPointerDetailsFromXEvent(*scoped_xevent);
- EXPECT_FLOAT_EQ(ComputeRotationAngle(pointer_details.twist), 0.15f);
- EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f);
- EXPECT_FLOAT_EQ(pointer_details.force, 0.1f);
-
- // Touch update, with new orientation info.
- valuators.clear();
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_ORIENTATION, 0.5f);
- scoped_xevent.InitTouchEvent(0, XI_TouchUpdate, 5, gfx::Point(20, 20),
- valuators);
- EXPECT_EQ(ui::ET_TOUCH_MOVED, ui::EventTypeFromXEvent(*scoped_xevent));
- EXPECT_EQ("20,20", FlooredEventLocationString(*scoped_xevent));
- EXPECT_EQ(GetTouchIdFromXEvent(*scoped_xevent), 0);
- pointer_details = GetTouchPointerDetailsFromXEvent(*scoped_xevent);
- EXPECT_FLOAT_EQ(ComputeRotationAngle(pointer_details.twist), 0.25f);
- EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f);
- EXPECT_FLOAT_EQ(pointer_details.force, 0.1f);
-
- // Another touch with tracking id 6, touch id 1.
- valuators.clear();
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_MAJOR, 100);
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_ORIENTATION, 0.9f);
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_PRESSURE, 500);
- scoped_xevent.InitTouchEvent(0, XI_TouchBegin, 6, gfx::Point(200, 200),
- valuators);
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, ui::EventTypeFromXEvent(*scoped_xevent));
- EXPECT_EQ("200,200", FlooredEventLocationString(*scoped_xevent));
- EXPECT_EQ(GetTouchIdFromXEvent(*scoped_xevent), 1);
- pointer_details = GetTouchPointerDetailsFromXEvent(*scoped_xevent);
- EXPECT_FLOAT_EQ(ComputeRotationAngle(pointer_details.twist), 0.45f);
- EXPECT_FLOAT_EQ(pointer_details.radius_x, 50.0f);
- EXPECT_FLOAT_EQ(pointer_details.force, 0.5f);
-
- // Touch with tracking id 5 should have old radius/angle value and new pressue
- // value.
- valuators.clear();
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_PRESSURE, 50);
- scoped_xevent.InitTouchEvent(0, XI_TouchEnd, 5, gfx::Point(30, 30),
- valuators);
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, ui::EventTypeFromXEvent(*scoped_xevent));
- EXPECT_EQ("30,30", FlooredEventLocationString(*scoped_xevent));
- EXPECT_EQ(GetTouchIdFromXEvent(*scoped_xevent), 0);
- pointer_details = GetTouchPointerDetailsFromXEvent(*scoped_xevent);
- EXPECT_FLOAT_EQ(ComputeRotationAngle(pointer_details.twist), 0.25f);
- EXPECT_FLOAT_EQ(pointer_details.radius_x, 10.0f);
- EXPECT_FLOAT_EQ(pointer_details.force, 0.f);
-
- // Touch with tracking id 6 should have old angle/pressure value and new
- // radius value.
- valuators.clear();
- valuators.emplace_back(DeviceDataManagerX11::DT_TOUCH_MAJOR, 50);
- scoped_xevent.InitTouchEvent(0, XI_TouchEnd, 6, gfx::Point(200, 200),
- valuators);
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, ui::EventTypeFromXEvent(*scoped_xevent));
- EXPECT_EQ("200,200", FlooredEventLocationString(*scoped_xevent));
- EXPECT_EQ(GetTouchIdFromXEvent(*scoped_xevent), 1);
- pointer_details = GetTouchPointerDetailsFromXEvent(*scoped_xevent);
- EXPECT_FLOAT_EQ(ComputeRotationAngle(pointer_details.twist), 0.45f);
- EXPECT_FLOAT_EQ(pointer_details.radius_x, 25.0f);
- EXPECT_FLOAT_EQ(pointer_details.force, 0.f);
-}
-
-int GetTouchIdForTrackingId(uint32_t tracking_id) {
- int slot = 0;
- bool success =
- TouchFactory::GetInstance()->QuerySlotForTrackingID(tracking_id, &slot);
- if (success)
- return slot;
- return -1;
-}
-
-TEST_F(EventsXTest, TouchEventNotRemovingFromNativeMapping) {
- const int kTrackingId = 5;
- const int kDeviceId = 0;
-
- std::vector<int> devices{kDeviceId};
- ui::SetUpTouchDevicesForTest(devices);
- std::vector<Valuator> valuators;
-
- // Two touch presses with the same tracking id.
- ui::ScopedXI2Event xpress0;
- xpress0.InitTouchEvent(kDeviceId, XI_TouchBegin, kTrackingId,
- gfx::Point(10, 10), valuators);
- auto upress0 = ui::BuildTouchEventFromXEvent(*xpress0);
- EXPECT_EQ(kDeviceId, GetTouchIdForTrackingId(kTrackingId));
-
- ui::ScopedXI2Event xpress1;
- xpress1.InitTouchEvent(kDeviceId, XI_TouchBegin, kTrackingId,
- gfx::Point(20, 20), valuators);
- auto upress1 = ui::BuildTouchEventFromXEvent(*xpress1);
- EXPECT_EQ(kDeviceId, GetTouchIdForTrackingId(kTrackingId));
-
- // The second touch release should clear the mapping from the
- // tracking id.
- ui::ScopedXI2Event xrelease1;
- xrelease1.InitTouchEvent(kDeviceId, XI_TouchEnd, kTrackingId,
- gfx::Point(10, 10), valuators);
- { auto urelease1 = ui::BuildTouchEventFromXEvent(*xrelease1); }
- EXPECT_EQ(-1, GetTouchIdForTrackingId(kTrackingId));
-}
-
-// Copied events should not remove native touch id mappings, as this causes a
-// crash (crbug.com/467102). Copied events do not contain a proper
-// PlatformEvent and should not attempt to access it.
-TEST_F(EventsXTest, CopiedTouchEventNotRemovingFromXEventMapping) {
- std::vector<int> devices;
- devices.push_back(0);
- ui::SetUpTouchDevicesForTest(devices);
- std::vector<Valuator> valuators;
-
- // Create a release event which has a native touch id mapping.
- ui::ScopedXI2Event xrelease0;
- xrelease0.InitTouchEvent(0, XI_TouchEnd, 0, gfx::Point(10, 10), valuators);
- auto urelease0 = ui::BuildTouchEventFromXEvent(*xrelease0);
- {
- // When the copy is destructed it should not attempt to remove the mapping.
- // Exiting this scope should not cause a crash.
- TouchEvent copy = *urelease0;
- }
-}
-
-// Verifies that the type of events from a disabled keyboard is ET_UNKNOWN, but
-// that an exception list of keys can still be processed.
-TEST_F(EventsXTest, DisableKeyboard) {
- DeviceDataManagerX11* device_data_manager =
- static_cast<DeviceDataManagerX11*>(DeviceDataManager::GetInstance());
- int blocked_device_id = 1;
- int other_device_id = 2;
- int master_device_id = 3;
- device_data_manager->DisableDevice(blocked_device_id);
-
- std::unique_ptr<std::set<KeyboardCode>> excepted_keys(
- new std::set<KeyboardCode>);
- excepted_keys->insert(VKEY_B);
- device_data_manager->SetDisabledKeyboardAllowedKeys(std::move(excepted_keys));
-
- ScopedXI2Event xev;
- // A is not allowed on the blocked keyboard, and should return ET_UNKNOWN.
- xev.InitGenericKeyEvent(master_device_id, blocked_device_id,
- ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
- EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromXEvent(*xev));
-
- // The B key is allowed as an exception, and should return KEY_PRESSED.
- xev.InitGenericKeyEvent(master_device_id, blocked_device_id,
- ui::ET_KEY_PRESSED, ui::VKEY_B, 0);
- EXPECT_EQ(ui::ET_KEY_PRESSED, ui::EventTypeFromXEvent(*xev));
-
- // Both A and B are allowed on an unblocked keyboard device.
- xev.InitGenericKeyEvent(master_device_id, other_device_id, ui::ET_KEY_PRESSED,
- ui::VKEY_A, 0);
- EXPECT_EQ(ui::ET_KEY_PRESSED, ui::EventTypeFromXEvent(*xev));
- xev.InitGenericKeyEvent(master_device_id, other_device_id, ui::ET_KEY_PRESSED,
- ui::VKEY_B, 0);
- EXPECT_EQ(ui::ET_KEY_PRESSED, ui::EventTypeFromXEvent(*xev));
-
- device_data_manager->EnableDevice(blocked_device_id);
- device_data_manager->SetDisabledKeyboardAllowedKeys(nullptr);
-
- // A key returns KEY_PRESSED as per usual now that keyboard was re-enabled.
- xev.InitGenericKeyEvent(master_device_id, blocked_device_id,
- ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
- EXPECT_EQ(ui::ET_KEY_PRESSED, ui::EventTypeFromXEvent(*xev));
-}
-
-// Verifies that the type of events from a disabled mouse is ET_UNKNOWN.
-TEST_F(EventsXTest, DisableMouse) {
- DeviceDataManagerX11* device_data_manager =
- static_cast<DeviceDataManagerX11*>(DeviceDataManager::GetInstance());
- int blocked_device_id = 1;
- int other_device_id = 2;
- std::vector<int> device_list;
- device_list.push_back(blocked_device_id);
- device_list.push_back(other_device_id);
- TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list);
-
- device_data_manager->DisableDevice(blocked_device_id);
-
- ScopedXI2Event xev;
- xev.InitGenericButtonEvent(blocked_device_id, ET_MOUSE_PRESSED, gfx::Point(),
- EF_LEFT_MOUSE_BUTTON);
- EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromXEvent(*xev));
-
- xev.InitGenericButtonEvent(other_device_id, ET_MOUSE_PRESSED, gfx::Point(),
- EF_LEFT_MOUSE_BUTTON);
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromXEvent(*xev));
-
- device_data_manager->EnableDevice(blocked_device_id);
-
- xev.InitGenericButtonEvent(blocked_device_id, ET_MOUSE_PRESSED, gfx::Point(),
- EF_LEFT_MOUSE_BUTTON);
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromXEvent(*xev));
-}
-
-#if !defined(OS_CHROMEOS)
-TEST_F(EventsXTest, ImeFabricatedKeyEvents) {
- Display* display = gfx::GetXDisplay();
-
- unsigned int state_to_be_fabricated[] = {
- 0,
- ShiftMask,
- LockMask,
- ShiftMask | LockMask,
- };
- for (unsigned int state : state_to_be_fabricated) {
- for (int is_char = 0; is_char < 2; ++is_char) {
- x11::Event x_event;
- InitKeyEvent(display, &x_event, true, 0, state);
- auto key_event = ui::BuildKeyEventFromXEvent(x_event);
- if (is_char) {
- KeyEventTestApi test_event(key_event.get());
- test_event.set_is_char(true);
- }
- EXPECT_TRUE(key_event->flags() & ui::EF_IME_FABRICATED_KEY);
- }
- }
-
- unsigned int state_to_be_not_fabricated[] = {
- ControlMask,
- Mod1Mask,
- Mod2Mask,
- ShiftMask | ControlMask,
- };
- for (unsigned int state : state_to_be_not_fabricated) {
- for (int is_char = 0; is_char < 2; ++is_char) {
- x11::Event x_event;
- InitKeyEvent(display, &x_event, true, 0, state);
- auto key_event = ui::BuildKeyEventFromXEvent(x_event);
- if (is_char) {
- KeyEventTestApi test_event(key_event.get());
- test_event.set_is_char(true);
- }
- EXPECT_FALSE(key_event->flags() & ui::EF_IME_FABRICATED_KEY);
- }
- }
-}
-#endif
-
-TEST_F(EventsXTest, IgnoresMotionEventForMouseWheelScroll) {
- int device_id = 1;
- std::vector<int> devices;
- devices.push_back(device_id);
- ui::SetUpPointerDevicesForTest(devices);
-
- ScopedXI2Event xev;
- xev.InitScrollEvent(device_id, 1, 2, 3, 4, 1);
- // We shouldn't produce a mouse move event on a mouse wheel
- // scroll. These events are only produced for some mice.
- EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromXEvent(*xev));
-}
-
-namespace {
-
-// Returns a fake TimeTicks based on the given millisecond offset.
-base::TimeTicks TimeTicksFromMillis(int64_t millis) {
- return base::TimeTicks() + base::TimeDelta::FromMilliseconds(millis);
-}
-
-} // namespace
-
-TEST_F(EventsXTest, TimestampRolloverAndAdjustWhenDecreasing) {
- x11::Event event;
- InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0);
-
- test::ScopedEventTestTickClock clock;
- clock.SetNowTicks(TimeTicksFromMillis(0x100000001));
- ResetTimestampRolloverCountersForTesting();
-
- event.xlib_event().xbutton.time = 0xFFFFFFFF;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(0xFFFFFFFF);
- EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromXEvent(event));
-
- clock.SetNowTicks(TimeTicksFromMillis(0x100000007));
- ResetTimestampRolloverCountersForTesting();
-
- event.xlib_event().xbutton.time = 3;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(3);
- EXPECT_EQ(TimeTicksFromMillis(0x100000000 + 3),
- ui::EventTimeFromXEvent(event));
-}
-
-TEST_F(EventsXTest, NoTimestampRolloverWhenMonotonicIncreasing) {
- x11::Event event;
- InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0);
-
- test::ScopedEventTestTickClock clock;
- clock.SetNowTicks(TimeTicksFromMillis(10));
- ResetTimestampRolloverCountersForTesting();
-
- event.xlib_event().xbutton.time = 6;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(6);
- EXPECT_EQ(TimeTicksFromMillis(6), ui::EventTimeFromXEvent(event));
- event.xlib_event().xbutton.time = 7;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(7);
- EXPECT_EQ(TimeTicksFromMillis(7), ui::EventTimeFromXEvent(event));
-
- clock.SetNowTicks(TimeTicksFromMillis(0x100000005));
- ResetTimestampRolloverCountersForTesting();
-
- event.xlib_event().xbutton.time = 0xFFFFFFFF;
- event.As<x11::ButtonEvent>()->time = static_cast<x11::Time>(0xFFFFFFFF);
- EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromXEvent(event));
-}
-
-} // namespace ui
diff --git a/chromium/ui/events/x/events_x_utils.cc b/chromium/ui/events/x/events_x_utils.cc
index 4229fe4adae..8ab4ce14041 100644
--- a/chromium/ui/events/x/events_x_utils.cc
+++ b/chromium/ui/events/x/events_x_utils.cc
@@ -20,11 +20,14 @@
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/devices/x11/device_list_cache_x11.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
+#include "ui/events/devices/x11/xinput_util.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/gfx/x/xproto.h"
namespace {
@@ -58,39 +61,23 @@ class XModifierStateWatcher {
}
}
- void UpdateStateFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromXKeyEvent(&xev);
+ void UpdateStateFromXEvent(const x11::Event& xev) {
+ ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromXKeyEvent(xev);
unsigned int mask = StateFromKeyboardCode(keyboard_code);
// Floating device can't access the modifer state from master device.
// We need to track the states of modifier keys in a singleton for
// floating devices such as touch screen. Issue 106426 is one example
// of why we need the modifier states for floating device.
- switch (xev.type) {
- case x11::KeyEvent::Press:
- state_ = xev.xkey.state | mask;
- break;
- case x11::KeyEvent::Release:
- state_ = xev.xkey.state & ~mask;
- break;
- case x11::GeGenericEvent::opcode: {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- switch (xievent->evtype) {
- case XI_KeyPress:
- state_ = xievent->mods.effective |= mask;
- break;
- case XI_KeyRelease:
- state_ = xievent->mods.effective &= ~mask;
- break;
- default:
- NOTREACHED();
- break;
- }
- break;
- }
- default:
- NOTREACHED();
- break;
+ if (auto* key = xev.As<x11::KeyEvent>()) {
+ if (key->opcode == x11::KeyEvent::Press)
+ state_ = static_cast<int>(key->state) | mask;
+ else
+ state_ = static_cast<int>(key->state) & ~mask;
+ } else if (auto* device = xev.As<x11::Input::DeviceEvent>()) {
+ if (device->opcode == x11::Input::DeviceEvent::KeyPress)
+ state_ = device->mods.effective | mask;
+ else if (device->opcode == x11::Input::DeviceEvent::KeyPress)
+ state_ = device->mods.effective & ~mask;
}
}
@@ -113,10 +100,11 @@ class XModifierStateWatcher {
// location (0, 0).
// This needs to be done in a cleaner way: http://crbug.com/169256
bool TouchEventIsGeneratedHack(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- XIDeviceEvent* event = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- CHECK(event->evtype == XI_TouchBegin || event->evtype == XI_TouchUpdate ||
- event->evtype == XI_TouchEnd);
+ auto* event = x11_event.As<x11::Input::DeviceEvent>();
+ CHECK(event);
+ CHECK(event->opcode == x11::Input::DeviceEvent::TouchBegin ||
+ event->opcode == x11::Input::DeviceEvent::TouchUpdate ||
+ event->opcode == x11::Input::DeviceEvent::TouchEnd);
// Force is normalized to [0, 1].
if (ui::GetTouchForceFromXEvent(x11_event) < 1.0f)
@@ -127,8 +115,7 @@ bool TouchEventIsGeneratedHack(const x11::Event& x11_event) {
// Radius is in pixels, and the valuator is the diameter in pixels.
double radius = ui::GetTouchRadiusXFromXEvent(x11_event), min, max;
- unsigned int deviceid =
- static_cast<XIDeviceEvent*>(xev.xcookie.data)->sourceid;
+ auto deviceid = event->sourceid;
if (!ui::DeviceDataManagerX11::GetInstance()->GetDataRange(
deviceid, ui::DeviceDataManagerX11::DT_TOUCH_MAJOR, &min, &max)) {
return false;
@@ -137,39 +124,43 @@ bool TouchEventIsGeneratedHack(const x11::Event& x11_event) {
return radius * 2 == max;
}
-int GetEventFlagsFromXState(unsigned int state) {
+int GetEventFlagsFromXState(x11::KeyButMask state) {
int flags = 0;
- if (state & ShiftMask)
+ if (static_cast<bool>(state & x11::KeyButMask::Shift))
flags |= ui::EF_SHIFT_DOWN;
- if (state & LockMask)
+ if (static_cast<bool>(state & x11::KeyButMask::Lock))
flags |= ui::EF_CAPS_LOCK_ON;
- if (state & ControlMask)
+ if (static_cast<bool>(state & x11::KeyButMask::Control))
flags |= ui::EF_CONTROL_DOWN;
- if (state & Mod1Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Mod1))
flags |= ui::EF_ALT_DOWN;
- if (state & Mod2Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Mod2))
flags |= ui::EF_NUM_LOCK_ON;
- if (state & Mod3Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Mod3))
flags |= ui::EF_MOD3_DOWN;
- if (state & Mod4Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Mod4))
flags |= ui::EF_COMMAND_DOWN;
- if (state & Mod5Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Mod5))
flags |= ui::EF_ALTGR_DOWN;
- if (state & Button1Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Button1))
flags |= ui::EF_LEFT_MOUSE_BUTTON;
- if (state & Button2Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Button2))
flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
- if (state & Button3Mask)
+ if (static_cast<bool>(state & x11::KeyButMask::Button3))
flags |= ui::EF_RIGHT_MOUSE_BUTTON;
// There are no masks for EF_BACK_MOUSE_BUTTON and
// EF_FORWARD_MOUSE_BUTTON.
return flags;
}
-int GetEventFlagsFromXKeyEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- DCHECK(xev.type == x11::KeyEvent::Press ||
- xev.type == x11::KeyEvent::Release);
+int GetEventFlagsFromXState(uint32_t state) {
+ return GetEventFlagsFromXState(static_cast<x11::KeyButMask>(state));
+}
+
+int GetEventFlagsFromXKeyEvent(const x11::Event& xev) {
+ auto* key = xev.As<x11::KeyEvent>();
+ DCHECK(key);
+ const auto state = static_cast<int>(key->state);
#if defined(OS_CHROMEOS)
const int ime_fabricated_flag = 0;
@@ -183,25 +174,25 @@ int GetEventFlagsFromXKeyEvent(const x11::Event& x11_event) {
//
// We have to send these fabricated key events to XIM so it can correctly
// handle the character compositions.
- const unsigned int shift_lock_mask = ShiftMask | LockMask;
- const bool fabricated_by_xim =
- xev.xkey.keycode == 0 && (xev.xkey.state & ~shift_lock_mask) == 0;
+ const auto detail = static_cast<uint8_t>(key->detail);
+ const auto shift_lock_mask =
+ static_cast<int>(x11::KeyButMask::Shift | x11::KeyButMask::Lock);
+ const bool fabricated_by_xim = detail == 0 && (state & ~shift_lock_mask) == 0;
const int ime_fabricated_flag =
fabricated_by_xim ? ui::EF_IME_FABRICATED_KEY : 0;
#endif
- return GetEventFlagsFromXState(xev.xkey.state) |
- (xev.xkey.send_event ? ui::EF_FINAL : 0) | ime_fabricated_flag;
+ return GetEventFlagsFromXState(state) | (key->send_event ? ui::EF_FINAL : 0) |
+ ime_fabricated_flag;
}
int GetEventFlagsFromXGenericEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- DCHECK(xev.type == x11::GeGenericEvent::opcode);
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- DCHECK((xievent->evtype == XI_KeyPress) ||
- (xievent->evtype == XI_KeyRelease));
+ auto* xievent = x11_event.As<x11::Input::DeviceEvent>();
+ DCHECK(xievent);
+ DCHECK(xievent->opcode == x11::Input::DeviceEvent::KeyPress ||
+ xievent->opcode == x11::Input::DeviceEvent::KeyRelease);
return GetEventFlagsFromXState(xievent->mods.effective) |
- (xev.xkey.send_event ? ui::EF_FINAL : 0);
+ (xievent->send_event ? ui::EF_FINAL : 0);
}
// Get the event flag for the button in XButtonEvent. During a ButtonPress
@@ -229,12 +220,16 @@ int GetEventFlagsForButton(int button) {
}
}
-int GetButtonMaskForX2Event(XIDeviceEvent* xievent) {
+int GetEventFlagsForButton(x11::Button button) {
+ return GetEventFlagsForButton(static_cast<int>(button));
+}
+
+int GetButtonMaskForX2Event(const x11::Input::DeviceEvent& xievent) {
int buttonflags = 0;
- for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) {
- if (XIMaskIsSet(xievent->buttons.mask, i)) {
+ for (size_t i = 0; i < 32 * xievent.button_mask.size(); i++) {
+ if (ui::IsXinputMaskSet(xievent.button_mask.data(), i)) {
int button =
- (xievent->sourceid == xievent->deviceid)
+ (xievent.sourceid == xievent.deviceid)
? ui::DeviceDataManagerX11::GetInstance()->GetMappedButton(i)
: i;
buttonflags |= GetEventFlagsForButton(button);
@@ -244,41 +239,40 @@ int GetButtonMaskForX2Event(XIDeviceEvent* xievent) {
}
ui::EventType GetTouchEventType(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- XIDeviceEvent* event = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- switch (event->evtype) {
- case XI_TouchBegin:
+ auto* event = x11_event.As<x11::Input::DeviceEvent>();
+ if (!event) {
+ // This is either a crossing event (which are handled by
+ // PlatformEventDispatcher directly) or a device changed event (which can
+ // happen when --touch-devices flag is used).
+ return ui::ET_UNKNOWN;
+ }
+ switch (event->opcode) {
+ case x11::Input::DeviceEvent::TouchBegin:
return TouchEventIsGeneratedHack(x11_event) ? ui::ET_UNKNOWN
: ui::ET_TOUCH_PRESSED;
- case XI_TouchUpdate:
+ case x11::Input::DeviceEvent::TouchUpdate:
return TouchEventIsGeneratedHack(x11_event) ? ui::ET_UNKNOWN
: ui::ET_TOUCH_MOVED;
- case XI_TouchEnd:
+ case x11::Input::DeviceEvent::TouchEnd:
return TouchEventIsGeneratedHack(x11_event) ? ui::ET_TOUCH_CANCELLED
: ui::ET_TOUCH_RELEASED;
+ default:;
}
DCHECK(ui::TouchFactory::GetInstance()->IsTouchDevice(event->sourceid));
- switch (event->evtype) {
- case XI_ButtonPress:
+ switch (event->opcode) {
+ case x11::Input::DeviceEvent::ButtonPress:
return ui::ET_TOUCH_PRESSED;
- case XI_ButtonRelease:
+ case x11::Input::DeviceEvent::ButtonRelease:
return ui::ET_TOUCH_RELEASED;
- case XI_Motion:
+ case x11::Input::DeviceEvent::Motion:
// Should not convert any emulated Motion event from touch device to
// touch event.
- if (!(event->flags & XIPointerEmulated) && GetButtonMaskForX2Event(event))
+ if (!static_cast<bool>(event->flags &
+ x11::Input::KeyEventFlags::KeyRepeat) &&
+ GetButtonMaskForX2Event(*event))
return ui::ET_TOUCH_MOVED;
return ui::ET_UNKNOWN;
- case XI_DeviceChanged:
- // This can happen when --touch-devices flag is used.
- return ui::ET_UNKNOWN;
- case XI_Leave:
- case XI_Enter:
- case XI_FocusIn:
- case XI_FocusOut:
- // These may be handled by the PlatformEventDispatcher directly.
- return ui::ET_UNKNOWN;
default:
NOTREACHED();
}
@@ -294,11 +288,10 @@ double GetTouchParamFromXEvent(const x11::Event& xev,
}
void ScaleTouchRadius(const x11::Event& x11_event, double* radius) {
- const XEvent& xev = x11_event.xlib_event();
- DCHECK_EQ(x11::GeGenericEvent::opcode, xev.type);
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- ui::DeviceDataManagerX11::GetInstance()->ApplyTouchRadiusScale(xiev->sourceid,
- radius);
+ auto* xiev = x11_event.As<x11::Input::DeviceEvent>();
+ DCHECK(xiev);
+ ui::DeviceDataManagerX11::GetInstance()->ApplyTouchRadiusScale(
+ static_cast<uint16_t>(xiev->sourceid), radius);
}
bool GetGestureTimes(const x11::Event& xev,
@@ -323,10 +316,11 @@ int64_t g_rollover_ms = 0;
// Takes Xlib Time and returns a time delta that is immune to timer rollover.
// This function is not thread safe as we do not use a lock.
-base::TimeTicks TimeTicksFromXEventTime(Time timestamp) {
- int64_t timestamp64 = timestamp;
+base::TimeTicks TimeTicksFromXEventTime(x11::Time timestamp) {
+ uint32_t timestamp32 = static_cast<uint32_t>(timestamp);
+ int64_t timestamp64 = static_cast<int64_t>(timestamp);
- if (!timestamp)
+ if (!timestamp64)
return ui::EventTimeForNow();
// If this is the first event that we get, assume the time stamp roll-over
@@ -341,7 +335,7 @@ base::TimeTicks TimeTicksFromXEventTime(Time timestamp) {
g_last_seen_timestamp_ms = timestamp64;
if (!had_recent_rollover)
return base::TimeTicks() +
- base::TimeDelta::FromMilliseconds(g_rollover_ms + timestamp);
+ base::TimeDelta::FromMilliseconds(g_rollover_ms + timestamp32);
DCHECK(timestamp64 <= UINT32_MAX)
<< "X11 Time does not roll over 32 bit, the below logic is likely wrong";
@@ -350,220 +344,204 @@ base::TimeTicks TimeTicksFromXEventTime(Time timestamp) {
int64_t now_ms = (now_ticks - base::TimeTicks()).InMilliseconds();
g_rollover_ms = now_ms & ~static_cast<int64_t>(UINT32_MAX);
- uint32_t delta = static_cast<uint32_t>(now_ms - timestamp);
+ uint32_t delta = static_cast<uint32_t>(now_ms - timestamp32);
return base::TimeTicks() + base::TimeDelta::FromMilliseconds(now_ms - delta);
}
-base::TimeTicks TimeTicksFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- switch (xev.type) {
- case x11::KeyEvent::Press:
- case x11::KeyEvent::Release:
- return TimeTicksFromXEventTime(xev.xkey.time);
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release:
- return TimeTicksFromXEventTime(xev.xbutton.time);
- case x11::MotionNotifyEvent::opcode:
- return TimeTicksFromXEventTime(xev.xmotion.time);
- case x11::CrossingEvent::EnterNotify:
- case x11::CrossingEvent::LeaveNotify:
- return TimeTicksFromXEventTime(xev.xcrossing.time);
- case x11::GeGenericEvent::opcode: {
- double start, end;
- double touch_timestamp;
- if (GetGestureTimes(x11_event, &start, &end)) {
- // If the driver supports gesture times, use them.
- return ui::EventTimeStampFromSeconds(end);
- } else if (ui::DeviceDataManagerX11::GetInstance()->GetEventData(
- x11_event,
- ui::DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP,
- &touch_timestamp)) {
- return ui::EventTimeStampFromSeconds(touch_timestamp);
- }
- XIDeviceEvent* xide = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return TimeTicksFromXEventTime(xide->time);
+base::TimeTicks TimeTicksFromXEvent(const x11::Event& xev) {
+ if (auto* key = xev.As<x11::KeyEvent>())
+ return TimeTicksFromXEventTime(key->time);
+ if (auto* button = xev.As<x11::ButtonEvent>())
+ return TimeTicksFromXEventTime(button->time);
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>())
+ return TimeTicksFromXEventTime(motion->time);
+ if (auto* crossing = xev.As<x11::CrossingEvent>())
+ return TimeTicksFromXEventTime(crossing->time);
+ if (auto* device = xev.As<x11::Input::DeviceEvent>()) {
+ double start, end;
+ double touch_timestamp;
+ if (GetGestureTimes(xev, &start, &end)) {
+ // If the driver supports gesture times, use them.
+ return ui::EventTimeStampFromSeconds(end);
+ } else if (ui::DeviceDataManagerX11::GetInstance()->GetEventData(
+ xev, ui::DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP,
+ &touch_timestamp)) {
+ return ui::EventTimeStampFromSeconds(touch_timestamp);
}
+ return TimeTicksFromXEventTime(device->time);
}
NOTREACHED();
return base::TimeTicks();
}
+// This is ported from libxi's FP1616toDBL in XExtInt.c
+double Fp1616ToDouble(x11::Input::Fp1616 x) {
+ auto x32 = static_cast<uint32_t>(x);
+ return x32 * 1.0 / (1 << 16);
+}
+
} // namespace
namespace ui {
-EventType EventTypeFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
+EventType EventTypeFromXEvent(const x11::Event& xev) {
// Allow the DeviceDataManager to block the event. If blocked return
// ET_UNKNOWN as the type so this event will not be further processed.
// NOTE: During some events unittests there is no device data manager.
if (DeviceDataManager::HasInstance() &&
- DeviceDataManagerX11::GetInstance()->IsEventBlocked(x11_event)) {
+ DeviceDataManagerX11::GetInstance()->IsEventBlocked(xev)) {
return ET_UNKNOWN;
}
- switch (xev.type) {
- case x11::KeyEvent::Press:
- return ET_KEY_PRESSED;
- case x11::KeyEvent::Release:
- return ET_KEY_RELEASED;
- case x11::ButtonEvent::Press:
- if (static_cast<int>(xev.xbutton.button) >= kMinWheelButton &&
- static_cast<int>(xev.xbutton.button) <= kMaxWheelButton)
- return ET_MOUSEWHEEL;
- return ET_MOUSE_PRESSED;
- case x11::ButtonEvent::Release:
- // Drop wheel events; we should've already scrolled on the press.
- if (static_cast<int>(xev.xbutton.button) >= kMinWheelButton &&
- static_cast<int>(xev.xbutton.button) <= kMaxWheelButton)
- return ET_UNKNOWN;
- return ET_MOUSE_RELEASED;
- case x11::MotionNotifyEvent::opcode:
- if (xev.xmotion.state & (Button1Mask | Button2Mask | Button3Mask))
- return ET_MOUSE_DRAGGED;
- return ET_MOUSE_MOVED;
- case x11::CrossingEvent::EnterNotify:
- // The standard on Windows is to send a MouseMove event when the mouse
- // first enters a window instead of sending a special mouse enter event.
- // To be consistent we follow the same style.
- return ET_MOUSE_MOVED;
- case x11::CrossingEvent::LeaveNotify:
- return ET_MOUSE_EXITED;
- case x11::GeGenericEvent::opcode: {
- TouchFactory* factory = TouchFactory::GetInstance();
- if (!factory->ShouldProcessXI2Event(const_cast<XEvent*>(&xev)))
- return ET_UNKNOWN;
-
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
-
- // This check works only for master and floating slave devices. That is
- // why it is necessary to check for the XI_Touch* events in the following
- // switch statement to account for attached-slave touchscreens.
- if (factory->IsTouchDevice(xievent->sourceid))
- return GetTouchEventType(x11_event);
-
- switch (xievent->evtype) {
- case XI_TouchBegin:
- return ui::ET_TOUCH_PRESSED;
- case XI_TouchUpdate:
- return ui::ET_TOUCH_MOVED;
- case XI_TouchEnd:
- return ui::ET_TOUCH_RELEASED;
- case XI_ButtonPress: {
- int button = EventButtonFromXEvent(x11_event);
- if (button >= kMinWheelButton && button <= kMaxWheelButton)
- return ET_MOUSEWHEEL;
- return ET_MOUSE_PRESSED;
+ if (auto* key = xev.As<x11::KeyEvent>()) {
+ return key->opcode == x11::KeyEvent::Press ? ET_KEY_PRESSED
+ : ET_KEY_RELEASED;
+ }
+ if (auto* xbutton = xev.As<x11::ButtonEvent>()) {
+ int button = static_cast<int>(xbutton->detail);
+ bool wheel = button >= kMinWheelButton && button <= kMaxWheelButton;
+ if (xbutton->opcode == x11::ButtonEvent::Press) {
+ return wheel ? ET_MOUSEWHEEL : ET_MOUSE_PRESSED;
+ }
+ // Drop wheel events; we should've already scrolled on the press.
+ return wheel ? ET_UNKNOWN : ET_MOUSE_RELEASED;
+ }
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>()) {
+ bool primary_button = static_cast<bool>(
+ motion->state & (x11::KeyButMask::Button1 | x11::KeyButMask::Button2 |
+ x11::KeyButMask::Button3));
+ return primary_button ? ET_MOUSE_DRAGGED : ET_MOUSE_MOVED;
+ }
+ if (auto* crossing = xev.As<x11::CrossingEvent>()) {
+ bool enter = crossing->opcode == x11::CrossingEvent::EnterNotify;
+ // The standard on Windows is to send a MouseMove event when the mouse
+ // first enters a window instead of sending a special mouse enter event.
+ // To be consistent we follow the same style.
+ return enter ? ET_MOUSE_MOVED : ET_MOUSE_EXITED;
+ }
+ if (auto* xievent = xev.As<x11::Input::DeviceEvent>()) {
+ TouchFactory* factory = TouchFactory::GetInstance();
+ if (!factory->ShouldProcessDeviceEvent(*xievent))
+ return ET_UNKNOWN;
+
+ // This check works only for master and floating slave devices. That is
+ // why it is necessary to check for the Touch events in the following
+ // switch statement to account for attached-slave touchscreens.
+ if (factory->IsTouchDevice(xievent->sourceid))
+ return GetTouchEventType(xev);
+
+ switch (xievent->opcode) {
+ case x11::Input::DeviceEvent::TouchBegin:
+ return ui::ET_TOUCH_PRESSED;
+ case x11::Input::DeviceEvent::TouchUpdate:
+ return ui::ET_TOUCH_MOVED;
+ case x11::Input::DeviceEvent::TouchEnd:
+ return ui::ET_TOUCH_RELEASED;
+ case x11::Input::DeviceEvent::ButtonPress: {
+ int button = EventButtonFromXEvent(xev);
+ if (button >= kMinWheelButton && button <= kMaxWheelButton)
+ return ET_MOUSEWHEEL;
+ return ET_MOUSE_PRESSED;
+ }
+ case x11::Input::DeviceEvent::ButtonRelease: {
+ int button = EventButtonFromXEvent(xev);
+ // Drop wheel events; we should've already scrolled on the press.
+ if (button >= kMinWheelButton && button <= kMaxWheelButton)
+ return ET_UNKNOWN;
+ return ET_MOUSE_RELEASED;
+ }
+ case x11::Input::DeviceEvent::Motion: {
+ bool is_cancel;
+ DeviceDataManagerX11* devices = DeviceDataManagerX11::GetInstance();
+ if (GetFlingDataFromXEvent(xev, nullptr, nullptr, nullptr, nullptr,
+ &is_cancel))
+ return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START;
+ if (devices->IsScrollEvent(xev)) {
+ return devices->IsTouchpadXInputEvent(xev) ? ET_SCROLL
+ : ET_MOUSEWHEEL;
}
- case XI_ButtonRelease: {
- int button = EventButtonFromXEvent(x11_event);
- // Drop wheel events; we should've already scrolled on the press.
- if (button >= kMinWheelButton && button <= kMaxWheelButton)
- return ET_UNKNOWN;
- return ET_MOUSE_RELEASED;
+ if (devices->GetScrollClassEventDetail(xev) != SCROLL_TYPE_NO_SCROLL) {
+ return devices->IsTouchpadXInputEvent(xev) ? ET_SCROLL
+ : ET_MOUSEWHEEL;
}
- case XI_Motion: {
- bool is_cancel;
- DeviceDataManagerX11* devices = DeviceDataManagerX11::GetInstance();
- if (GetFlingDataFromXEvent(x11_event, nullptr, nullptr, nullptr,
- nullptr, &is_cancel))
- return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START;
- if (devices->IsScrollEvent(x11_event)) {
- return devices->IsTouchpadXInputEvent(x11_event) ? ET_SCROLL
- : ET_MOUSEWHEEL;
- }
- if (devices->GetScrollClassEventDetail(x11_event) !=
- SCROLL_TYPE_NO_SCROLL) {
- return devices->IsTouchpadXInputEvent(x11_event) ? ET_SCROLL
- : ET_MOUSEWHEEL;
- }
- if (devices->IsCMTMetricsEvent(x11_event))
- return ET_UMA_DATA;
- if (GetButtonMaskForX2Event(xievent))
- return ET_MOUSE_DRAGGED;
- if (DeviceDataManagerX11::GetInstance()->HasEventData(
- xievent, DeviceDataManagerX11::DT_CMT_SCROLL_X) ||
- DeviceDataManagerX11::GetInstance()->HasEventData(
- xievent, DeviceDataManagerX11::DT_CMT_SCROLL_Y)) {
- // Don't produce mouse move events for mousewheel scrolls.
- return ET_UNKNOWN;
- }
-
- return ET_MOUSE_MOVED;
+ if (devices->IsCMTMetricsEvent(xev))
+ return ET_UMA_DATA;
+ if (GetButtonMaskForX2Event(*xievent))
+ return ET_MOUSE_DRAGGED;
+ if (DeviceDataManagerX11::GetInstance()->HasEventData(
+ xev, DeviceDataManagerX11::DT_CMT_SCROLL_X) ||
+ DeviceDataManagerX11::GetInstance()->HasEventData(
+ xev, DeviceDataManagerX11::DT_CMT_SCROLL_Y)) {
+ // Don't produce mouse move events for mousewheel scrolls.
+ return ET_UNKNOWN;
}
- case XI_KeyPress:
- return ET_KEY_PRESSED;
- case XI_KeyRelease:
- return ET_KEY_RELEASED;
+
+ return ET_MOUSE_MOVED;
}
+ case x11::Input::DeviceEvent::KeyPress:
+ return ET_KEY_PRESSED;
+ case x11::Input::DeviceEvent::KeyRelease:
+ return ET_KEY_RELEASED;
}
- default:
- break;
}
return ET_UNKNOWN;
}
-int EventFlagsFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- switch (xev.type) {
- case x11::KeyEvent::Press:
- case x11::KeyEvent::Release: {
- XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent(x11_event);
- return GetEventFlagsFromXKeyEvent(x11_event);
- }
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release: {
- int flags = GetEventFlagsFromXState(xev.xbutton.state);
- const EventType type = EventTypeFromXEvent(x11_event);
- if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED)
- flags |= GetEventFlagsForButton(xev.xbutton.button);
- return flags;
- }
- case x11::CrossingEvent::EnterNotify:
- // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is not
- // a real mouse move event.
- return GetEventFlagsFromXState(xev.xcrossing.state) | EF_IS_SYNTHESIZED;
- case x11::CrossingEvent::LeaveNotify:
- return GetEventFlagsFromXState(xev.xcrossing.state);
- case x11::MotionNotifyEvent::opcode:
- return GetEventFlagsFromXState(xev.xmotion.state);
- case x11::GeGenericEvent::opcode: {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
-
- switch (xievent->evtype) {
- case XI_TouchBegin:
- case XI_TouchUpdate:
- case XI_TouchEnd:
- return GetButtonMaskForX2Event(xievent) |
- GetEventFlagsFromXState(xievent->mods.effective) |
- GetEventFlagsFromXState(
- XModifierStateWatcher::GetInstance()->state());
- case XI_ButtonPress:
- case XI_ButtonRelease: {
- const bool touch =
- TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
- int flags = GetButtonMaskForX2Event(xievent) |
- GetEventFlagsFromXState(xievent->mods.effective);
- if (touch) {
- flags |= GetEventFlagsFromXState(
- XModifierStateWatcher::GetInstance()->state());
- }
-
- const EventType type = EventTypeFromXEvent(x11_event);
- int button = EventButtonFromXEvent(x11_event);
- if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch)
- flags |= GetEventFlagsForButton(button);
- return flags;
- }
- case XI_Motion:
- return GetButtonMaskForX2Event(xievent) |
- GetEventFlagsFromXState(xievent->mods.effective);
- case XI_KeyPress:
- case XI_KeyRelease: {
- XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent(
- x11_event);
- return GetEventFlagsFromXGenericEvent(x11_event);
+int EventFlagsFromXEvent(const x11::Event& xev) {
+ if (xev.As<x11::KeyEvent>()) {
+ XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent(xev);
+ return GetEventFlagsFromXKeyEvent(xev);
+ }
+ if (auto* button = xev.As<x11::ButtonEvent>()) {
+ int flags = GetEventFlagsFromXState(button->state);
+ const EventType type = EventTypeFromXEvent(xev);
+ if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED)
+ flags |= GetEventFlagsForButton(button->detail);
+ return flags;
+ }
+ if (auto* crossing = xev.As<x11::CrossingEvent>()) {
+ int state = GetEventFlagsFromXState(crossing->state);
+ // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is not
+ // a real mouse move event.
+ if (crossing->opcode == x11::CrossingEvent::EnterNotify)
+ state |= EF_IS_SYNTHESIZED;
+ return state;
+ }
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>())
+ return GetEventFlagsFromXState(motion->state);
+ if (auto* xievent = xev.As<x11::Input::DeviceEvent>()) {
+ switch (xievent->opcode) {
+ case x11::Input::DeviceEvent::TouchBegin:
+ case x11::Input::DeviceEvent::TouchUpdate:
+ case x11::Input::DeviceEvent::TouchEnd:
+ return GetButtonMaskForX2Event(*xievent) |
+ GetEventFlagsFromXState(xievent->mods.effective) |
+ GetEventFlagsFromXState(
+ XModifierStateWatcher::GetInstance()->state());
+ case x11::Input::DeviceEvent::ButtonPress:
+ case x11::Input::DeviceEvent::ButtonRelease: {
+ const bool touch =
+ TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
+ int flags = GetButtonMaskForX2Event(*xievent) |
+ GetEventFlagsFromXState(xievent->mods.effective);
+ if (touch) {
+ flags |= GetEventFlagsFromXState(
+ XModifierStateWatcher::GetInstance()->state());
}
+
+ const EventType type = EventTypeFromXEvent(xev);
+ int button = EventButtonFromXEvent(xev);
+ if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch)
+ flags |= GetEventFlagsForButton(button);
+ return flags;
+ }
+ case x11::Input::DeviceEvent::Motion:
+ return GetButtonMaskForX2Event(*xievent) |
+ GetEventFlagsFromXState(xievent->mods.effective);
+ case x11::Input::DeviceEvent::KeyPress:
+ case x11::Input::DeviceEvent::KeyRelease: {
+ XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent(xev);
+ return GetEventFlagsFromXGenericEvent(xev);
}
}
}
@@ -576,66 +554,50 @@ base::TimeTicks EventTimeFromXEvent(const x11::Event& xev) {
return timestamp;
}
-gfx::Point EventLocationFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- switch (xev.type) {
- case x11::CrossingEvent::EnterNotify:
- case x11::CrossingEvent::LeaveNotify:
- return gfx::Point(xev.xcrossing.x, xev.xcrossing.y);
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release:
- return gfx::Point(xev.xbutton.x, xev.xbutton.y);
- case x11::MotionNotifyEvent::opcode:
- return gfx::Point(xev.xmotion.x, xev.xmotion.y);
- case x11::GeGenericEvent::opcode: {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- float x = xievent->event_x;
- float y = xievent->event_y;
+gfx::Point EventLocationFromXEvent(const x11::Event& xev) {
+ if (auto* crossing = xev.As<x11::CrossingEvent>())
+ return gfx::Point(crossing->event_x, crossing->event_y);
+ if (auto* button = xev.As<x11::ButtonEvent>())
+ return gfx::Point(button->event_x, button->event_y);
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>())
+ return gfx::Point(motion->event_x, motion->event_y);
+ if (auto* xievent = xev.As<x11::Input::DeviceEvent>()) {
+ float x = Fp1616ToDouble(xievent->event_x);
+ float y = Fp1616ToDouble(xievent->event_y);
#if defined(OS_CHROMEOS)
- switch (xievent->evtype) {
- case XI_TouchBegin:
- case XI_TouchUpdate:
- case XI_TouchEnd:
- ui::DeviceDataManagerX11::GetInstance()->ApplyTouchTransformer(
- xievent->deviceid, &x, &y);
- break;
- default:
- break;
- }
-#endif // defined(OS_CHROMEOS)
- return gfx::Point(static_cast<int>(x), static_cast<int>(y));
+ switch (xievent->opcode) {
+ case x11::Input::DeviceEvent::TouchBegin:
+ case x11::Input::DeviceEvent::TouchUpdate:
+ case x11::Input::DeviceEvent::TouchEnd:
+ ui::DeviceDataManagerX11::GetInstance()->ApplyTouchTransformer(
+ static_cast<uint16_t>(xievent->deviceid), &x, &y);
+ break;
+ default:
+ break;
}
+#endif // defined(OS_CHROMEOS)
+ return gfx::Point(static_cast<int>(x), static_cast<int>(y));
}
return gfx::Point();
}
-gfx::Point EventSystemLocationFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- switch (xev.type) {
- case x11::CrossingEvent::EnterNotify:
- case x11::CrossingEvent::LeaveNotify: {
- return gfx::Point(xev.xcrossing.x_root, xev.xcrossing.y_root);
- }
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release: {
- return gfx::Point(xev.xbutton.x_root, xev.xbutton.y_root);
- }
- case x11::MotionNotifyEvent::opcode: {
- return gfx::Point(xev.xmotion.x_root, xev.xmotion.y_root);
- }
- case x11::GeGenericEvent::opcode: {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- return gfx::Point(xievent->root_x, xievent->root_y);
- }
+gfx::Point EventSystemLocationFromXEvent(const x11::Event& xev) {
+ if (auto* crossing = xev.As<x11::CrossingEvent>())
+ return gfx::Point(crossing->root_x, crossing->root_y);
+ if (auto* button = xev.As<x11::ButtonEvent>())
+ return gfx::Point(button->root_x, button->root_y);
+ if (auto* motion = xev.As<x11::MotionNotifyEvent>())
+ return gfx::Point(motion->root_x, motion->root_y);
+ if (auto* xievent = xev.As<x11::Input::DeviceEvent>()) {
+ return gfx::Point(Fp1616ToDouble(xievent->root_x),
+ Fp1616ToDouble(xievent->root_y));
}
-
return gfx::Point();
}
-int EventButtonFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- CHECK_EQ(x11::GeGenericEvent::opcode, xev.type);
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
+int EventButtonFromXEvent(const x11::Event& xev) {
+ auto* xievent = xev.As<x11::Input::DeviceEvent>();
+ DCHECK(xievent);
int button = xievent->detail;
return (xievent->sourceid == xievent->deviceid)
@@ -643,47 +605,33 @@ int EventButtonFromXEvent(const x11::Event& x11_event) {
: button;
}
-int GetChangedMouseButtonFlagsFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- switch (xev.type) {
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release:
- return GetEventFlagsForButton(xev.xbutton.button);
- case x11::GeGenericEvent::opcode: {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- switch (xievent->evtype) {
- case XI_ButtonPress:
- case XI_ButtonRelease:
- return GetEventFlagsForButton(EventButtonFromXEvent(x11_event));
- default:
- break;
- }
- break;
- }
- default:
- break;
+int GetChangedMouseButtonFlagsFromXEvent(const x11::Event& xev) {
+ if (auto* button = xev.As<x11::ButtonEvent>())
+ return GetEventFlagsForButton(button->detail);
+ auto* device = xev.As<x11::Input::DeviceEvent>();
+ if (device && (device->opcode == x11::Input::DeviceEvent::ButtonPress ||
+ device->opcode == x11::Input::DeviceEvent::ButtonRelease)) {
+ return GetEventFlagsForButton(EventButtonFromXEvent(xev));
}
return 0;
}
-gfx::Vector2d GetMouseWheelOffsetFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
+gfx::Vector2d GetMouseWheelOffsetFromXEvent(const x11::Event& xev) {
float x_offset, y_offset;
- if (GetScrollOffsetsFromXEvent(x11_event, &x_offset, &y_offset, nullptr,
- nullptr, nullptr)) {
+ if (GetScrollOffsetsFromXEvent(xev, &x_offset, &y_offset, nullptr, nullptr,
+ nullptr)) {
return gfx::Vector2d(static_cast<int>(x_offset),
static_cast<int>(y_offset));
}
- int button = xev.type == x11::GeGenericEvent::opcode
- ? EventButtonFromXEvent(x11_event)
- : xev.xbutton.button;
+ auto* device = xev.As<x11::Input::DeviceEvent>();
+ int button = device ? EventButtonFromXEvent(xev)
+ : static_cast<int>(xev.As<x11::ButtonEvent>()->detail);
// If this is an xinput1 scroll event from an xinput2 mouse then we need to
// block the legacy scroll events for the necessary axes.
int scroll_class_type =
- DeviceDataManagerX11::GetInstance()->GetScrollClassDeviceDetail(
- x11_event);
+ DeviceDataManagerX11::GetInstance()->GetScrollClassDeviceDetail(xev);
bool xi2_vertical = scroll_class_type & SCROLL_TYPE_VERTICAL;
bool xi2_horizontal = scroll_class_type & SCROLL_TYPE_HORIZONTAL;
@@ -738,15 +686,13 @@ float GetTouchAngleFromXEvent(const x11::Event& xev) {
}
float GetTouchForceFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- XIDeviceEvent* event = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- if (event->evtype == XI_TouchEnd)
+ auto* event = x11_event.As<x11::Input::DeviceEvent>();
+ if (event->opcode == x11::Input::DeviceEvent::TouchEnd)
return 0.0;
double force = 0.0;
force = GetTouchParamFromXEvent(
x11_event, ui::DeviceDataManagerX11::DT_TOUCH_PRESSURE, 0.0);
- unsigned int deviceid =
- static_cast<XIDeviceEvent*>(xev.xcookie.data)->sourceid;
+ auto deviceid = event->sourceid;
// Force is normalized to fall into [0, 1]
if (!ui::DeviceDataManagerX11::GetInstance()->NormalizeData(
deviceid, ui::DeviceDataManagerX11::DT_TOUCH_PRESSURE, &force))
@@ -754,14 +700,6 @@ float GetTouchForceFromXEvent(const x11::Event& x11_event) {
return force;
}
-EventPointerType GetTouchPointerTypeFromXEvent(const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- XIDeviceEvent* event = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- DCHECK(ui::TouchFactory::GetInstance()->IsTouchDevice(event->sourceid));
- return ui::TouchFactory::GetInstance()->GetTouchDevicePointerType(
- event->sourceid);
-}
-
PointerDetails GetTouchPointerDetailsFromXEvent(const x11::Event& xev) {
return PointerDetails(
EventPointerType::kTouch, GetTouchIdFromXEvent(xev),
@@ -850,6 +788,10 @@ bool IsAltPressed() {
return XModifierStateWatcher::GetInstance()->state() & Mod1Mask;
}
+int GetModifierKeyState() {
+ return XModifierStateWatcher::GetInstance()->state();
+}
+
void ResetTimestampRolloverCountersForTesting() {
g_last_seen_timestamp_ms = 0;
g_rollover_ms = 0;
diff --git a/chromium/ui/events/x/events_x_utils.h b/chromium/ui/events/x/events_x_utils.h
index 6fb201cfa6e..4dbca2f3270 100644
--- a/chromium/ui/events/x/events_x_utils.h
+++ b/chromium/ui/events/x/events_x_utils.h
@@ -18,6 +18,7 @@
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/event.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xinput.h"
namespace ui {
@@ -65,10 +66,6 @@ EVENTS_X_EXPORT float GetTouchAngleFromXEvent(const x11::Event& xev);
// Gets the force from a native_event. Normalized to be [0, 1]. Default is 0.0.
EVENTS_X_EXPORT float GetTouchForceFromXEvent(const x11::Event& xev);
-// Gets the pointer type from a native_event.
-EVENTS_X_EXPORT EventPointerType
-GetTouchPointerTypeFromXEvent(const x11::Event& xev);
-
// Gets the pointer details from an x11::Event.
EVENTS_X_EXPORT PointerDetails
GetTouchPointerDetailsFromXEvent(const x11::Event& xev);
@@ -94,6 +91,10 @@ EVENTS_X_EXPORT bool GetFlingDataFromXEvent(const x11::Event& xev,
// Uses the XModifierStateWatcher to determine if alt is pressed or not.
EVENTS_X_EXPORT bool IsAltPressed();
+// Proxies the XModifierStateWatcher::state() to return the current state of
+// modifier keys.
+EVENTS_X_EXPORT int GetModifierKeyState();
+
EVENTS_X_EXPORT void ResetTimestampRolloverCountersForTesting();
} // namespace ui
diff --git a/chromium/ui/events/x/keyboard_hook_x11.cc b/chromium/ui/events/x/keyboard_hook_x11.cc
index ac4b00dd22f..670534c08e6 100644
--- a/chromium/ui/events/x/keyboard_hook_x11.cc
+++ b/chromium/ui/events/x/keyboard_hook_x11.cc
@@ -2,29 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/events/x/keyboard_hook_x11.h"
+
#include <memory>
#include <utility>
-#include <vector>
#include "base/callback.h"
#include "base/check_op.h"
#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "base/optional.h"
#include "base/stl_util.h"
-#include "base/threading/thread_checker.h"
#include "ui/events/event.h"
-#include "ui/events/keyboard_hook_base.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_types.h"
namespace ui {
namespace {
+static KeyboardHookX11* g_instance = nullptr;
+
// XGrabKey essentially requires the modifier mask to explicitly be specified.
// You can specify 'AnyModifier' however doing so means the call to XGrabKey
// will fail if that key has been grabbed with any combination of modifiers.
@@ -47,38 +43,7 @@ const DomCode kDomCodesForLockAllKeys[] = {
DomCode::CONTROL_RIGHT, DomCode::SHIFT_RIGHT, DomCode::ALT_RIGHT,
DomCode::META_RIGHT};
-// A default implementation for the X11 platform.
-class KeyboardHookX11 : public KeyboardHookBase {
- public:
- KeyboardHookX11(base::Optional<base::flat_set<DomCode>> dom_codes,
- gfx::AcceleratedWidget accelerated_widget,
- KeyEventCallback callback);
- ~KeyboardHookX11() override;
-
- void Register();
-
- private:
- static KeyboardHookX11* instance_;
-
- // Helper methods for setting up key event capture.
- void CaptureAllKeys();
- void CaptureSpecificKeys();
- void CaptureKeyForDomCode(DomCode dom_code);
-
- THREAD_CHECKER(thread_checker_);
-
- // The x11 default display and the owner's native window.
- XDisplay* const x_display_ = nullptr;
- const gfx::AcceleratedWidget x_window_ = gfx::kNullAcceleratedWidget;
-
- // Tracks the keys that were grabbed.
- std::vector<int> grabbed_keys_;
-
- DISALLOW_COPY_AND_ASSIGN(KeyboardHookX11);
-};
-
-// static
-KeyboardHookX11* KeyboardHookX11::instance_ = nullptr;
+} // namespace
KeyboardHookX11::KeyboardHookX11(
base::Optional<base::flat_set<DomCode>> dom_codes,
@@ -91,8 +56,8 @@ KeyboardHookX11::KeyboardHookX11(
KeyboardHookX11::~KeyboardHookX11() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK_EQ(instance_, this);
- instance_ = nullptr;
+ DCHECK_EQ(g_instance, this);
+ g_instance = nullptr;
// Use XUngrabKeys for each key that has been grabbed. XUngrabKeyboard
// purportedly releases all keys when called and would not require the nested
@@ -105,17 +70,19 @@ KeyboardHookX11::~KeyboardHookX11() {
}
}
-void KeyboardHookX11::Register() {
+bool KeyboardHookX11::RegisterHook() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Only one instance of this class can be registered at a time.
- DCHECK(!instance_);
- instance_ = this;
+ DCHECK(!g_instance);
+ g_instance = this;
if (dom_codes().has_value())
CaptureSpecificKeys();
else
CaptureAllKeys();
+
+ return true;
}
void KeyboardHookX11::CaptureAllKeys() {
@@ -155,26 +122,4 @@ void KeyboardHookX11::CaptureKeyForDomCode(DomCode dom_code) {
grabbed_keys_.push_back(native_key_code);
}
-} // namespace
-
-// static
-std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
- base::Optional<base::flat_set<DomCode>> dom_codes,
- gfx::AcceleratedWidget accelerated_widget,
- KeyboardHook::KeyEventCallback callback) {
- std::unique_ptr<KeyboardHookX11> keyboard_hook =
- std::make_unique<KeyboardHookX11>(
- std::move(dom_codes), accelerated_widget, std::move(callback));
-
- keyboard_hook->Register();
-
- return keyboard_hook;
-}
-
-// static
-std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
- KeyEventCallback callback) {
- return nullptr;
-}
-
} // namespace ui
diff --git a/chromium/ui/events/x/keyboard_hook_x11.h b/chromium/ui/events/x/keyboard_hook_x11.h
new file mode 100644
index 00000000000..7a217075bea
--- /dev/null
+++ b/chromium/ui/events/x/keyboard_hook_x11.h
@@ -0,0 +1,50 @@
+// 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 UI_EVENTS_X_KEYBOARD_HOOK_X11_H_
+#define UI_EVENTS_X_KEYBOARD_HOOK_X11_H_
+
+#include <vector>
+
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "ui/events/keyboard_hook_base.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace ui {
+
+// A default implementation for the X11 platform.
+class KeyboardHookX11 : public KeyboardHookBase {
+ public:
+ KeyboardHookX11(base::Optional<base::flat_set<DomCode>> dom_codes,
+ gfx::AcceleratedWidget accelerated_widget,
+ KeyEventCallback callback);
+ KeyboardHookX11(const KeyboardHookX11&) = delete;
+ KeyboardHookX11& operator=(const KeyboardHookX11&) = delete;
+ ~KeyboardHookX11() override;
+
+ // KeyboardHookBase:
+ bool RegisterHook() override;
+
+ private:
+ // Helper methods for setting up key event capture.
+ void CaptureAllKeys();
+ void CaptureSpecificKeys();
+ void CaptureKeyForDomCode(DomCode dom_code);
+
+ THREAD_CHECKER(thread_checker_);
+
+ // The x11 default display and the owner's native window.
+ XDisplay* const x_display_ = nullptr;
+ const gfx::AcceleratedWidget x_window_ = gfx::kNullAcceleratedWidget;
+
+ // Tracks the keys that were grabbed.
+ std::vector<int> grabbed_keys_;
+};
+
+} // namespace ui
+
+#endif // UI_EVENTS_X_KEYBOARD_HOOK_X11_H_
diff --git a/chromium/ui/events/x/x11_event_translation.cc b/chromium/ui/events/x/x11_event_translation.cc
index fcff13c80e6..6f4c17d724b 100644
--- a/chromium/ui/events/x/x11_event_translation.cc
+++ b/chromium/ui/events/x/x11_event_translation.cc
@@ -18,6 +18,11 @@
#include "ui/events/x/events_x_utils.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
+
+#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
+#endif
namespace ui {
@@ -44,20 +49,22 @@ class TouchEventX11 : public ui::TouchEvent {
Event::Properties GetEventPropertiesFromXEvent(EventType type,
const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
using Values = std::vector<uint8_t>;
Event::Properties properties;
if (type == ET_KEY_PRESSED || type == ET_KEY_RELEASED) {
+ auto* key = x11_event.As<x11::KeyEvent>();
+
// Keyboard group
- uint8_t group = XkbGroupForCoreState(xev.xkey.state);
+ auto state = static_cast<uint32_t>(key->state);
+ uint8_t group = XkbGroupForCoreState(state);
properties.emplace(kPropertyKeyboardGroup, Values{group});
// Hardware keycode
- uint8_t hw_keycode = xev.xkey.keycode;
+ uint8_t hw_keycode = static_cast<uint8_t>(key->detail);
properties.emplace(kPropertyKeyboardHwKeyCode, Values{hw_keycode});
// IBus-gtk specific flags
- uint8_t ibus_flags = (xev.xkey.state >> kPropertyKeyboardIBusFlagOffset) &
+ uint8_t ibus_flags = (state >> kPropertyKeyboardIBusFlagOffset) &
kPropertyKeyboardIBusFlagMask;
if (ibus_flags)
properties.emplace(kPropertyKeyboardIBusFlag, Values{ibus_flags});
@@ -66,7 +73,9 @@ Event::Properties GetEventPropertiesFromXEvent(EventType type,
// NotifyVirtual events are created for intermediate windows that the
// pointer crosses through. These occur when middle clicking.
// Change these into mouse move events.
- bool crossing_intermediate_window = xev.xcrossing.detail == NotifyVirtual;
+ auto* crossing = x11_event.As<x11::CrossingEvent>();
+ bool crossing_intermediate_window =
+ crossing->detail == x11::NotifyDetail::Virtual;
if (crossing_intermediate_window) {
properties.emplace(kPropertyMouseCrossedIntermediateWindow,
crossing_intermediate_window);
@@ -77,20 +86,25 @@ Event::Properties GetEventPropertiesFromXEvent(EventType type,
std::unique_ptr<KeyEvent> CreateKeyEvent(EventType event_type,
const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- KeyboardCode key_code = KeyboardCodeFromXKeyEvent(&xev);
+ KeyboardCode key_code = KeyboardCodeFromXKeyEvent(x11_event);
int event_flags = EventFlagsFromXEvent(x11_event);
// In Ozone builds, keep DomCode/DomKey unset, so they are extracted lazily
// in KeyEvent::ApplyLayout() which makes it possible for CrOS/Linux, for
// example, to support host system keyboard layouts.
+ std::unique_ptr<KeyEvent> event;
#if defined(USE_OZONE)
- auto event = std::make_unique<KeyEvent>(event_type, key_code, event_flags,
- EventTimeFromXEvent(x11_event));
-#else
- auto event = std::make_unique<KeyEvent>(
- event_type, key_code, CodeFromXEvent(&xev), event_flags,
- GetDomKeyFromXEvent(&xev), EventTimeFromXEvent(x11_event));
+ if (features::IsUsingOzonePlatform()) {
+ event = std::make_unique<KeyEvent>(event_type, key_code, event_flags,
+ EventTimeFromXEvent(x11_event));
+ }
+#endif
+#if defined(USE_X11)
+ if (!event) {
+ event = std::make_unique<KeyEvent>(
+ event_type, key_code, CodeFromXEvent(x11_event), event_flags,
+ GetDomKeyFromXEvent(x11_event), EventTimeFromXEvent(x11_event));
+ }
#endif
DCHECK(event);
@@ -99,24 +113,19 @@ std::unique_ptr<KeyEvent> CreateKeyEvent(EventType event_type,
return event;
}
-void SetEventSourceDeviceId(MouseEvent* event, const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
+void SetEventSourceDeviceId(MouseEvent* event, const x11::Event& xev) {
DCHECK(event);
- if (xev.type == x11::GeGenericEvent::opcode) {
- XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
- event->set_source_device_id(xiev->sourceid);
- }
+ if (auto* xiev = xev.As<x11::Input::DeviceEvent>())
+ event->set_source_device_id(static_cast<uint16_t>(xiev->sourceid));
}
std::unique_ptr<MouseEvent> CreateMouseEvent(EventType type,
const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
// Ignore EventNotify and LeaveNotify events from children of |xwindow_|.
// NativeViewGLSurfaceGLX adds a child to |xwindow_|.
// https://crbug.com/792322
- bool enter_or_leave = xev.type == x11::CrossingEvent::EnterNotify ||
- xev.type == x11::CrossingEvent::LeaveNotify;
- if (enter_or_leave && xev.xcrossing.detail == NotifyInferior)
+ auto* crossing = x11_event.As<x11::CrossingEvent>();
+ if (crossing && crossing->detail == x11::NotifyDetail::Inferior)
return nullptr;
PointerDetails details{EventPointerType::kMouse};
@@ -135,8 +144,7 @@ std::unique_ptr<MouseEvent> CreateMouseEvent(EventType type,
std::unique_ptr<MouseWheelEvent> CreateMouseWheelEvent(
const x11::Event& x11_event) {
- const XEvent& xev = x11_event.xlib_event();
- int button_flags = (xev.type == x11::GeGenericEvent::opcode)
+ int button_flags = x11_event.As<x11::Input::DeviceEvent>()
? GetChangedMouseButtonFlagsFromXEvent(x11_event)
: 0;
auto event = std::make_unique<MouseWheelEvent>(
@@ -156,10 +164,12 @@ std::unique_ptr<TouchEvent> CreateTouchEvent(EventType type,
type, EventLocationFromXEvent(xev), EventTimeFromXEvent(xev),
GetTouchPointerDetailsFromXEvent(xev));
#if defined(USE_OZONE)
- // Touch events don't usually have |root_location| set differently than
- // |location|, since there is a touch device to display association, but this
- // doesn't happen in Ozone X11.
- event->set_root_location(EventSystemLocationFromXEvent(xev));
+ if (features::IsUsingOzonePlatform()) {
+ // Touch events don't usually have |root_location| set differently than
+ // |location|, since there is a touch device to display association, but
+ // this doesn't happen in Ozone X11.
+ event->set_root_location(EventSystemLocationFromXEvent(xev));
+ }
#endif
return event;
}
@@ -225,34 +235,27 @@ std::unique_ptr<ui::Event> TranslateFromXI2Event(const x11::Event& xev,
std::unique_ptr<Event> TranslateFromXEvent(const x11::Event& xev) {
EventType event_type = EventTypeFromXEvent(xev);
- switch (xev.xlib_event().type) {
- case x11::CrossingEvent::LeaveNotify:
- case x11::CrossingEvent::EnterNotify:
- case x11::MotionNotifyEvent::opcode:
- return CreateMouseEvent(event_type, xev);
- case x11::KeyEvent::Press:
- case x11::KeyEvent::Release:
- return CreateKeyEvent(event_type, xev);
- case x11::ButtonEvent::Press:
- case x11::ButtonEvent::Release: {
- switch (event_type) {
- case ET_MOUSEWHEEL:
- return CreateMouseWheelEvent(xev);
- case ET_MOUSE_PRESSED:
- case ET_MOUSE_RELEASED:
- return CreateMouseEvent(event_type, xev);
- case ET_UNKNOWN:
- // No event is created for X11-release events for mouse-wheel
- // buttons.
- break;
- default:
- NOTREACHED();
- }
- break;
+ if (xev.As<x11::CrossingEvent>() || xev.As<x11::MotionNotifyEvent>())
+ return CreateMouseEvent(event_type, xev);
+ if (xev.As<x11::KeyEvent>())
+ return CreateKeyEvent(event_type, xev);
+ if (xev.As<x11::ButtonEvent>()) {
+ switch (event_type) {
+ case ET_MOUSEWHEEL:
+ return CreateMouseWheelEvent(xev);
+ case ET_MOUSE_PRESSED:
+ case ET_MOUSE_RELEASED:
+ return CreateMouseEvent(event_type, xev);
+ case ET_UNKNOWN:
+ // No event is created for X11-release events for mouse-wheel
+ // buttons.
+ break;
+ default:
+ NOTREACHED();
}
- case x11::GeGenericEvent::opcode:
- return TranslateFromXI2Event(xev, event_type);
}
+ if (xev.As<x11::Input::DeviceEvent>())
+ return TranslateFromXI2Event(xev, event_type);
return nullptr;
}
diff --git a/chromium/ui/events/x/x11_event_translation_unittest.cc b/chromium/ui/events/x/x11_event_translation_unittest.cc
deleted file mode 100644
index 5f43a97bf90..00000000000
--- a/chromium/ui/events/x/x11_event_translation_unittest.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 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 "ui/events/x/x11_event_translation.h"
-
-#include <xcb/xcb.h>
-
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/base_event_utils.h"
-#include "ui/events/event.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/keycodes/dom/dom_code.h"
-#include "ui/events/keycodes/dom/dom_key.h"
-#include "ui/events/keycodes/dom/keycode_converter.h"
-#include "ui/events/keycodes/keyboard_codes_posix.h"
-#include "ui/events/test/events_test_utils.h"
-#include "ui/events/test/events_test_utils_x11.h"
-#include "ui/events/test/keyboard_layout.h"
-#include "ui/events/test/scoped_event_test_tick_clock.h"
-#include "ui/events/types/event_type.h"
-#include "ui/gfx/x/connection.h"
-#include "ui/gfx/x/event.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/xproto.h"
-
-namespace ui {
-
-// Ensure DomKey extraction happens lazily in Ozone X11, while in non-Ozone
-// path it is set right away in XEvent => ui::Event translation. This prevents
-// regressions such as crbug.com/1007389.
-TEST(XEventTranslationTest, KeyEventDomKeyExtraction) {
- ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
- ScopedXI2Event xev;
- xev.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_NONE);
-
- auto keyev = ui::BuildKeyEventFromXEvent(*xev);
- EXPECT_TRUE(keyev);
-
- KeyEventTestApi test(keyev.get());
-#if defined(USE_OZONE)
- EXPECT_EQ(ui::DomKey::NONE, test.dom_key());
-#else
- EXPECT_EQ(ui::DomKey::ENTER, test.dom_key());
-#endif
-
- EXPECT_EQ(13, keyev->GetCharacter());
- EXPECT_EQ("Enter", keyev->GetCodeString());
-}
-
-// Ensure KeyEvent::Properties is properly set regardless X11 build config is
-// in place. This prevents regressions such as crbug.com/1047999.
-TEST(XEventTranslationTest, KeyEventXEventPropertiesSet) {
- ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
- ScopedXI2Event scoped_xev;
- scoped_xev.InitKeyEvent(ET_KEY_PRESSED, VKEY_A, EF_NONE);
-
- x11::Event* xev = scoped_xev;
- XDisplay* xdisplay = xev->xlib_event().xkey.display;
- // Set keyboard group in XKeyEvent
- uint32_t state = XkbBuildCoreState(xev->xlib_event().xkey.state, 2u);
- // Set IBus-specific flags
- state |= 0x3 << ui::kPropertyKeyboardIBusFlagOffset;
- xev->xlib_event().xkey.state = state;
- xev->As<x11::KeyEvent>()->state = static_cast<x11::KeyButMask>(state);
-
- auto keyev = ui::BuildKeyEventFromXEvent(*xev);
- EXPECT_TRUE(keyev);
-
- auto* properties = keyev->properties();
- EXPECT_TRUE(properties);
- EXPECT_EQ(3u, properties->size());
-
- // Ensure hardware keycode, keyboard group and ibus flag properties are
- // properly set.
- auto hw_keycode_it = properties->find(ui::kPropertyKeyboardHwKeyCode);
- EXPECT_NE(hw_keycode_it, properties->end());
- EXPECT_EQ(1u, hw_keycode_it->second.size());
- EXPECT_EQ(XKeysymToKeycode(xdisplay, XK_a), hw_keycode_it->second[0]);
-
- auto kbd_group_it = properties->find(ui::kPropertyKeyboardGroup);
- EXPECT_NE(kbd_group_it, properties->end());
- EXPECT_EQ(1u, kbd_group_it->second.size());
- EXPECT_EQ(2u, kbd_group_it->second[0]);
-
- auto ibus_flag_it = properties->find(ui::kPropertyKeyboardIBusFlag);
- EXPECT_NE(ibus_flag_it, properties->end());
- EXPECT_EQ(1u, ibus_flag_it->second.size());
- EXPECT_EQ(0x3, ibus_flag_it->second[0]);
-}
-
-// Ensure XEvents with bogus timestamps are properly handled when translated
-// into ui::*Events.
-TEST(XEventTranslationTest, BogusTimestampCorrection) {
- using base::TimeDelta;
- using base::TimeTicks;
-
- ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
- ScopedXI2Event scoped_xev;
- scoped_xev.InitKeyEvent(ET_KEY_PRESSED, VKEY_RETURN, EF_NONE);
- x11::Event* xev = scoped_xev;
-
- test::ScopedEventTestTickClock test_clock;
- test_clock.Advance(TimeDelta::FromSeconds(1));
-
- // Set initial time as 1000ms
- TimeTicks now_ticks = EventTimeForNow();
- int64_t now_ms = (now_ticks - TimeTicks()).InMilliseconds();
- EXPECT_EQ(1000, now_ms);
-
- // Emulate XEvent generated 500ms before current time (non-bogus) and verify
- // the translated Event uses native event's timestamp.
- xev->xlib_event().xkey.time = 500;
- xev->As<x11::KeyEvent>()->time = static_cast<x11::Time>(500);
- auto keyev = ui::BuildKeyEventFromXEvent(*xev);
- EXPECT_TRUE(keyev);
- EXPECT_EQ(now_ticks - TimeDelta::FromMilliseconds(500), keyev->time_stamp());
-
- // Emulate XEvent generated 1000ms ahead in time (bogus timestamp) and verify
- // the translated Event's timestamp is fixed using (i.e: EventTimeForNow()
- // instead of the original XEvent's time)
- xev->xlib_event().xkey.time = 2000;
- xev->As<x11::KeyEvent>()->time = static_cast<x11::Time>(2000);
- auto keyev2 = ui::BuildKeyEventFromXEvent(*xev);
- EXPECT_TRUE(keyev2);
- EXPECT_EQ(EventTimeForNow(), keyev2->time_stamp());
-
- // Emulate XEvent >= 60sec old (bogus timestamp) and ensure translated
- // ui::Event's timestamp has been corrected (i.e: use ui::EventTimeForNow()
- // instead of the original XEvent's time). To emulate such scenario, we
- // advance the clock by 5 minutes and set the XEvent's time to 1min, so delta
- // is 4min 1sec.
- test_clock.Advance(TimeDelta::FromMinutes(5));
- xev->xlib_event().xkey.time = 1000 * 60;
- xev->As<x11::KeyEvent>()->time = static_cast<x11::Time>(1000 * 60);
- auto keyev3 = ui::BuildKeyEventFromXEvent(*xev);
- EXPECT_TRUE(keyev3);
- EXPECT_EQ(EventTimeForNow(), keyev3->time_stamp());
-}
-
-// Ensure MouseEvent::changed_button_flags is correctly translated from
-// X{Button,Crossing}Events.
-TEST(XEventTranslationTest, ChangedMouseButtonFlags) {
- ui::ScopedXI2Event event;
- // Taking in a ButtonPress XEvent, with left button pressed.
- event.InitButtonEvent(ui::ET_MOUSE_PRESSED, gfx::Point(500, 500),
- ui::EF_LEFT_MOUSE_BUTTON);
- auto mouseev = ui::BuildMouseEventFromXEvent(*event);
- EXPECT_TRUE(mouseev);
- EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, mouseev->changed_button_flags());
-
- // Taking in a ButtonPress XEvent, with no button pressed.
- x11::Event& x11_event = *event;
- x11_event.xlib_event().xbutton.button = 0;
- x11_event.As<x11::ButtonEvent>()->detail = static_cast<x11::Button>(0);
- auto mouseev2 = ui::BuildMouseEventFromXEvent(*event);
- EXPECT_TRUE(mouseev2);
- EXPECT_EQ(0, mouseev2->changed_button_flags());
-
- // Taking in a EnterNotify XEvent
- xcb_generic_event_t ge;
- memset(&ge, 0, sizeof(ge));
- auto* enter = reinterpret_cast<xcb_enter_notify_event_t*>(&ge);
- enter->response_type = x11::CrossingEvent::EnterNotify;
- enter->detail = NotifyVirtual;
- x11::Event enter_event(&ge, x11::Connection::Get());
-
- auto mouseev3 = ui::BuildMouseEventFromXEvent(enter_event);
- EXPECT_TRUE(mouseev3);
- EXPECT_EQ(0, mouseev3->changed_button_flags());
-}
-
-// Verifies 'repeat' flag is properly set when key events for modifiers and
-// their counterparts are mixed. Ensures regressions like crbug.com/1069690
-// are not reintroduced in the future.
-TEST(XEventTranslationTest, KeyModifiersCounterpartRepeat) {
- using base::TimeDelta;
-
- // Use a TestTickClock so we have the power to control the time :)
- test::ScopedEventTestTickClock test_clock;
-
- // Create and init a XEvent for ShiftLeft key.
- ui::ScopedXI2Event shift_l_pressed;
- shift_l_pressed.InitKeyEvent(ET_KEY_PRESSED, VKEY_LSHIFT, EF_NONE);
-
- // Press ShiftLeft a first time and hold it.
- auto keyev_shift_l_pressed = BuildKeyEventFromXEvent(*shift_l_pressed);
- EXPECT_FALSE(keyev_shift_l_pressed->is_repeat());
-
- // Create a few more ShiftLeft key events and ensure 'repeat' flag is set.
- test_clock.Advance(TimeDelta::FromMilliseconds(100));
- keyev_shift_l_pressed = BuildKeyEventFromXEvent(*shift_l_pressed);
- EXPECT_TRUE(keyev_shift_l_pressed->is_repeat());
-
- test_clock.Advance(TimeDelta::FromMilliseconds(200));
- keyev_shift_l_pressed = BuildKeyEventFromXEvent(*shift_l_pressed);
- EXPECT_TRUE(keyev_shift_l_pressed->is_repeat());
-
- test_clock.Advance(TimeDelta::FromMilliseconds(500));
- keyev_shift_l_pressed = BuildKeyEventFromXEvent(*shift_l_pressed);
- EXPECT_TRUE(keyev_shift_l_pressed->is_repeat());
-
- // Press and release ShiftRight and verify 'repeat' flag is not set.
-
- // Create and init XEvent for emulating a ShiftRight key press.
- ui::ScopedXI2Event shift_r_pressed;
- shift_r_pressed.InitKeyEvent(ET_KEY_PRESSED, VKEY_RSHIFT, EF_SHIFT_DOWN);
-
- test_clock.Advance(TimeDelta::FromSeconds(1));
- auto keyev_shift_r_pressed = BuildKeyEventFromXEvent(*shift_r_pressed);
- EXPECT_FALSE(keyev_shift_r_pressed->is_repeat());
- EXPECT_EQ(ET_KEY_PRESSED, keyev_shift_r_pressed->type());
-
- // Create and init XEvent for emulating a ShiftRight key release.
- ui::ScopedXI2Event shift_r_released;
- shift_r_released.InitKeyEvent(ET_KEY_RELEASED, VKEY_RSHIFT, EF_SHIFT_DOWN);
-
- test_clock.Advance(TimeDelta::FromMilliseconds(300));
- auto keyev_shift_r_released = BuildKeyEventFromXEvent(*shift_r_released);
- EXPECT_FALSE(keyev_shift_r_released->is_repeat());
- EXPECT_EQ(ET_KEY_RELEASED, keyev_shift_r_released->type());
-}
-
-} // namespace ui
diff --git a/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn
index 8cc31e64297..25626506e74 100644
--- a/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn
+++ b/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn
@@ -24,6 +24,7 @@ js_type_check("closure_compile_module") {
":files_message",
":files_metadata_box",
":files_metadata_entry",
+ ":files_password_dialog",
":files_quick_view",
":files_ripple",
":files_safe_media",
@@ -78,6 +79,23 @@ js_library("files_metadata_box") {
js_library("files_metadata_entry") {
}
+js_library("files_password_dialog") {
+ deps = [
+ "//ui/file_manager/file_manager/common/js:async_util",
+ "//ui/webui/resources/cr_elements/cr_button:cr_button",
+ "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+ "//ui/webui/resources/cr_elements/cr_input:cr_input",
+ ]
+}
+
+js_unittest("files_password_dialog_unittest") {
+ deps = [
+ ":files_password_dialog",
+ "//ui/file_manager/base/js:test_error_reporting",
+ "//ui/webui/resources/js:webui_resource_test",
+ ]
+}
+
js_library("files_quick_view") {
deps = [ ":files_metadata_box" ]
externs_list = [
@@ -146,6 +164,7 @@ js_unittest("files_xf_elements_unittest") {
js_test_gen_html("js_test_gen_html") {
deps = [
":files_message_unittest",
+ ":files_password_dialog_unittest",
":files_toast_unittest",
":files_tooltip_unittest",
":files_xf_elements_unittest",
diff --git a/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 338795f93e7..9225f7db982 100644
--- a/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -45,6 +45,7 @@ js_type_check("closure_compile_module") {
":file_selection",
":file_tasks",
":file_transfer_controller",
+ ":file_type_filters_controller",
":file_watcher",
":folder_shortcuts_data_model",
":gear_menu_controller",
@@ -358,6 +359,7 @@ js_library("file_manager") {
":empty_folder_controller",
":file_selection",
":file_transfer_controller",
+ ":file_type_filters_controller",
":folder_shortcuts_data_model",
":gear_menu_controller",
":import_controller",
@@ -488,6 +490,20 @@ js_unittest("file_transfer_controller_unittest") {
]
}
+js_library("file_type_filters_controller") {
+ deps = [
+ ":directory_model",
+ "//ui/file_manager/file_manager/common/js:files_app_entry_types",
+ ]
+}
+
+js_unittest("file_type_filters_controller_unittest") {
+ deps = [
+ ":file_type_filters_controller",
+ "//ui/webui/resources/js:webui_resource_test",
+ ]
+}
+
js_library("file_watcher") {
deps = [
"//ui/file_manager/base/js:volume_manager_types",
@@ -847,6 +863,7 @@ js_test_gen_html("js_test_gen_html") {
":file_manager_commands_unittest",
":file_tasks_unittest",
":file_transfer_controller_unittest",
+ ":file_type_filters_controller_unittest",
":import_controller_unittest",
":list_thumbnail_loader_unittest",
":navigation_list_model_unittest",
diff --git a/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 422d15d2972..d983cab92d1 100644
--- a/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -286,6 +286,7 @@ js_library("file_manager_ui") {
":suggest_apps_dialog",
"//ui/file_manager/file_manager/common/js:util",
"//ui/file_manager/file_manager/foreground/elements:files_message",
+ "//ui/file_manager/file_manager/foreground/elements:files_password_dialog",
"//ui/file_manager/file_manager/foreground/elements:files_spinner",
"//ui/file_manager/file_manager/foreground/elements:files_toast",
"//ui/file_manager/file_manager/foreground/elements:files_toggle_ripple",
@@ -436,6 +437,8 @@ js_library("location_line") {
js_library("multi_menu") {
deps = [
+ # TODO(files-ng): remove util dep when the files-ng flag is removed.
+ "//ui/file_manager/file_manager/common/js:util",
"//ui/webui/resources/js:event_tracker",
"//ui/webui/resources/js/cr/ui:menu",
"//ui/webui/resources/js/cr/ui:menu_item",
@@ -446,6 +449,8 @@ js_library("multi_menu") {
js_library("multi_menu_button") {
deps = [
+ # TODO(files-ng): remove util dep when the files-ng flag is removed.
+ "//ui/file_manager/file_manager/common/js:util",
"//ui/webui/resources/js:event_tracker",
"//ui/webui/resources/js/cr/ui:menu",
"//ui/webui/resources/js/cr/ui:menu_button",
diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn
index 86e50da4cb0..e688b1c5cd6 100644
--- a/chromium/ui/gfx/BUILD.gn
+++ b/chromium/ui/gfx/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//device/vr/buildflags/buildflags.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
@@ -28,7 +27,7 @@ source_set("gfx_export") {
}
# Used for color generation at build time without importing all the gfx.
-jumbo_component("color_utils") {
+component("color_utils") {
sources = [
"color_palette.h",
"color_utils.cc",
@@ -43,7 +42,7 @@ jumbo_component("color_utils") {
]
}
-jumbo_component("geometry_skia") {
+component("geometry_skia") {
sources = [
"geometry_skia_export.h",
"rrect_f.cc",
@@ -66,7 +65,7 @@ jumbo_component("geometry_skia") {
defines = [ "GEOMETRY_SKIA_IMPLEMENTATION" ]
}
-jumbo_component("gfx") {
+component("gfx") {
sources = [
"break_list.h",
"color_analysis.cc",
@@ -150,7 +149,7 @@ jumbo_component("gfx") {
"android/view_configuration.h",
]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"font_fallback_linux.cc",
"font_fallback_linux.h",
@@ -386,12 +385,12 @@ jumbo_component("gfx") {
}
# Linux.
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//third_party/fontconfig" ]
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreFoundation.framework",
"CoreGraphics.framework",
@@ -412,7 +411,7 @@ jumbo_component("gfx") {
}
}
-jumbo_component("color_space") {
+component("color_space") {
sources = [
"color_space.cc",
"color_space.h",
@@ -443,7 +442,7 @@ jumbo_component("color_space") {
"mac/display_icc_profiles.cc",
"mac/display_icc_profiles.h",
]
- libs = [
+ frameworks = [
"CoreFoundation.framework",
"CoreGraphics.framework",
]
@@ -519,7 +518,7 @@ group("memory_buffer") {
}
# Cannot be a static_library in component builds due to exported functions
-jumbo_source_set("memory_buffer_sources") {
+source_set("memory_buffer_sources") {
visibility = [ ":*" ] # Depend on through ":memory_buffer".
# TODO(brettw) refactor this so these sources are in a coherent directory
@@ -566,7 +565,7 @@ jumbo_source_set("memory_buffer_sources") {
"//ui/gfx/geometry",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"linux/client_native_pixmap_dmabuf.cc",
"linux/client_native_pixmap_dmabuf.h",
@@ -579,7 +578,7 @@ jumbo_source_set("memory_buffer_sources") {
deps += [ "//build/config/linux/libdrm" ]
}
- if (is_linux || is_android) {
+ if (is_linux || is_chromeos || is_android) {
deps += [ "//third_party/libsync" ]
}
@@ -596,7 +595,7 @@ jumbo_source_set("memory_buffer_sources") {
public_deps += [ "//ipc:message_support" ]
}
- if ((is_linux || use_ozone) && !is_nacl) {
+ if ((is_linux || is_chromeos || use_ozone) && !is_nacl) {
sources += [
"native_pixmap_handle.cc",
"native_pixmap_handle.h",
@@ -605,7 +604,7 @@ jumbo_source_set("memory_buffer_sources") {
}
# TODO(ccameron): This can be moved into a separate source_set.
-jumbo_component("gfx_switches") {
+component("gfx_switches") {
sources = [
"switches.cc",
"switches.h",
@@ -617,7 +616,7 @@ jumbo_component("gfx_switches") {
deps = [ "//base" ]
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"animation/animation_test_api.cc",
@@ -672,7 +671,7 @@ test("gfx_unittests") {
"text_elider_unittest.cc",
"text_utils_unittest.cc",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"font_fallback_linux_unittest.cc",
"font_render_params_linux_unittest.cc",
@@ -733,7 +732,6 @@ test("gfx_unittests") {
"geometry/quaternion_unittest.cc",
"geometry/rect_unittest.cc",
"geometry/rounded_corners_f_unittest.cc",
- "geometry/safe_integer_conversions_unittest.cc",
"geometry/scroll_offset_unittest.cc",
"geometry/size_unittest.cc",
"geometry/vector2d_unittest.cc",
@@ -763,7 +761,7 @@ test("gfx_unittests") {
sources += [ "system_fonts_win_unittest.cc" ]
}
- if (is_linux || is_android || is_fuchsia || is_win) {
+ if (is_linux || is_chromeos || is_android || is_fuchsia || is_win) {
sources += [ "platform_font_skia_unittest.cc" ]
}
@@ -794,7 +792,7 @@ test("gfx_unittests") {
data_deps = [ "//ui/resources:ui_test_pak_data" ]
- if (is_mac || is_ios) {
+ if (is_apple) {
deps += [ "//ui/resources:ui_test_pak_bundle_data" ]
}
@@ -855,7 +853,7 @@ test("gfx_unittests") {
]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"linux/fontconfig_util_unittest.cc",
"linux/native_pixmap_dmabuf_unittest.cc",
diff --git a/chromium/ui/gfx/animation/BUILD.gn b/chromium/ui/gfx/animation/BUILD.gn
index f2c3edbbf16..49b6c01a7a7 100644
--- a/chromium/ui/gfx/animation/BUILD.gn
+++ b/chromium/ui/gfx/animation/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
# Reset sources_assignment_filter for the BUILD.gn file to prevent
@@ -16,7 +15,7 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
-jumbo_component("animation") {
+component("animation") {
sources = [
"animation.cc",
"animation.h",
@@ -67,7 +66,7 @@ jumbo_component("animation") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreFoundation.framework",
"CoreGraphics.framework",
diff --git a/chromium/ui/gfx/animation/animation_mac.mm b/chromium/ui/gfx/animation/animation_mac.mm
index 11db9646568..175ceff333e 100644
--- a/chromium/ui/gfx/animation/animation_mac.mm
+++ b/chromium/ui/gfx/animation/animation_mac.mm
@@ -7,7 +7,7 @@
#import <Cocoa/Cocoa.h>
#include "base/mac/mac_util.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
// Only available since 10.12.
@interface NSWorkspace (AvailableSinceSierra)
@@ -25,8 +25,7 @@ bool Animation::ShouldRenderRichAnimationImpl() {
bool Animation::ScrollAnimationsEnabledBySystem() {
// Because of sandboxing, OS settings should only be queried from the browser
// process.
- DCHECK(base::MessageLoopCurrentForUI::IsSet() ||
- base::MessageLoopCurrentForIO::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet() || base::CurrentIOThread::IsSet());
bool enabled = false;
id value = nil;
diff --git a/chromium/ui/gfx/animation/linear_animation.cc b/chromium/ui/gfx/animation/linear_animation.cc
index 77fa3f1f179..3d8d29ebee6 100644
--- a/chromium/ui/gfx/animation/linear_animation.cc
+++ b/chromium/ui/gfx/animation/linear_animation.cc
@@ -45,8 +45,7 @@ double LinearAnimation::GetCurrentValue() const {
void LinearAnimation::SetCurrentValue(double new_value) {
new_value = base::ClampToRange(new_value, 0.0, 1.0);
- base::TimeDelta time_delta = base::TimeDelta::FromMicroseconds(
- static_cast<int64_t>(duration_.InMicroseconds() * (new_value - state_)));
+ const base::TimeDelta time_delta = duration_ * (new_value - state_);
SetStartTime(start_time() - time_delta);
state_ = new_value;
}
@@ -72,19 +71,13 @@ void LinearAnimation::SetDuration(base::TimeDelta duration) {
? animation_duration_scale
: 1.0;
}();
- duration_ = duration * duration_scale_factor;
- if (duration_ < timer_interval())
- duration_ = timer_interval();
+ duration_ = std::max(duration * duration_scale_factor, timer_interval());
if (is_animating())
SetStartTime(container()->last_tick_time());
}
void LinearAnimation::Step(base::TimeTicks time_now) {
- base::TimeDelta elapsed_time = time_now - start_time();
- state_ = static_cast<double>(elapsed_time.InMicroseconds()) /
- static_cast<double>(duration_.InMicroseconds());
- if (state_ >= 1.0)
- state_ = 1.0;
+ state_ = std::min((time_now - start_time()) / duration_, 1.0);
AnimateToState(state_);
diff --git a/chromium/ui/gfx/animation/multi_animation.cc b/chromium/ui/gfx/animation/multi_animation.cc
index ec00449a724..de0be7d78a5 100644
--- a/chromium/ui/gfx/animation/multi_animation.cc
+++ b/chromium/ui/gfx/animation/multi_animation.cc
@@ -46,23 +46,26 @@ void MultiAnimation::Step(base::TimeTicks time_now) {
size_t last_index = current_part_index_;
base::TimeDelta delta = time_now - start_time();
- if (delta >= cycle_time_ && !continuous_) {
+ bool should_stop = delta >= cycle_time_ && !continuous_;
+ if (should_stop) {
current_part_index_ = parts_.size() - 1;
current_value_ = Tween::CalculateValue(parts_[current_part_index_].type, 1);
- Stop();
- return;
+ } else {
+ delta %= cycle_time_;
+ const Part& part = GetPart(&delta, &current_part_index_);
+ const double percent = (delta + part.part_start) / part.total_length;
+ DCHECK_LE(percent, 1);
+ current_value_ = Tween::CalculateValue(part.type, percent);
}
- delta %= cycle_time_;
- const Part& part = GetPart(&delta, &current_part_index_);
- double percent = (delta + part.part_start).InMillisecondsF() /
- part.total_length.InMillisecondsF();
- DCHECK_LE(percent, 1);
- current_value_ = Tween::CalculateValue(part.type, percent);
if ((current_value_ != last_value || current_part_index_ != last_index) &&
delegate()) {
+ // Run AnimationProgressed() even if the animation will be stopped, so that
+ // the animation runs its final frame.
delegate()->AnimationProgressed(this);
}
+ if (should_stop)
+ Stop();
}
void MultiAnimation::SetStartTime(base::TimeTicks start_time) {
diff --git a/chromium/ui/gfx/animation/multi_animation_unittest.cc b/chromium/ui/gfx/animation/multi_animation_unittest.cc
index 3d4e40d5633..ca833058a61 100644
--- a/chromium/ui/gfx/animation/multi_animation_unittest.cc
+++ b/chromium/ui/gfx/animation/multi_animation_unittest.cc
@@ -6,6 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/animation/animation_container_element.h"
+#include "ui/gfx/animation/animation_delegate.h"
namespace gfx {
@@ -79,6 +80,40 @@ TEST(MultiAnimationTest, DontCycle) {
EXPECT_FALSE(animation.is_animating());
}
+class CurrentValueDelegate : public AnimationDelegate {
+ public:
+ CurrentValueDelegate() = default;
+
+ double latest_current_value() { return latest_current_value_; }
+
+ // AnimationDelegate overrides:
+ void AnimationProgressed(const Animation* animation) override {
+ latest_current_value_ = animation->GetCurrentValue();
+ }
+
+ private:
+ double latest_current_value_ = 0.0;
+};
+
+// Makes sure multi-animation runs the final frame when exceeding the cycle time
+// and not running continuously.
+TEST(MultiAnimationTest, ExceedCycleNonContinuous) {
+ MultiAnimation::Parts parts;
+ parts.push_back(MultiAnimation::Part(base::TimeDelta::FromMilliseconds(200),
+ Tween::LINEAR));
+ MultiAnimation animation(parts, MultiAnimation::kDefaultTimerInterval);
+ CurrentValueDelegate delegate;
+ animation.set_delegate(&delegate);
+ animation.set_continuous(false);
+ AnimationContainerElement* as_element =
+ static_cast<AnimationContainerElement*>(&animation);
+ as_element->SetStartTime(base::TimeTicks());
+
+ // Step to 300, which is greater than the cycle time.
+ as_element->Step(base::TimeTicks() + base::TimeDelta::FromMilliseconds(300));
+ EXPECT_EQ(1.0, delegate.latest_current_value());
+}
+
// Makes sure multi-animation cycles correctly.
TEST(MultiAnimationTest, Cycle) {
MultiAnimation::Parts parts;
diff --git a/chromium/ui/gfx/animation/tween.cc b/chromium/ui/gfx/animation/tween.cc
index d53879fb14c..2ab0834f943 100644
--- a/chromium/ui/gfx/animation/tween.cc
+++ b/chromium/ui/gfx/animation/tween.cc
@@ -15,7 +15,6 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/cubic_bezier.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#if defined(OS_WIN)
#include <float.h>
@@ -81,7 +80,7 @@ double Tween::CalculateValue(Tween::Type type, double state) {
namespace {
uint8_t FloatToColorByte(float f) {
- return base::saturated_cast<uint8_t>(ToRoundedInt(f * 255.f));
+ return base::ClampRound<uint8_t>(f * 255.0f);
}
uint8_t BlendColorComponents(uint8_t start,
@@ -97,11 +96,6 @@ uint8_t BlendColorComponents(uint8_t start,
return FloatToColorByte(blended_premultiplied / blended_alpha);
}
-double TimeDeltaDivide(base::TimeDelta dividend, base::TimeDelta divisor) {
- return static_cast<double>(dividend.InMicroseconds()) /
- static_cast<double>(divisor.InMicroseconds());
-}
-
} // namespace
// static
@@ -148,8 +142,7 @@ float Tween::ClampedFloatValueBetween(const base::TimeTicks& time,
if (time >= target_time)
return target;
- double progress =
- TimeDeltaDivide(time - start_time, target_time - start_time);
+ const double progress = (time - start_time) / (target_time - start_time);
return FloatValueBetween(progress, start, target);
}
@@ -171,8 +164,8 @@ int Tween::IntValueBetween(double value, int start, int target) {
// static
int Tween::LinearIntValueBetween(double value, int start, int target) {
- // NOTE: Do not use ToRoundedInt()! See comments on function declaration.
- return ToFlooredInt(0.5 + DoubleValueBetween(value, start, target));
+ // NOTE: Do not use base::ClampRound()! See comments on function declaration.
+ return base::ClampFloor(0.5 + DoubleValueBetween(value, start, target));
}
// static
diff --git a/chromium/ui/gfx/ca_layer_params.h b/chromium/ui/gfx/ca_layer_params.h
index 4014e64a75d..64af450ed36 100644
--- a/chromium/ui/gfx/ca_layer_params.h
+++ b/chromium/ui/gfx/ca_layer_params.h
@@ -9,7 +9,7 @@
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gfx_export.h"
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
#include "ui/gfx/mac/io_surface.h"
#endif
@@ -37,7 +37,7 @@ struct GFX_EXPORT CALayerParams {
// Used to set the contents of a CALayer in the browser to an IOSurface that
// is specified by the GPU process. This is non-null iff |ca_context_id| is
// zero.
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
gfx::ScopedRefCountedIOSurfaceMachPort io_surface_mach_port;
#endif
diff --git a/chromium/ui/gfx/canvas.cc b/chromium/ui/gfx/canvas.cc
index 303aeca726f..5e60708161c 100644
--- a/chromium/ui/gfx/canvas.cc
+++ b/chromium/ui/gfx/canvas.cc
@@ -9,6 +9,7 @@
#include "base/i18n/rtl.h"
#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_shader.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -21,7 +22,6 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_paint_util.h"
@@ -73,8 +73,8 @@ void Canvas::SizeStringInt(const base::string16& text,
float factional_height = static_cast<float>(*height);
SizeStringFloat(text, font_list, &fractional_width, &factional_height,
line_height, flags);
- *width = ToCeiledInt(fractional_width);
- *height = ToCeiledInt(factional_height);
+ *width = base::ClampCeil(fractional_width);
+ *height = base::ClampCeil(factional_height);
}
// static
@@ -315,8 +315,8 @@ void Canvas::DrawImageInt(const ImageSkia& image,
ScopedCanvas scoper(this);
canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale),
SkFloatToScalar(1.0f / bitmap_scale));
- canvas_->translate(std::round(x * bitmap_scale),
- std::round(y * bitmap_scale));
+ canvas_->translate(SkFloatToScalar(std::round(x * bitmap_scale)),
+ SkFloatToScalar(std::round(y * bitmap_scale)));
canvas_->saveLayer(nullptr, &flags);
canvas_->drawPicture(image_rep.GetPaintRecord());
canvas_->restore();
diff --git a/chromium/ui/gfx/codec/BUILD.gn b/chromium/ui/gfx/codec/BUILD.gn
index 45fa24ee9db..8b002e0aa13 100644
--- a/chromium/ui/gfx/codec/BUILD.gn
+++ b/chromium/ui/gfx/codec/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
-jumbo_component("codec") {
+component("codec") {
sources = [
"codec_export.h",
"jpeg_codec.cc",
diff --git a/chromium/ui/gfx/color_space.cc b/chromium/ui/gfx/color_space.cc
index 1a4c33cd1b9..f6eb5f8fa25 100644
--- a/chromium/ui/gfx/color_space.cc
+++ b/chromium/ui/gfx/color_space.cc
@@ -40,6 +40,35 @@ static bool FloatsEqualWithinTolerance(const float* a,
return true;
}
+skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
+ // Note that SkColorSpace doesn't have the notion of an unspecified SDR white
+ // level.
+ if (sdr_white_level == 0.f)
+ sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
+
+ // The generic PQ transfer function produces normalized luminance values i.e.
+ // the range 0-1 represents 0-10000 nits for the reference display, but we
+ // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
+ const double w = 10000. / sdr_white_level;
+ // Distribute scaling factor W by scaling A and B with X ^ (1/F):
+ // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
+ // See https://crbug.com/1058580#c32 for discussion.
+ skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
+ const double ws = pow(w, 1. / fn.f);
+ fn.a = ws * fn.a;
+ fn.b = ws * fn.b;
+ return fn;
+}
+
+float GetSDRWhiteLevelFromPQSkTransferFunction(
+ const skcms_TransferFunction& fn) {
+ DCHECK_EQ(fn.g, SkNamedTransferFn::kPQ.g);
+ const double ws_a = static_cast<double>(fn.a) / SkNamedTransferFn::kPQ.a;
+ const double w_a = pow(ws_a, fn.f);
+ const double sdr_white_level_a = 10000.0f / w_a;
+ return sdr_white_level_a;
+}
+
} // namespace
// static
@@ -68,20 +97,15 @@ ColorSpace::ColorSpace(const SkColorSpace& sk_color_space)
TransferID::INVALID,
MatrixID::RGB,
RangeID::FULL) {
- // Special case the HDR transfer functions since they're not numerical
- auto transfer_eq = [](skcms_TransferFunction x, skcms_TransferFunction y) {
- return x.g == y.g && x.a == y.a && x.b == y.b && x.c == y.c && x.d == y.d &&
- x.e == y.e && x.f == y.f;
- };
-
skcms_TransferFunction fn;
if (sk_color_space.isNumericalTransferFn(&fn)) {
transfer_ = TransferID::CUSTOM;
SetCustomTransferFunction(fn);
- } else if (transfer_eq(fn, SkNamedTransferFn::kHLG)) {
+ } else if (skcms_TransferFunction_isHLGish(&fn)) {
transfer_ = TransferID::ARIB_STD_B67;
- } else if (transfer_eq(fn, SkNamedTransferFn::kPQ)) {
+ } else if (skcms_TransferFunction_isPQish(&fn)) {
transfer_ = TransferID::SMPTEST2084;
+ transfer_params_[0] = GetSDRWhiteLevelFromPQSkTransferFunction(fn);
} else {
// Construct an invalid result: Unable to extract necessary parameters
return;
@@ -101,27 +125,29 @@ bool ColorSpace::IsValid() const {
}
// static
-ColorSpace ColorSpace::CreateSCRGBLinear(float slope) {
- if (slope == 1.f) {
- return ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR, MatrixID::RGB,
- RangeID::FULL);
- }
+ColorSpace ColorSpace::CreateSCRGBLinear(float sdr_white_level) {
skcms_TransferFunction fn = {0};
- fn.g = 1.f;
- fn.a = slope;
+ fn.g = 1.0f;
+ fn.a = kDefaultScrgbLinearSdrWhiteLevel / sdr_white_level;
return ColorSpace(PrimaryID::BT709, TransferID::CUSTOM_HDR, MatrixID::RGB,
RangeID::FULL, nullptr, &fn);
}
// static
-ColorSpace ColorSpace::CreateHDR10(float sdr_white_point) {
+ColorSpace ColorSpace::CreateHDR10(float sdr_white_level) {
ColorSpace result(PrimaryID::BT2020, TransferID::SMPTEST2084, MatrixID::RGB,
RangeID::FULL);
- result.transfer_params_[0] = sdr_white_point;
+ result.transfer_params_[0] = sdr_white_level;
return result;
}
// static
+ColorSpace ColorSpace::CreateHLG() {
+ return ColorSpace(PrimaryID::BT2020, TransferID::ARIB_STD_B67, MatrixID::RGB,
+ RangeID::FULL);
+}
+
+// static
ColorSpace ColorSpace::CreatePiecewiseHDR(
PrimaryID primaries,
float sdr_joint,
@@ -446,6 +472,12 @@ std::string ColorSpace::ToString() const {
case TransferID::CUSTOM_HDR: {
skcms_TransferFunction fn;
GetTransferFunction(&fn);
+ if (fn.g == 1.0f && fn.a > 0.0f && fn.b == 0.0f && fn.c == 0.0f &&
+ fn.d == 0.0f && fn.e == 0.0f && fn.f == 0.0f) {
+ ss << "LINEAR_HDR (slope " << fn.a << ", SDR white point "
+ << kDefaultScrgbLinearSdrWhiteLevel / fn.a << " nits)";
+ break;
+ }
ss << fn.c << "*x + " << fn.f << " if |x| < " << fn.d << " else sign(x)*("
<< fn.a << "*|x| + " << fn.b << ")**" << fn.g << " + " << fn.e;
break;
@@ -523,16 +555,6 @@ ColorSpace ColorSpace::GetScaledColorSpace(float factor) const {
return result;
}
-ColorSpace ColorSpace::GetRasterColorSpace() const {
- // Rasterization doesn't support more than 8 bit unorm values. If the output
- // space has an extended range, use Display P3 for the rasterization space,
- // to get a somewhat wider color gamut.
- if (IsHDR())
- return CreateDisplayP3D65();
-
- return *this;
-}
-
bool ColorSpace::IsSuitableForBlending() const {
switch (transfer_) {
case TransferID::SMPTEST2084:
@@ -571,10 +593,17 @@ ColorSpace ColorSpace::GetWithMatrixAndRange(MatrixID matrix,
return result;
}
-ColorSpace ColorSpace::GetWithPQSDRWhiteLevel(float sdr_white_level) const {
+ColorSpace ColorSpace::GetWithSDRWhiteLevel(float sdr_white_level) const {
ColorSpace result = *this;
- if (transfer_ == TransferID::SMPTEST2084 && transfer_params_[0] == 0.f)
+ if (transfer_ == TransferID::SMPTEST2084) {
result.transfer_params_[0] = sdr_white_level;
+ } else if (transfer_ == TransferID::LINEAR_HDR) {
+ result.transfer_ = TransferID::CUSTOM_HDR;
+ skcms_TransferFunction fn = {0};
+ fn.g = 1.f;
+ fn.a = kDefaultScrgbLinearSdrWhiteLevel / sdr_white_level;
+ result.SetCustomTransferFunction(fn);
+ }
return result;
}
@@ -613,7 +642,7 @@ sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const {
transfer_fn = SkNamedTransferFn::kHLG;
break;
case TransferID::SMPTEST2084:
- GetPQTransferFunction(&transfer_fn);
+ transfer_fn = GetPQSkTransferFunction(transfer_params_[0]);
break;
default:
if (!GetTransferFunction(&transfer_fn)) {
@@ -945,23 +974,6 @@ bool ColorSpace::GetTransferFunction(skcms_TransferFunction* fn) const {
}
}
-void ColorSpace::GetPQTransferFunction(skcms_TransferFunction* fn) const {
- DCHECK_EQ(transfer_, TransferID::SMPTEST2084);
- const float sdr_white_level =
- transfer_params_[0] == 0.0f ? kDefaultSDRWhiteLevel : transfer_params_[0];
- // The generic PQ transfer function produces normalized luminance values i.e.
- // the range 0-1 represents 0-10000 nits for the reference display, but we
- // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
- const float w = 10000.0f / sdr_white_level;
- // Distribute scaling factor W by scaling A and B with X ^ (1/F):
- // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
- // See https://crbug.com/1058580#c32 for discussion.
- *fn = SkNamedTransferFn::kPQ;
- const float ws = powf(w, 1 / fn->f);
- fn->a = ws * fn->a;
- fn->b = ws * fn->b;
-}
-
bool ColorSpace::GetInverseTransferFunction(skcms_TransferFunction* fn) const {
if (!GetTransferFunction(fn))
return false;
@@ -1127,26 +1139,36 @@ void ColorSpace::GetRangeAdjustMatrix(int bit_depth, SkMatrix44* matrix) const {
}
}
-bool ColorSpace::ToSkYUVColorSpace(SkYUVColorSpace* out) const {
- if (range_ == RangeID::FULL) {
- if (matrix_ == MatrixID::BT470BG || matrix_ == MatrixID::SMPTE170M) {
- *out = kJPEG_SkYUVColorSpace;
- return true;
- }
- }
+bool ColorSpace::ToSkYUVColorSpace(int bit_depth, SkYUVColorSpace* out) const {
switch (matrix_) {
case MatrixID::BT709:
- *out = kRec709_SkYUVColorSpace;
+ *out = range_ == RangeID::FULL ? kRec709_Full_SkYUVColorSpace
+ : kRec709_Limited_SkYUVColorSpace;
return true;
case MatrixID::BT470BG:
case MatrixID::SMPTE170M:
- *out = kRec601_SkYUVColorSpace;
+ *out = range_ == RangeID::FULL ? kJPEG_SkYUVColorSpace
+ : kRec601_Limited_SkYUVColorSpace;
return true;
case MatrixID::BT2020_NCL:
- *out = kBT2020_SkYUVColorSpace;
- return true;
+ if (bit_depth == 8) {
+ *out = range_ == RangeID::FULL ? kBT2020_8bit_Full_SkYUVColorSpace
+ : kBT2020_8bit_Limited_SkYUVColorSpace;
+ return true;
+ }
+ if (bit_depth == 10) {
+ *out = range_ == RangeID::FULL ? kBT2020_10bit_Full_SkYUVColorSpace
+ : kBT2020_10bit_Limited_SkYUVColorSpace;
+ return true;
+ }
+ if (bit_depth == 12) {
+ *out = range_ == RangeID::FULL ? kBT2020_12bit_Full_SkYUVColorSpace
+ : kBT2020_12bit_Limited_SkYUVColorSpace;
+ return true;
+ }
+ return false;
default:
break;
diff --git a/chromium/ui/gfx/color_space.h b/chromium/ui/gfx/color_space.h
index 425f88769c2..08cf2adcee7 100644
--- a/chromium/ui/gfx/color_space.h
+++ b/chromium/ui/gfx/color_space.h
@@ -185,12 +185,24 @@ class COLOR_SPACE_EXPORT ColorSpace {
}
// scRGB uses the same primaries as sRGB but has a linear transfer function
- // for all real values. The slope of the transfer function may be specified
- // by |slope|.
- static ColorSpace CreateSCRGBLinear(float slope = 1.f);
+ // for all real values, and a white point of kDefaultScrgbLinearSdrWhiteLevel.
+ static constexpr ColorSpace CreateSCRGBLinear() {
+ return ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR, MatrixID::RGB,
+ RangeID::FULL);
+ }
+ // Allows specifying a custom SDR white level. Only used on Windows.
+ static ColorSpace CreateSCRGBLinear(float sdr_white_level);
// HDR10 uses BT.2020 primaries with SMPTE ST 2084 PQ transfer function.
- static ColorSpace CreateHDR10(float sdr_white_point = 0.f);
+ static constexpr ColorSpace CreateHDR10() {
+ return ColorSpace(PrimaryID::BT2020, TransferID::SMPTEST2084, MatrixID::RGB,
+ RangeID::FULL);
+ }
+ // Allows specifying a custom SDR white level. Only used on Windows.
+ static ColorSpace CreateHDR10(float sdr_white_level);
+
+ // HLG uses the BT.2020 primaries with the ARIB_STD_B67 transfer function.
+ static ColorSpace CreateHLG();
// Create a piecewise-HDR color space.
// - If |primaries| is CUSTOM, then |custom_primary_matrix| must be
@@ -224,10 +236,22 @@ class COLOR_SPACE_EXPORT ColorSpace {
}
// On macOS and on ChromeOS, sRGB's (1,1,1) always coincides with PQ's 100
- // nits (which may not be 100 physical nits). Life is more complicated on
- // Windows.
+ // nits (which may not be 100 physical nits). On Windows, sRGB's (1,1,1)
+ // maps to scRGB linear's (1,1,1) when the SDR white level is set to 80 nits.
+ // See also kDefaultScrgbLinearSdrWhiteLevel.
static constexpr float kDefaultSDRWhiteLevel = 100.f;
+ // The default white level in nits for scRGB linear color space. On Windows,
+ // sRGB's (1,1,1) maps to scRGB linear's (1,1,1) when the SDR white level is
+ // set to 80 nits. On Mac and ChromeOS, sRGB's (1,1,1) maps to PQ's 100 nits.
+ // Using a platform specific value here satisfies both constraints.
+#if defined(OS_WIN)
+ static constexpr float kDefaultScrgbLinearSdrWhiteLevel = 80.0f;
+#else
+ static constexpr float kDefaultScrgbLinearSdrWhiteLevel =
+ kDefaultSDRWhiteLevel;
+#endif // OS_WIN
+
bool operator==(const ColorSpace& other) const;
bool operator!=(const ColorSpace& other) const;
bool operator<(const ColorSpace& other) const;
@@ -257,10 +281,6 @@ class COLOR_SPACE_EXPORT ColorSpace {
// everything will be half as bright in linear lumens.
ColorSpace GetScaledColorSpace(float factor) const;
- // If |this| is the final output color space, return the color space that
- // would be appropriate for rasterization.
- ColorSpace GetRasterColorSpace() const;
-
// Return true if blending in |this| is close enough to blending in sRGB to
// be considered acceptable (only PQ and nearly-linear transfer functions
// return false).
@@ -270,10 +290,10 @@ class COLOR_SPACE_EXPORT ColorSpace {
// the caller but replacing the matrix and range with the given values.
ColorSpace GetWithMatrixAndRange(MatrixID matrix, RangeID range) const;
- // If this color space has a PQ transfer function that did not specify an
- // SDR white level, then return |this| with its SDR white level set to
- // |sdr_white_level|. Otherwise return |this| unmodified.
- ColorSpace GetWithPQSDRWhiteLevel(float sdr_white_level) const;
+ // If this color space has a PQ or scRGB linear transfer function, then return
+ // |this| with its SDR white level set to |sdr_white_level|. Otherwise return
+ // |this| unmodified.
+ ColorSpace GetWithSDRWhiteLevel(float sdr_white_level) const;
// This will return nullptr for non-RGB spaces, spaces with non-FULL
// range, and unspecified spaces.
@@ -284,10 +304,14 @@ class COLOR_SPACE_EXPORT ColorSpace {
// buffer.
const _GLcolorSpace* AsGLColorSpace() const;
- // For YUV color spaces, return the closest SkYUVColorSpace.
- // Returns true if a close match is found. Otherwise, leaves *out unchanged
- // and returns false.
- bool ToSkYUVColorSpace(SkYUVColorSpace* out) const;
+ // For YUV color spaces, return the closest SkYUVColorSpace. Returns true if a
+ // close match is found. Otherwise, leaves *out unchanged and returns false.
+ // If |matrix_id| is MatrixID::BT2020_NCL and |bit_depth| is provided, a bit
+ // depth appropriate SkYUVColorSpace will be provided.
+ bool ToSkYUVColorSpace(int bit_depth, SkYUVColorSpace* out) const;
+ bool ToSkYUVColorSpace(SkYUVColorSpace* out) const {
+ return ToSkYUVColorSpace(kDefaultBitDepth, out);
+ }
void GetPrimaryMatrix(skcms_Matrix3x3* to_XYZD50) const;
void GetPrimaryMatrix(SkMatrix44* to_XYZD50) const;
@@ -362,8 +386,6 @@ class COLOR_SPACE_EXPORT ColorSpace {
static bool GetTransferFunction(TransferID, skcms_TransferFunction* fn);
static size_t TransferParamCount(TransferID);
- void GetPQTransferFunction(skcms_TransferFunction* fn) const;
-
void SetCustomTransferFunction(const skcms_TransferFunction& fn);
void SetCustomPrimaries(const skcms_Matrix3x3& to_XYZD50);
diff --git a/chromium/ui/gfx/color_space_unittest.cc b/chromium/ui/gfx/color_space_unittest.cc
index b790f8f834d..57a995c4bf4 100644
--- a/chromium/ui/gfx/color_space_unittest.cc
+++ b/chromium/ui/gfx/color_space_unittest.cc
@@ -179,20 +179,17 @@ TEST(ColorSpace, RangeAdjust) {
}
}
-TEST(ColorSpace, RasterAndBlend) {
+TEST(ColorSpace, Blending) {
ColorSpace display_color_space;
// A linear transfer function being used for HDR should be blended using an
// sRGB-like transfer function.
display_color_space = ColorSpace::CreateSCRGBLinear();
EXPECT_FALSE(display_color_space.IsSuitableForBlending());
- EXPECT_EQ(ColorSpace::CreateDisplayP3D65(),
- display_color_space.GetRasterColorSpace());
// If not used for HDR, a linear transfer function should be left unchanged.
display_color_space = ColorSpace::CreateXYZD50();
EXPECT_TRUE(display_color_space.IsSuitableForBlending());
- EXPECT_EQ(display_color_space, display_color_space.GetRasterColorSpace());
}
TEST(ColorSpace, ConversionToAndFromSkColorSpace) {
@@ -244,6 +241,34 @@ TEST(ColorSpace, ConversionToAndFromSkColorSpace) {
}
}
+TEST(ColorSpace, PQToSkColorSpace) {
+ ColorSpace color_space;
+ ColorSpace roundtrip_color_space;
+ float roundtrip_sdr_white_level;
+ const float kEpsilon = 1.e-5f;
+
+ // We expect that when a white point is specified, the conversion from
+ // ColorSpace -> SkColorSpace -> ColorSpace be the identity. Because of
+ // rounding error, this will not quite be the case.
+ color_space = ColorSpace::CreateHDR10(50.f);
+ roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
+ EXPECT_TRUE(
+ roundtrip_color_space.GetPQSDRWhiteLevel(&roundtrip_sdr_white_level));
+ EXPECT_NEAR(50.f, roundtrip_sdr_white_level, kEpsilon);
+ EXPECT_EQ(ColorSpace::TransferID::SMPTEST2084,
+ roundtrip_color_space.GetTransferID());
+
+ // When no white level is specified, we should get an SkColorSpace that
+ // specifies the default white level. Of note is that in the roundtrip, the
+ // value of kDefaultSDRWhiteLevel gets baked in.
+ color_space = ColorSpace::CreateHDR10();
+ roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
+ EXPECT_TRUE(
+ roundtrip_color_space.GetPQSDRWhiteLevel(&roundtrip_sdr_white_level));
+ EXPECT_NEAR(ColorSpace::kDefaultSDRWhiteLevel, roundtrip_sdr_white_level,
+ kEpsilon);
+}
+
TEST(ColorSpace, MixedInvalid) {
ColorSpace color_space;
color_space = color_space.GetWithMatrixAndRange(ColorSpace::MatrixID::INVALID,
@@ -286,5 +311,56 @@ TEST(ColorSpace, GetsPrimariesTransferMatrixAndRange) {
EXPECT_EQ(color_space.GetRangeID(), ColorSpace::RangeID::LIMITED);
}
+TEST(ColorSpace, PQWhiteLevel) {
+ constexpr float kCustomWhiteLevel = 200.f;
+
+ ColorSpace color_space = ColorSpace::CreateHDR10(kCustomWhiteLevel);
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
+ float sdr_white_level;
+ EXPECT_TRUE(color_space.GetPQSDRWhiteLevel(&sdr_white_level));
+ EXPECT_EQ(sdr_white_level, kCustomWhiteLevel);
+
+ color_space = ColorSpace::CreateHDR10();
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
+ EXPECT_TRUE(color_space.GetPQSDRWhiteLevel(&sdr_white_level));
+ EXPECT_EQ(sdr_white_level, ColorSpace::kDefaultSDRWhiteLevel);
+
+ color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel);
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
+ EXPECT_TRUE(color_space.GetPQSDRWhiteLevel(&sdr_white_level));
+ EXPECT_EQ(sdr_white_level, kCustomWhiteLevel);
+
+ constexpr float kCustomWhiteLevel2 = kCustomWhiteLevel * 2;
+ color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel2);
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
+ EXPECT_TRUE(color_space.GetPQSDRWhiteLevel(&sdr_white_level));
+ EXPECT_EQ(sdr_white_level, kCustomWhiteLevel2);
+}
+
+TEST(ColorSpace, LinearHDRWhiteLevel) {
+ constexpr float kCustomWhiteLevel = 200.f;
+ constexpr float kCustomSlope =
+ ColorSpace::kDefaultScrgbLinearSdrWhiteLevel / kCustomWhiteLevel;
+
+ ColorSpace color_space = ColorSpace::CreateSCRGBLinear(kCustomWhiteLevel);
+ skcms_TransferFunction fn;
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR);
+ EXPECT_TRUE(color_space.GetTransferFunction(&fn));
+ EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
+ std::make_tuple(1.f, kCustomSlope, 0.f, 0.f, 0.f, 0.f, 0.f));
+
+ color_space = ColorSpace::CreateSCRGBLinear();
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::LINEAR_HDR);
+ EXPECT_TRUE(color_space.GetTransferFunction(&fn));
+ EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
+ std::make_tuple(1.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f));
+
+ color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel);
+ EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR);
+ EXPECT_TRUE(color_space.GetTransferFunction(&fn));
+ EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
+ std::make_tuple(1.f, kCustomSlope, 0.f, 0.f, 0.f, 0.f, 0.f));
+}
+
} // namespace
} // namespace gfx
diff --git a/chromium/ui/gfx/color_utils.cc b/chromium/ui/gfx/color_utils.cc
index 92752de2378..c4e41658b4e 100644
--- a/chromium/ui/gfx/color_utils.cc
+++ b/chromium/ui/gfx/color_utils.cc
@@ -17,7 +17,6 @@
#include "build/build_config.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/color_palette.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#if defined(OS_WIN)
#include <windows.h>
@@ -52,7 +51,7 @@ int calcHue(float temp1, float temp2, float hue) {
else if (hue * 3.0f < 2.0f)
result = temp1 + (temp2 - temp1) * (2.0f / 3.0f - hue) * 6.0f;
- return static_cast<int>(std::round(result * 255));
+ return base::ClampRound(result * 255);
}
// Assumes sRGB.
@@ -88,9 +87,9 @@ float GetRelativeLuminance(SkColor color) {
}
uint8_t GetLuma(SkColor color) {
- return static_cast<uint8_t>(std::round((0.299f * SkColorGetR(color)) +
- (0.587f * SkColorGetG(color)) +
- (0.114f * SkColorGetB(color))));
+ return base::ClampRound<uint8_t>(0.299f * SkColorGetR(color) +
+ 0.587f * SkColorGetG(color) +
+ 0.114f * SkColorGetB(color));
}
void SkColorToHSL(SkColor c, HSL* hsl) {
@@ -133,8 +132,7 @@ SkColor HSLToSkColor(const HSL& hsl, SkAlpha alpha) {
// If there's no color, we don't care about hue and can do everything based on
// brightness.
if (!saturation) {
- const uint8_t light =
- base::saturated_cast<uint8_t>(gfx::ToRoundedInt(lightness * 255));
+ const uint8_t light = base::ClampRound<uint8_t>(lightness * 255);
return SkColorSetARGB(alpha, light, light, light);
}
@@ -232,10 +230,8 @@ SkColor HSLShift(SkColor color, const HSL& shift) {
g += (255.0f - g) * ((shift.l - 0.5f) * 2.0f);
b += (255.0f - b) * ((shift.l - 0.5f) * 2.0f);
}
- return SkColorSetARGB(alpha,
- static_cast<int>(std::round(r)),
- static_cast<int>(std::round(g)),
- static_cast<int>(std::round(b)));
+ return SkColorSetARGB(alpha, base::ClampRound<U8CPU>(r),
+ base::ClampRound<U8CPU>(g), base::ClampRound<U8CPU>(b));
}
void BuildLumaHistogram(const SkBitmap& bitmap, int histogram[256]) {
@@ -290,8 +286,9 @@ SkColor AlphaBlend(SkColor foreground, SkColor background, float alpha) {
float b =
SkColorGetB(foreground) * f_weight + SkColorGetB(background) * b_weight;
- return SkColorSetARGB(gfx::ToRoundedInt(normalizer), gfx::ToRoundedInt(r),
- gfx::ToRoundedInt(g), gfx::ToRoundedInt(b));
+ return SkColorSetARGB(base::ClampRound<U8CPU>(normalizer),
+ base::ClampRound<U8CPU>(r), base::ClampRound<U8CPU>(g),
+ base::ClampRound<U8CPU>(b));
}
SkColor GetResultingPaintColor(SkColor foreground, SkColor background) {
diff --git a/chromium/ui/gfx/display_color_spaces.cc b/chromium/ui/gfx/display_color_spaces.cc
index 0723c93b5f7..08c30ede734 100644
--- a/chromium/ui/gfx/display_color_spaces.cc
+++ b/chromium/ui/gfx/display_color_spaces.cc
@@ -95,8 +95,7 @@ BufferFormat DisplayColorSpaces::GetOutputBufferFormat(
}
gfx::ColorSpace DisplayColorSpaces::GetRasterColorSpace() const {
- return GetOutputColorSpace(ContentColorUsage::kHDR, false /* needs_alpha */)
- .GetRasterColorSpace();
+ return GetOutputColorSpace(ContentColorUsage::kHDR, false /* needs_alpha */);
}
gfx::ColorSpace DisplayColorSpaces::GetCompositingColorSpace(
@@ -113,6 +112,10 @@ bool DisplayColorSpaces::SupportsHDR() const {
GetOutputColorSpace(ContentColorUsage::kHDR, true).IsHDR();
}
+ColorSpace DisplayColorSpaces::GetScreenInfoColorSpace() const {
+ return GetOutputColorSpace(ContentColorUsage::kHDR, false /* needs_alpha */);
+}
+
void DisplayColorSpaces::ToStrings(
std::vector<std::string>* out_names,
std::vector<gfx::ColorSpace>* out_color_spaces,
diff --git a/chromium/ui/gfx/display_color_spaces.h b/chromium/ui/gfx/display_color_spaces.h
index b611b2be994..f2711c22957 100644
--- a/chromium/ui/gfx/display_color_spaces.h
+++ b/chromium/ui/gfx/display_color_spaces.h
@@ -74,6 +74,11 @@ class COLOR_SPACE_EXPORT DisplayColorSpaces {
}
float GetSDRWhiteLevel() const { return sdr_white_level_; }
+ // TODO(https://crbug.com/1116870): These helper functions exist temporarily
+ // to handle the transition of blink::ScreenInfo off of ColorSpace. All calls
+ // to these functions are to be eliminated.
+ ColorSpace GetScreenInfoColorSpace() const;
+
// Return the color space that should be used for rasterization.
// TODO: This will eventually need to take a ContentColorUsage.
gfx::ColorSpace GetRasterColorSpace() const;
@@ -99,7 +104,7 @@ class COLOR_SPACE_EXPORT DisplayColorSpaces {
private:
// Serialization of DisplayColorSpaces directly accesses members.
-
+ friend struct IPC::ParamTraits<gfx::DisplayColorSpaces>;
friend struct mojo::StructTraits<gfx::mojom::DisplayColorSpacesDataView,
gfx::DisplayColorSpaces>;
diff --git a/chromium/ui/gfx/font.cc b/chromium/ui/gfx/font.cc
index 92b159e13d1..50a071a20fa 100644
--- a/chromium/ui/gfx/font.cc
+++ b/chromium/ui/gfx/font.cc
@@ -26,7 +26,7 @@ Font& Font::operator=(const Font& other) {
return *this;
}
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
Font::Font(NativeFont native_font)
: platform_font_(PlatformFont::CreateFromNativeFont(native_font)) {
}
@@ -90,7 +90,7 @@ const FontRenderParams& Font::GetFontRenderParams() const {
return platform_font_->GetFontRenderParams();
}
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
NativeFont Font::GetNativeFont() const {
return platform_font_->GetNativeFont();
}
diff --git a/chromium/ui/gfx/font.h b/chromium/ui/gfx/font.h
index 3da61df9dd6..f8aa02af51c 100644
--- a/chromium/ui/gfx/font.h
+++ b/chromium/ui/gfx/font.h
@@ -60,7 +60,7 @@ class GFX_EXPORT Font {
Font(const Font& other);
Font& operator=(const Font& other);
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
// Creates a font from the specified native font.
explicit Font(NativeFont native_font);
#endif
@@ -117,7 +117,7 @@ class GFX_EXPORT Font {
// Returns an object describing how the font should be rendered.
const FontRenderParams& GetFontRenderParams() const;
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
// Returns the native font handle.
// Lifetime lore:
// Mac: The object is owned by the system and should not be released.
diff --git a/chromium/ui/gfx/font_fallback_win.cc b/chromium/ui/gfx/font_fallback_win.cc
index 3b5987468ae..9b00c71251d 100644
--- a/chromium/ui/gfx/font_fallback_win.cc
+++ b/chromium/ui/gfx/font_fallback_win.cc
@@ -9,12 +9,12 @@
#include "base/macros.h"
#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "base/win/registry.h"
#include "ui/gfx/font.h"
@@ -238,7 +238,7 @@ bool GetFallbackFont(const Font& font,
// browser process because we can use the shared system fallback, but in the
// renderer this can cause hangs. Code that needs font fallback in the
// renderer should instead use the font proxy.
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
// The text passed must be at least length 1.
if (text.empty())
diff --git a/chromium/ui/gfx/font_list.cc b/chromium/ui/gfx/font_list.cc
index 5cb2b220692..502b80872ab 100644
--- a/chromium/ui/gfx/font_list.cc
+++ b/chromium/ui/gfx/font_list.cc
@@ -24,7 +24,7 @@ base::LazyInstance<scoped_refptr<gfx::FontListImpl>>::Leaky g_default_impl =
bool g_default_impl_initialized = false;
bool IsFontFamilyAvailable(const std::string& family, SkFontMgr* fontManager) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
return !!fontManager->legacyMakeTypeface(family.c_str(), SkFontStyle());
#else
sk_sp<SkFontStyleSet> set(fontManager->matchFamily(family.c_str()));
diff --git a/chromium/ui/gfx/font_names_testing.cc b/chromium/ui/gfx/font_names_testing.cc
index fda2dfe0263..86e4abb739a 100644
--- a/chromium/ui/gfx/font_names_testing.cc
+++ b/chromium/ui/gfx/font_names_testing.cc
@@ -22,7 +22,7 @@ Note that we have to support the full range from JellyBean to the latest
dessert.
*/
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
const char kTestFontName[] = "Arimo";
#elif defined(OS_ANDROID)
const char kTestFontName[] = "sans-serif";
@@ -30,7 +30,7 @@ const char kTestFontName[] = "sans-serif";
const char kTestFontName[] = "Arial";
#endif
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
const char kSymbolFontName[] = "DejaVu Sans";
#elif defined(OS_ANDROID)
const char kSymbolFontName[] = "monospace";
@@ -40,11 +40,11 @@ const char kSymbolFontName[] = "Segoe UI Symbol";
const char kSymbolFontName[] = "Symbol";
#endif
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
const char kCJKFontName[] = "Noto Sans CJK JP";
#elif defined(OS_ANDROID)
const char kCJKFontName[] = "serif";
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
const char kCJKFontName[] = "Heiti SC";
#else
const char kCJKFontName[] = "SimSun";
diff --git a/chromium/ui/gfx/font_render_params.h b/chromium/ui/gfx/font_render_params.h
index 6564b08a70a..504331333b4 100644
--- a/chromium/ui/gfx/font_render_params.h
+++ b/chromium/ui/gfx/font_render_params.h
@@ -111,7 +111,7 @@ GFX_EXPORT FontRenderParams GetFontRenderParams(
const FontRenderParamsQuery& query,
std::string* family_out);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Clears GetFontRenderParams()'s cache. Intended to be called by tests that are
// changing Fontconfig's configuration.
GFX_EXPORT void ClearFontRenderParamsCacheForTest();
@@ -120,8 +120,8 @@ GFX_EXPORT void ClearFontRenderParamsCacheForTest();
// Gets the device scale factor to query the FontRenderParams.
GFX_EXPORT float GetFontRenderParamsDeviceScaleFactor();
-#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID) || \
- defined(OS_FUCHSIA)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
+ defined(OS_ANDROID) || defined(OS_FUCHSIA)
// Sets the device scale factor for FontRenderParams to decide
// if it should enable subpixel positioning.
GFX_EXPORT void SetFontRenderParamsDeviceScaleFactor(
diff --git a/chromium/ui/gfx/font_unittest.cc b/chromium/ui/gfx/font_unittest.cc
index 399fea2e4c7..480a049f062 100644
--- a/chromium/ui/gfx/font_unittest.cc
+++ b/chromium/ui/gfx/font_unittest.cc
@@ -53,7 +53,7 @@ TEST_F(FontTest, DefaultFont) {
TEST_F(FontTest, LoadArial) {
Font cf(kTestFontName, 16);
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
EXPECT_TRUE(cf.GetNativeFont());
#endif
EXPECT_EQ(cf.GetStyle(), Font::NORMAL);
@@ -66,7 +66,7 @@ TEST_F(FontTest, LoadArial) {
TEST_F(FontTest, LoadArialBold) {
Font cf(kTestFontName, 16);
Font bold(cf.Derive(0, Font::NORMAL, Font::Weight::BOLD));
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
EXPECT_TRUE(bold.GetNativeFont());
#endif
EXPECT_EQ(bold.GetStyle(), Font::NORMAL);
diff --git a/chromium/ui/gfx/font_util.cc b/chromium/ui/gfx/font_util.cc
index 6b56f5e7c01..faf6dd7b2d6 100644
--- a/chromium/ui/gfx/font_util.cc
+++ b/chromium/ui/gfx/font_util.cc
@@ -6,7 +6,7 @@
#include "build/build_config.h"
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
#include <fontconfig/fontconfig.h>
#include "ui/gfx/linux/fontconfig_util.h"
#endif
@@ -24,11 +24,11 @@ void InitializeFonts() {
// background (resources have not yet been granted to cast) since it prevents
// the long delay the user would have seen on first rendering.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Ensures the config is created on this thread.
FcConfig* config = GetGlobalFontConfig();
DCHECK(config);
-#endif // OS_LINUX
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
#if defined(OS_WIN)
gfx::win::InitializeDirectWrite();
diff --git a/chromium/ui/gfx/geometry/BUILD.gn b/chromium/ui/gfx/geometry/BUILD.gn
index 2efaeb6fd8a..563eb24b40d 100644
--- a/chromium/ui/gfx/geometry/BUILD.gn
+++ b/chromium/ui/gfx/geometry/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("geometry") {
+component("geometry") {
sources = [
"../gfx_export.h",
"angle_conversions.h",
@@ -43,7 +41,6 @@ jumbo_component("geometry") {
"rect_f.h",
"rounded_corners_f.cc",
"rounded_corners_f.h",
- "safe_integer_conversions.h",
"scroll_offset.cc",
"scroll_offset.h",
"size.cc",
diff --git a/chromium/ui/gfx/geometry/point.cc b/chromium/ui/gfx/geometry/point.cc
index 883cb4bd2c6..0307b2fa885 100644
--- a/chromium/ui/gfx/geometry/point.cc
+++ b/chromium/ui/gfx/geometry/point.cc
@@ -13,7 +13,7 @@
#include <windows.h>
#elif defined(OS_IOS)
#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <ApplicationServices/ApplicationServices.h>
#endif
@@ -34,7 +34,7 @@ Point& Point::operator=(const POINT& point) {
y_ = point.y;
return *this;
}
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
Point::Point(const CGPoint& point) : x_(point.x), y_(point.y) {
}
#endif
@@ -46,7 +46,7 @@ POINT Point::ToPOINT() const {
p.y = y();
return p;
}
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
CGPoint Point::ToCGPoint() const {
return CGPointMake(x(), y());
}
diff --git a/chromium/ui/gfx/geometry/point.h b/chromium/ui/gfx/geometry/point.h
index c7a1649665f..71c5a5b949b 100644
--- a/chromium/ui/gfx/geometry/point.h
+++ b/chromium/ui/gfx/geometry/point.h
@@ -17,7 +17,7 @@
#if defined(OS_WIN)
typedef unsigned long DWORD;
typedef struct tagPOINT POINT;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
typedef struct CGPoint CGPoint;
#endif
@@ -35,13 +35,13 @@ class GEOMETRY_EXPORT Point {
explicit Point(DWORD point);
explicit Point(const POINT& point);
Point& operator=(const POINT& point);
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
explicit Point(const CGPoint& point);
#endif
#if defined(OS_WIN)
POINT ToPOINT() const;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
CGPoint ToCGPoint() const;
#endif
diff --git a/chromium/ui/gfx/geometry/point_conversions.cc b/chromium/ui/gfx/geometry/point_conversions.cc
index 0613e7a9db1..bf800da0289 100644
--- a/chromium/ui/gfx/geometry/point_conversions.cc
+++ b/chromium/ui/gfx/geometry/point_conversions.cc
@@ -4,26 +4,20 @@
#include "ui/gfx/geometry/point_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "base/numerics/safe_conversions.h"
namespace gfx {
Point ToFlooredPoint(const PointF& point) {
- int x = ToFlooredInt(point.x());
- int y = ToFlooredInt(point.y());
- return Point(x, y);
+ return Point(base::ClampFloor(point.x()), base::ClampFloor(point.y()));
}
Point ToCeiledPoint(const PointF& point) {
- int x = ToCeiledInt(point.x());
- int y = ToCeiledInt(point.y());
- return Point(x, y);
+ return Point(base::ClampCeil(point.x()), base::ClampCeil(point.y()));
}
Point ToRoundedPoint(const PointF& point) {
- int x = ToRoundedInt(point.x());
- int y = ToRoundedInt(point.y());
- return Point(x, y);
+ return Point(base::ClampRound(point.x()), base::ClampRound(point.y()));
}
} // namespace gfx
diff --git a/chromium/ui/gfx/geometry/rect.cc b/chromium/ui/gfx/geometry/rect.cc
index e58746c83e9..370fe34565e 100644
--- a/chromium/ui/gfx/geometry/rect.cc
+++ b/chromium/ui/gfx/geometry/rect.cc
@@ -10,7 +10,7 @@
#include <windows.h>
#elif defined(OS_IOS)
#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <ApplicationServices/ApplicationServices.h>
#endif
@@ -27,7 +27,7 @@ Rect::Rect(const RECT& r)
: origin_(r.left, r.top),
size_(std::abs(r.right - r.left), std::abs(r.bottom - r.top)) {
}
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
Rect::Rect(const CGRect& r)
: origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {
}
@@ -42,7 +42,7 @@ RECT Rect::ToRECT() const {
r.bottom = bottom();
return r;
}
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
CGRect Rect::ToCGRect() const {
return CGRectMake(x(), y(), width(), height());
}
diff --git a/chromium/ui/gfx/geometry/rect.h b/chromium/ui/gfx/geometry/rect.h
index 3ec89df0d47..13aa771ffe2 100644
--- a/chromium/ui/gfx/geometry/rect.h
+++ b/chromium/ui/gfx/geometry/rect.h
@@ -17,15 +17,15 @@
#include <string>
#include "base/check.h"
+#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#if defined(OS_WIN)
typedef struct tagRECT RECT;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
typedef struct CGRect CGRect;
#endif
@@ -48,14 +48,14 @@ class GEOMETRY_EXPORT Rect {
#if defined(OS_WIN)
explicit Rect(const RECT& r);
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
explicit Rect(const CGRect& r);
#endif
#if defined(OS_WIN)
// Construct an equivalent Win32 RECT object.
RECT ToRECT() const;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
// Construct an equivalent CoreGraphics object.
CGRect ToCGRect() const;
#endif
@@ -285,8 +285,8 @@ inline Rect ScaleToEnclosingRect(const Rect& rect,
float y_scale) {
if (x_scale == 1.f && y_scale == 1.f)
return rect;
- // These next functions cast instead of using e.g. ToFlooredInt() because we
- // haven't checked to ensure that the clamping behavior of the helper
+ // These next functions cast instead of using e.g. base::ClampFloor() because
+ // we haven't checked to ensure that the clamping behavior of the helper
// functions doesn't degrade performance, and callers shouldn't be passing
// values that cause overflow anyway.
DCHECK(base::IsValueInRangeForNumericType<int>(
@@ -312,15 +312,17 @@ inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
// ScaleToEnclosingRect but clamping instead of asserting if the resulting rect
// would overflow.
+// TODO(pkasting): Attempt to switch ScaleTo...Rect() to this construction and
+// check performance.
inline Rect ScaleToEnclosingRectSafe(const Rect& rect,
float x_scale,
float y_scale) {
if (x_scale == 1.f && y_scale == 1.f)
return rect;
- int x = base::saturated_cast<int>(std::floor(rect.x() * x_scale));
- int y = base::saturated_cast<int>(std::floor(rect.y() * y_scale));
- int w = base::saturated_cast<int>(std::ceil(rect.width() * x_scale));
- int h = base::saturated_cast<int>(std::ceil(rect.height() * y_scale));
+ int x = base::ClampFloor(rect.x() * x_scale);
+ int y = base::ClampFloor(rect.y() * y_scale);
+ int w = base::ClampCeil(rect.width() * x_scale);
+ int h = base::ClampCeil(rect.height() * y_scale);
return Rect(x, y, w, h);
}
diff --git a/chromium/ui/gfx/geometry/rect_conversions.cc b/chromium/ui/gfx/geometry/rect_conversions.cc
index 4958ef3160b..212318bdf1f 100644
--- a/chromium/ui/gfx/geometry/rect_conversions.cc
+++ b/chromium/ui/gfx/geometry/rect_conversions.cc
@@ -8,29 +8,29 @@
#include <cmath>
#include "base/check.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "base/numerics/safe_conversions.h"
namespace gfx {
namespace {
-int ToFlooredIntIgnoringError(float f, float error) {
- int rounded = ToRoundedInt(f);
- return std::abs(rounded - f) < error ? rounded : ToFlooredInt(f);
+int FloorIgnoringError(float f, float error) {
+ int rounded = base::ClampRound(f);
+ return std::abs(rounded - f) < error ? rounded : base::ClampFloor(f);
}
-int ToCeiledIntIgnoringError(float f, float error) {
- int rounded = ToRoundedInt(f);
- return std::abs(rounded - f) < error ? rounded : ToCeiledInt(f);
+int CeilIgnoringError(float f, float error) {
+ int rounded = base::ClampRound(f);
+ return std::abs(rounded - f) < error ? rounded : base::ClampCeil(f);
}
} // anonymous namespace
Rect ToEnclosingRect(const RectF& r) {
- int left = ToFlooredInt(r.x());
- int right = r.width() ? ToCeiledInt(r.right()) : left;
- int top = ToFlooredInt(r.y());
- int bottom = r.height() ? ToCeiledInt(r.bottom()) : top;
+ int left = base::ClampFloor(r.x());
+ int right = r.width() ? base::ClampCeil(r.right()) : left;
+ int top = base::ClampFloor(r.y());
+ int bottom = r.height() ? base::ClampCeil(r.bottom()) : top;
Rect result;
result.SetByBounds(left, top, right, bottom);
@@ -38,10 +38,10 @@ Rect ToEnclosingRect(const RectF& r) {
}
Rect ToEnclosingRectIgnoringError(const RectF& r, float error) {
- int left = ToFlooredIntIgnoringError(r.x(), error);
- int right = r.width() ? ToCeiledIntIgnoringError(r.right(), error) : left;
- int top = ToFlooredIntIgnoringError(r.y(), error);
- int bottom = r.height() ? ToCeiledIntIgnoringError(r.bottom(), error) : top;
+ int left = FloorIgnoringError(r.x(), error);
+ int right = r.width() ? CeilIgnoringError(r.right(), error) : left;
+ int top = FloorIgnoringError(r.y(), error);
+ int bottom = r.height() ? CeilIgnoringError(r.bottom(), error) : top;
Rect result;
result.SetByBounds(left, top, right, bottom);
@@ -50,16 +50,17 @@ Rect ToEnclosingRectIgnoringError(const RectF& r, float error) {
Rect ToEnclosedRect(const RectF& rect) {
Rect result;
- result.SetByBounds(ToCeiledInt(rect.x()), ToCeiledInt(rect.y()),
- ToFlooredInt(rect.right()), ToFlooredInt(rect.bottom()));
+ result.SetByBounds(base::ClampCeil(rect.x()), base::ClampCeil(rect.y()),
+ base::ClampFloor(rect.right()),
+ base::ClampFloor(rect.bottom()));
return result;
}
Rect ToEnclosedRectIgnoringError(const RectF& r, float error) {
- int left = ToCeiledIntIgnoringError(r.x(), error);
- int right = r.width() ? ToFlooredIntIgnoringError(r.right(), error) : left;
- int top = ToCeiledIntIgnoringError(r.y(), error);
- int bottom = r.height() ? ToFlooredIntIgnoringError(r.bottom(), error) : top;
+ int left = CeilIgnoringError(r.x(), error);
+ int right = r.width() ? FloorIgnoringError(r.right(), error) : left;
+ int top = CeilIgnoringError(r.y(), error);
+ int bottom = r.height() ? FloorIgnoringError(r.bottom(), error) : top;
Rect result;
result.SetByBounds(left, top, right, bottom);
@@ -72,10 +73,10 @@ Rect ToNearestRect(const RectF& rect) {
float float_max_x = rect.right();
float float_max_y = rect.bottom();
- int min_x = ToRoundedInt(float_min_x);
- int min_y = ToRoundedInt(float_min_y);
- int max_x = ToRoundedInt(float_max_x);
- int max_y = ToRoundedInt(float_max_y);
+ int min_x = base::ClampRound(float_min_x);
+ int min_y = base::ClampRound(float_min_y);
+ int max_x = base::ClampRound(float_max_x);
+ int max_y = base::ClampRound(float_max_y);
// If these DCHECKs fail, you're using the wrong method, consider using
// ToEnclosingRect or ToEnclosedRect instead.
@@ -96,33 +97,30 @@ bool IsNearestRectWithinDistance(const gfx::RectF& rect, float distance) {
float float_max_x = rect.right();
float float_max_y = rect.bottom();
- int min_x = ToRoundedInt(float_min_x);
- int min_y = ToRoundedInt(float_min_y);
- int max_x = ToRoundedInt(float_max_x);
- int max_y = ToRoundedInt(float_max_y);
+ int min_x = base::ClampRound(float_min_x);
+ int min_y = base::ClampRound(float_min_y);
+ int max_x = base::ClampRound(float_max_x);
+ int max_y = base::ClampRound(float_max_y);
- return
- (std::abs(min_x - float_min_x) < distance) &&
- (std::abs(min_y - float_min_y) < distance) &&
- (std::abs(max_x - float_max_x) < distance) &&
- (std::abs(max_y - float_max_y) < distance);
+ return (std::abs(min_x - float_min_x) < distance) &&
+ (std::abs(min_y - float_min_y) < distance) &&
+ (std::abs(max_x - float_max_x) < distance) &&
+ (std::abs(max_y - float_max_y) < distance);
}
gfx::Rect ToRoundedRect(const gfx::RectF& rect) {
- int left = ToRoundedInt(rect.x());
- int top = ToRoundedInt(rect.y());
- int right = ToRoundedInt(rect.right());
- int bottom = ToRoundedInt(rect.bottom());
+ int left = base::ClampRound(rect.x());
+ int top = base::ClampRound(rect.y());
+ int right = base::ClampRound(rect.right());
+ int bottom = base::ClampRound(rect.bottom());
gfx::Rect result;
result.SetByBounds(left, top, right, bottom);
return result;
}
Rect ToFlooredRectDeprecated(const RectF& rect) {
- return Rect(ToFlooredInt(rect.x()),
- ToFlooredInt(rect.y()),
- ToFlooredInt(rect.width()),
- ToFlooredInt(rect.height()));
+ return Rect(base::ClampFloor(rect.x()), base::ClampFloor(rect.y()),
+ base::ClampFloor(rect.width()), base::ClampFloor(rect.height()));
}
} // namespace gfx
diff --git a/chromium/ui/gfx/geometry/rect_f.cc b/chromium/ui/gfx/geometry/rect_f.cc
index 129e89fad80..0250709923a 100644
--- a/chromium/ui/gfx/geometry/rect_f.cc
+++ b/chromium/ui/gfx/geometry/rect_f.cc
@@ -8,14 +8,14 @@
#include <limits>
#include "base/check.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/insets_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#if defined(OS_IOS)
#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <ApplicationServices/ApplicationServices.h>
#endif
@@ -32,7 +32,7 @@ static void AdjustAlongAxis(float dst_origin,
*origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
}
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
RectF::RectF(const CGRect& r)
: origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {
}
@@ -223,9 +223,12 @@ float RectF::ManhattanInternalDistance(const RectF& rect) const {
}
bool RectF::IsExpressibleAsRect() const {
- return IsExpressibleAsInt(x()) && IsExpressibleAsInt(y()) &&
- IsExpressibleAsInt(width()) && IsExpressibleAsInt(height()) &&
- IsExpressibleAsInt(right()) && IsExpressibleAsInt(bottom());
+ return base::IsValueInRangeForNumericType<int>(x()) &&
+ base::IsValueInRangeForNumericType<int>(y()) &&
+ base::IsValueInRangeForNumericType<int>(width()) &&
+ base::IsValueInRangeForNumericType<int>(height()) &&
+ base::IsValueInRangeForNumericType<int>(right()) &&
+ base::IsValueInRangeForNumericType<int>(bottom());
}
std::string RectF::ToString() const {
diff --git a/chromium/ui/gfx/geometry/rect_f.h b/chromium/ui/gfx/geometry/rect_f.h
index 81abb8c46af..2a5571fd896 100644
--- a/chromium/ui/gfx/geometry/rect_f.h
+++ b/chromium/ui/gfx/geometry/rect_f.h
@@ -14,7 +14,7 @@
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
typedef struct CGRect CGRect;
#endif
@@ -39,7 +39,7 @@ class GEOMETRY_EXPORT RectF {
static_cast<float>(r.width()),
static_cast<float>(r.height())) {}
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
explicit RectF(const CGRect& r);
// Construct an equivalent CoreGraphics object.
CGRect ToCGRect() const;
diff --git a/chromium/ui/gfx/geometry/safe_integer_conversions.h b/chromium/ui/gfx/geometry/safe_integer_conversions.h
deleted file mode 100644
index c89cec2d422..00000000000
--- a/chromium/ui/gfx/geometry/safe_integer_conversions.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_
-#define UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_
-
-#include <cmath>
-#include <limits>
-
-#include "base/numerics/safe_conversions.h"
-
-namespace gfx {
-
-inline int ToFlooredInt(float value) {
- return base::saturated_cast<int>(std::floor(value));
-}
-
-inline int ToCeiledInt(float value) {
- return base::saturated_cast<int>(std::ceil(value));
-}
-
-inline int ToFlooredInt(double value) {
- return base::saturated_cast<int>(std::floor(value));
-}
-
-inline int ToCeiledInt(double value) {
- return base::saturated_cast<int>(std::ceil(value));
-}
-
-inline int ToRoundedInt(float value) {
- float rounded;
- if (value >= 0.0f)
- rounded = std::floor(value + 0.5f);
- else
- rounded = std::ceil(value - 0.5f);
- return base::saturated_cast<int>(rounded);
-}
-
-inline int ToRoundedInt(double value) {
- double rounded;
- if (value >= 0.0)
- rounded = std::floor(value + 0.5);
- else
- rounded = std::ceil(value - 0.5);
- return base::saturated_cast<int>(rounded);
-}
-
-inline bool IsExpressibleAsInt(float value) {
- if (value != value)
- return false; // no int NaN.
- if (value > std::numeric_limits<int>::max())
- return false;
- if (value < std::numeric_limits<int>::min())
- return false;
- return true;
-}
-
-} // namespace gfx
-
-#endif // UI_GFX_GEOMETRY_SAFE_INTEGER_CONVERSIONS_H_
diff --git a/chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc b/chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc
deleted file mode 100644
index 91bdbb8d359..00000000000
--- a/chromium/ui/gfx/geometry/safe_integer_conversions_unittest.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/gfx/geometry/safe_integer_conversions.h"
-
-#include <limits>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace gfx {
-
-TEST(SafeIntegerConversions, ToFlooredInt) {
- float max = std::numeric_limits<int>::max();
- float min = std::numeric_limits<int>::min();
- float infinity = std::numeric_limits<float>::infinity();
-
- int int_max = std::numeric_limits<int>::max();
- int int_min = std::numeric_limits<int>::min();
-
- EXPECT_EQ(int_max, ToFlooredInt(infinity));
- EXPECT_EQ(int_max, ToFlooredInt(max));
- EXPECT_EQ(int_max, ToFlooredInt(max + 100));
-
- EXPECT_EQ(-101, ToFlooredInt(-100.5f));
- EXPECT_EQ(0, ToFlooredInt(0.f));
- EXPECT_EQ(100, ToFlooredInt(100.5f));
-
- EXPECT_EQ(int_min, ToFlooredInt(-infinity));
- EXPECT_EQ(int_min, ToFlooredInt(min));
- EXPECT_EQ(int_min, ToFlooredInt(min - 100));
-}
-
-TEST(SafeIntegerConversions, ToCeiledInt) {
- float max = std::numeric_limits<int>::max();
- float min = std::numeric_limits<int>::min();
- float infinity = std::numeric_limits<float>::infinity();
-
- int int_max = std::numeric_limits<int>::max();
- int int_min = std::numeric_limits<int>::min();
-
- EXPECT_EQ(int_max, ToCeiledInt(infinity));
- EXPECT_EQ(int_max, ToCeiledInt(max));
- EXPECT_EQ(int_max, ToCeiledInt(max + 100));
-
- EXPECT_EQ(-100, ToCeiledInt(-100.5f));
- EXPECT_EQ(0, ToCeiledInt(0.f));
- EXPECT_EQ(101, ToCeiledInt(100.5f));
-
- EXPECT_EQ(int_min, ToCeiledInt(-infinity));
- EXPECT_EQ(int_min, ToCeiledInt(min));
- EXPECT_EQ(int_min, ToCeiledInt(min - 100));
-}
-
-TEST(SafeIntegerConversions, ToRoundedInt) {
- float max = std::numeric_limits<int>::max();
- float min = std::numeric_limits<int>::min();
- float infinity = std::numeric_limits<float>::infinity();
-
- int int_max = std::numeric_limits<int>::max();
- int int_min = std::numeric_limits<int>::min();
-
- EXPECT_EQ(int_max, ToRoundedInt(infinity));
- EXPECT_EQ(int_max, ToRoundedInt(max));
- EXPECT_EQ(int_max, ToRoundedInt(max + 100));
-
- EXPECT_EQ(-100, ToRoundedInt(-100.1f));
- EXPECT_EQ(-101, ToRoundedInt(-100.5f));
- EXPECT_EQ(-101, ToRoundedInt(-100.9f));
- EXPECT_EQ(0, ToRoundedInt(0.f));
- EXPECT_EQ(100, ToRoundedInt(100.1f));
- EXPECT_EQ(101, ToRoundedInt(100.5f));
- EXPECT_EQ(101, ToRoundedInt(100.9f));
-
- EXPECT_EQ(int_min, ToRoundedInt(-infinity));
- EXPECT_EQ(int_min, ToRoundedInt(min));
- EXPECT_EQ(int_min, ToRoundedInt(min - 100));
-}
-
-} // namespace gfx
diff --git a/chromium/ui/gfx/geometry/scroll_offset.h b/chromium/ui/gfx/geometry/scroll_offset.h
index 1f604fa6a7f..96e8d960896 100644
--- a/chromium/ui/gfx/geometry/scroll_offset.h
+++ b/chromium/ui/gfx/geometry/scroll_offset.h
@@ -8,8 +8,8 @@
#include <iosfwd>
#include <string>
+#include "base/numerics/safe_conversions.h"
#include "ui/gfx/geometry/geometry_export.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
namespace gfx {
@@ -106,7 +106,7 @@ inline ScrollOffset operator-(const ScrollOffset& lhs,
}
inline Vector2d ScrollOffsetToFlooredVector2d(const ScrollOffset& v) {
- return Vector2d(ToFlooredInt(v.x()), ToFlooredInt(v.y()));
+ return Vector2d(base::ClampFloor(v.x()), base::ClampFloor(v.y()));
}
inline Vector2dF ScrollOffsetToVector2dF(const ScrollOffset& v) {
diff --git a/chromium/ui/gfx/geometry/size.cc b/chromium/ui/gfx/geometry/size.cc
index 257425a55e0..bebcd33eedd 100644
--- a/chromium/ui/gfx/geometry/size.cc
+++ b/chromium/ui/gfx/geometry/size.cc
@@ -8,7 +8,7 @@
#include <windows.h>
#elif defined(OS_IOS)
#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <ApplicationServices/ApplicationServices.h>
#endif
@@ -16,12 +16,11 @@
#include "base/numerics/safe_math.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
namespace gfx {
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
Size::Size(const CGSize& s)
: width_(s.width < 0 ? 0 : s.width),
height_(s.height < 0 ? 0 : s.height) {
@@ -49,7 +48,7 @@ SIZE Size::ToSIZE() const {
s.cy = height();
return s;
}
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
CGSize Size::ToCGSize() const {
return CGSizeMake(width(), height());
}
diff --git a/chromium/ui/gfx/geometry/size.h b/chromium/ui/gfx/geometry/size.h
index 353a7cae864..3472dacf24b 100644
--- a/chromium/ui/gfx/geometry/size.h
+++ b/chromium/ui/gfx/geometry/size.h
@@ -16,7 +16,7 @@
#if defined(OS_WIN)
typedef struct tagSIZE SIZE;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
typedef struct CGSize CGSize;
#endif
@@ -28,11 +28,11 @@ class GEOMETRY_EXPORT Size {
constexpr Size() : width_(0), height_(0) {}
constexpr Size(int width, int height)
: width_(std::max(0, width)), height_(std::max(0, height)) {}
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
explicit Size(const CGSize& s);
#endif
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
Size& operator=(const CGSize& s);
#endif
@@ -42,7 +42,7 @@ class GEOMETRY_EXPORT Size {
#if defined(OS_WIN)
SIZE ToSIZE() const;
-#elif defined(OS_MACOSX) || defined(OS_IOS)
+#elif defined(OS_APPLE)
CGSize ToCGSize() const;
#endif
diff --git a/chromium/ui/gfx/geometry/size_conversions.cc b/chromium/ui/gfx/geometry/size_conversions.cc
index c924e86c9ae..18dab3340a6 100644
--- a/chromium/ui/gfx/geometry/size_conversions.cc
+++ b/chromium/ui/gfx/geometry/size_conversions.cc
@@ -4,26 +4,20 @@
#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "base/numerics/safe_conversions.h"
namespace gfx {
Size ToFlooredSize(const SizeF& size) {
- int w = ToFlooredInt(size.width());
- int h = ToFlooredInt(size.height());
- return Size(w, h);
+ return Size(base::ClampFloor(size.width()), base::ClampFloor(size.height()));
}
Size ToCeiledSize(const SizeF& size) {
- int w = ToCeiledInt(size.width());
- int h = ToCeiledInt(size.height());
- return Size(w, h);
+ return Size(base::ClampCeil(size.width()), base::ClampCeil(size.height()));
}
Size ToRoundedSize(const SizeF& size) {
- int w = ToRoundedInt(size.width());
- int h = ToRoundedInt(size.height());
- return Size(w, h);
+ return Size(base::ClampRound(size.width()), base::ClampRound(size.height()));
}
} // namespace gfx
diff --git a/chromium/ui/gfx/geometry/vector2d_conversions.cc b/chromium/ui/gfx/geometry/vector2d_conversions.cc
index dceb69e2ffe..c33199bf966 100644
--- a/chromium/ui/gfx/geometry/vector2d_conversions.cc
+++ b/chromium/ui/gfx/geometry/vector2d_conversions.cc
@@ -4,26 +4,22 @@
#include "ui/gfx/geometry/vector2d_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "base/numerics/safe_conversions.h"
namespace gfx {
Vector2d ToFlooredVector2d(const Vector2dF& vector2d) {
- int x = ToFlooredInt(vector2d.x());
- int y = ToFlooredInt(vector2d.y());
- return Vector2d(x, y);
+ return Vector2d(base::ClampFloor(vector2d.x()),
+ base::ClampFloor(vector2d.y()));
}
Vector2d ToCeiledVector2d(const Vector2dF& vector2d) {
- int x = ToCeiledInt(vector2d.x());
- int y = ToCeiledInt(vector2d.y());
- return Vector2d(x, y);
+ return Vector2d(base::ClampCeil(vector2d.x()), base::ClampCeil(vector2d.y()));
}
Vector2d ToRoundedVector2d(const Vector2dF& vector2d) {
- int x = ToRoundedInt(vector2d.x());
- int y = ToRoundedInt(vector2d.y());
- return Vector2d(x, y);
+ return Vector2d(base::ClampRound(vector2d.x()),
+ base::ClampRound(vector2d.y()));
}
} // namespace gfx
diff --git a/chromium/ui/gfx/gpu_fence.cc b/chromium/ui/gfx/gpu_fence.cc
index 87fc6d10761..a8c2422c833 100644
--- a/chromium/ui/gfx/gpu_fence.cc
+++ b/chromium/ui/gfx/gpu_fence.cc
@@ -8,7 +8,7 @@
#include "base/notreached.h"
#include "base/time/time.h"
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
#include <sync/sync.h>
#endif
@@ -62,7 +62,7 @@ void GpuFence::Wait() {
case GpuFenceHandleType::kEmpty:
break;
case GpuFenceHandleType::kAndroidNativeFenceSync:
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
static const int kInfiniteSyncWaitTimeout = -1;
DCHECK_GE(owned_fd_.get(), 0);
if (sync_wait(owned_fd_.get(), kInfiniteSyncWaitTimeout) < 0) {
@@ -79,7 +79,7 @@ void GpuFence::Wait() {
GpuFence::FenceStatus GpuFence::GetStatusChangeTime(int fd,
base::TimeTicks* time) {
DCHECK_NE(fd, -1);
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
auto info =
std::unique_ptr<sync_fence_info_data, void (*)(sync_fence_info_data*)>{
sync_fence_info(fd), sync_fence_info_free};
@@ -111,7 +111,7 @@ GpuFence::FenceStatus GpuFence::GetStatusChangeTime(int fd,
base::TimeTicks GpuFence::GetMaxTimestamp() const {
base::TimeTicks timestamp;
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
FenceStatus status = GetStatusChangeTime(owned_fd_.get(), &timestamp);
DCHECK_EQ(status, FenceStatus::kSignaled);
return timestamp;
diff --git a/chromium/ui/gfx/gpu_memory_buffer.cc b/chromium/ui/gfx/gpu_memory_buffer.cc
index 3df966fd645..56bda293133 100644
--- a/chromium/ui/gfx/gpu_memory_buffer.cc
+++ b/chromium/ui/gfx/gpu_memory_buffer.cc
@@ -34,9 +34,9 @@ GpuMemoryBufferHandle GpuMemoryBufferHandle::Clone() const {
handle.region = region.Duplicate();
handle.offset = offset;
handle.stride = stride;
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
handle.native_pixmap_handle = CloneHandleForIPC(native_pixmap_handle);
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_MAC)
NOTIMPLEMENTED();
#elif defined(OS_WIN)
NOTIMPLEMENTED();
diff --git a/chromium/ui/gfx/gpu_memory_buffer.h b/chromium/ui/gfx/gpu_memory_buffer.h
index 235177891fb..259e1426123 100644
--- a/chromium/ui/gfx/gpu_memory_buffer.h
+++ b/chromium/ui/gfx/gpu_memory_buffer.h
@@ -15,9 +15,9 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gfx_export.h"
-#if defined(USE_OZONE) || defined(OS_LINUX)
+#if defined(USE_OZONE) || defined(OS_LINUX) || defined(OS_CHROMEOS)
#include "ui/gfx/native_pixmap_handle.h"
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_MAC)
#include "ui/gfx/mac/io_surface.h"
#elif defined(OS_WIN)
#include "base/win/scoped_handle.h"
@@ -69,9 +69,9 @@ struct GFX_EXPORT GpuMemoryBufferHandle {
base::UnsafeSharedMemoryRegion region;
uint32_t offset = 0;
int32_t stride = 0;
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
NativePixmapHandle native_pixmap_handle;
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_MAC)
ScopedRefCountedIOSurfaceMachPort mach_port;
#elif defined(OS_WIN)
base::win::ScopedHandle dxgi_handle;
diff --git a/chromium/ui/gfx/icon_util.cc b/chromium/ui/gfx/icon_util.cc
index b9681af099f..42f3e56cb07 100644
--- a/chromium/ui/gfx/icon_util.cc
+++ b/chromium/ui/gfx/icon_util.cc
@@ -20,6 +20,7 @@
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h"
+#include "ui/gfx/skbitmap_operations.h"
namespace {
@@ -526,7 +527,7 @@ bool IconUtil::CreateIconFileFromImageFamily(
if (write_type == NORMAL_WRITE) {
if (base::WriteFile(icon_path, buffer))
return true;
- bool delete_success = base::DeleteFile(icon_path, false);
+ bool delete_success = base::DeleteFile(icon_path);
DCHECK(delete_success);
return false;
}
@@ -628,7 +629,11 @@ void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap,
// opaque.
unsigned char* image_addr = reinterpret_cast<unsigned char*>(icon_image);
unsigned char* xor_mask_addr = image_addr + sizeof(BITMAPINFOHEADER);
- CopySkBitmapBitsIntoIconBuffer(bitmap, xor_mask_addr, xor_mask_size);
+
+ // Make sure pixels are not premultiplied by alpha.
+ SkBitmap unpremul_bitmap = SkBitmapOperations::UnPreMultiply(bitmap);
+ CopySkBitmapBitsIntoIconBuffer(unpremul_bitmap, xor_mask_addr, xor_mask_size);
+
*image_byte_count = bytes_in_resource;
}
diff --git a/chromium/ui/gfx/icon_util_unittest.cc b/chromium/ui/gfx/icon_util_unittest.cc
index ca63b19b91d..4e856eb8292 100644
--- a/chromium/ui/gfx/icon_util_unittest.cc
+++ b/chromium/ui/gfx/icon_util_unittest.cc
@@ -14,6 +14,7 @@
#include "base/path_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/icon_util_unittests_resource.h"
#include "ui/gfx/image/image.h"
@@ -430,3 +431,36 @@ TEST_F(IconUtilTest, TestNumIconDimensionsUpToMediumSize) {
IconUtil::kIconDimensions[
IconUtil::kNumIconDimensionsUpToMediumSize - 1]);
}
+
+TEST_F(IconUtilTest, TestTransparentIcon) {
+ base::FilePath icon_filename =
+ temp_directory_.GetPath().AppendASCII(kTempIconFilename);
+ int size = 48;
+ auto semi_transparent_red = SkColorSetARGB(0x77, 0xFF, 0x00, 0x00);
+
+ // Create a bitmap with a semi transparent red dot.
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(size, size, false);
+ EXPECT_EQ(bitmap.alphaType(), kPremul_SkAlphaType);
+ {
+ SkCanvas canvas(bitmap);
+ canvas.drawColor(SK_ColorWHITE);
+ SkPaint paint;
+ paint.setColor(semi_transparent_red);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas.drawPoint(1, 1, paint);
+ }
+
+ // Create icon from that bitmap.
+ gfx::ImageFamily image_family;
+ image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap));
+ ASSERT_TRUE(
+ IconUtil::CreateIconFileFromImageFamily(image_family, icon_filename));
+
+ // Load icon and check that dot has same color.
+ ScopedHICON icon(LoadIconFromFile(icon_filename, size, size));
+ ASSERT_TRUE(icon.is_valid());
+ SkBitmap bitmap_loaded =
+ IconUtil::CreateSkBitmapFromHICON(icon.get(), gfx::Size(size, size));
+ EXPECT_EQ(bitmap_loaded.getColor(1, 1), semi_transparent_red);
+}
diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc
index 363b940a676..bfc36b69db9 100644
--- a/chromium/ui/gfx/image/image.cc
+++ b/chromium/ui/gfx/image/image.cc
@@ -22,7 +22,7 @@
#if defined(OS_IOS)
#include "base/mac/foundation_util.h"
#include "ui/gfx/image/image_skia_util_ios.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "ui/gfx/image/image_skia_util_mac.h"
@@ -84,7 +84,7 @@ class ImageRep {
return const_cast<ImageRepCocoaTouch*>(
static_cast<const ImageRep*>(this)->AsImageRepCocoaTouch());
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
const ImageRepCocoa* AsImageRepCocoa() const {
CHECK_EQ(type_, Image::kImageRepCocoa);
return reinterpret_cast<const ImageRepCocoa*>(this);
@@ -195,7 +195,7 @@ class ImageRepCocoaTouch : public ImageRep {
DISALLOW_COPY_AND_ASSIGN(ImageRepCocoaTouch);
};
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
class ImageRepCocoa : public ImageRep {
public:
explicit ImageRepCocoa(NSImage* image)
@@ -223,7 +223,7 @@ class ImageRepCocoa : public ImageRep {
DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa);
};
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
// The Storage class acts similarly to the pixels in a SkBitmap: the Image
// class holds a refptr instance of Storage, which in turn holds all the
@@ -236,11 +236,11 @@ class ImageStorage : public base::RefCounted<ImageStorage> {
public:
explicit ImageStorage(Image::RepresentationType default_type)
: default_representation_type_(default_type)
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
,
default_representation_color_space_(
base::mac::GetGenericRGBColorSpace())
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
{
}
@@ -282,7 +282,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> {
return result.first->second.get();
}
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
void set_default_representation_color_space(CGColorSpaceRef color_space) {
DCHECK(IsOnValidSequence());
default_representation_color_space_ = color_space;
@@ -291,7 +291,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> {
DCHECK(IsOnValidSequence());
return default_representation_color_space_;
}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
private:
friend class base::RefCounted<ImageStorage>;
@@ -302,13 +302,13 @@ class ImageStorage : public base::RefCounted<ImageStorage> {
// exist in the |representations_| map.
Image::RepresentationType default_representation_type_;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
// The default representation's colorspace. This is used for converting to
// NSImage. This field exists to compensate for PNGCodec not writing or
// reading colorspace ancillary chunks. (sRGB, iCCP).
// Not owned.
CGColorSpaceRef default_representation_color_space_;
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
// All the representations of an Image. Size will always be at least one, with
// more for any converted representations.
@@ -352,7 +352,7 @@ Image::Image(UIImage* image) {
AddRepresentation(std::make_unique<internal::ImageRepCocoaTouch>(image));
}
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
Image::Image(NSImage* image) {
if (image) {
storage_ = new internal::ImageStorage(Image::kImageRepCocoa);
@@ -371,6 +371,10 @@ Image& Image::operator=(Image&& other) noexcept = default;
Image::~Image() {}
+bool Image::operator==(const Image& other) const {
+ return storage_ == other.storage_;
+}
+
// static
Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) {
return Image(ImageSkia::CreateFrom1xBitmap(bitmap));
@@ -424,7 +428,7 @@ const ImageSkia* Image::ToImageSkia() const {
ImageSkia(ImageSkiaFromUIImage(native_rep->image())));
break;
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
case kImageRepCocoa: {
const internal::ImageRepCocoa* native_rep =
GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
@@ -470,7 +474,7 @@ UIImage* Image::ToUIImage() const {
}
return rep->AsImageRepCocoaTouch()->image();
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
NSImage* Image::ToNSImage() const {
const internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false);
if (!rep) {
@@ -531,7 +535,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const {
cocoa_touch_rep->image());
break;
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
case kImageRepCocoa: {
const internal::ImageRepCocoa* cocoa_rep =
GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
@@ -576,7 +580,7 @@ ImageSkia Image::AsImageSkia() const {
return IsEmpty() ? ImageSkia() : *ToImageSkia();
}
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
NSImage* Image::AsNSImage() const {
return IsEmpty() ? nil : ToNSImage();
}
@@ -612,12 +616,12 @@ gfx::Size Image::Size() const {
return GetRepresentation(DefaultRepresentationType(), true)->Size();
}
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
void Image::SetSourceColorSpace(CGColorSpaceRef color_space) {
if (storage())
storage()->set_default_representation_color_space(color_space);
}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
Image::RepresentationType Image::DefaultRepresentationType() const {
CHECK(storage());
diff --git a/chromium/ui/gfx/image/image.h b/chromium/ui/gfx/image/image.h
index d2dbcd28628..466b7bf5e98 100644
--- a/chromium/ui/gfx/image/image.h
+++ b/chromium/ui/gfx/image/image.h
@@ -31,7 +31,7 @@
#include "ui/gfx/gfx_export.h"
#include "ui/gfx/native_widget_types.h"
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
typedef struct CGColorSpace* CGColorSpaceRef;
#endif
@@ -70,7 +70,7 @@ class GFX_EXPORT Image {
#if defined(OS_IOS)
// Retains |image|.
explicit Image(UIImage* image);
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
// Retains |image|.
explicit Image(NSImage* image);
#endif
@@ -93,6 +93,9 @@ class GFX_EXPORT Image {
// representations.
~Image();
+ // True iff both images are backed by the same storage.
+ bool operator==(const Image& other) const;
+
// Creates an image from the passed in 1x bitmap.
// WARNING: The resulting image will be pixelated when painted on a high
// density display.
@@ -117,7 +120,7 @@ class GFX_EXPORT Image {
const ImageSkia* ToImageSkia() const;
#if defined(OS_IOS)
UIImage* ToUIImage() const;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
NSImage* ToNSImage() const;
#endif
@@ -135,7 +138,7 @@ class GFX_EXPORT Image {
ImageSkia AsImageSkia() const;
// Same as ToNSImage(), but returns nil if this image is empty.
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
NSImage* AsNSImage() const;
#endif
@@ -153,12 +156,12 @@ class GFX_EXPORT Image {
int Height() const;
gfx::Size Size() const;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
// Set the default representation's color space. This is used for converting
// to NSImage. This is used to compensate for PNGCodec not writing or reading
// colorspace ancillary chunks. (sRGB, iCCP).
void SetSourceColorSpace(CGColorSpaceRef color_space);
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
private:
// Returns the type of the default representation.
diff --git a/chromium/ui/gfx/image/image_platform.h b/chromium/ui/gfx/image/image_platform.h
index e77500c9fcf..610e8561a88 100644
--- a/chromium/ui/gfx/image/image_platform.h
+++ b/chromium/ui/gfx/image/image_platform.h
@@ -24,7 +24,7 @@
#if defined(OS_IOS)
#include "base/mac/foundation_util.h"
#include "ui/gfx/image/image_skia_util_ios.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "ui/gfx/image/image_skia_util_mac.h"
@@ -38,13 +38,13 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage(
UIImage* uiimage);
UIImage* UIImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps);
gfx::Size UIImageSize(UIImage* image);
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage(
NSImage* nsimage);
NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps,
CGColorSpaceRef color_space);
gfx::Size NSImageSize(NSImage* image);
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
ImageSkia ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps);
scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
diff --git a/chromium/ui/gfx/image/image_skia_operations.cc b/chromium/ui/gfx/image/image_skia_operations.cc
index 0de914b9d8f..da72f3a6786 100644
--- a/chromium/ui/gfx/image/image_skia_operations.cc
+++ b/chromium/ui/gfx/image/image_skia_operations.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkClipOp.h"
#include "third_party/skia/include/core/SkDrawLooper.h"
@@ -413,7 +414,8 @@ class HorizontalShadowSource : public CanvasImageSource {
static int GetHeightForShadows(const std::vector<ShadowValue>& shadows) {
int height = 0;
for (const auto& shadow : shadows) {
- height = std::max(height, shadow.y() + ToCeiledInt(shadow.blur() / 2));
+ height =
+ std::max(height, shadow.y() + base::ClampCeil(shadow.blur() / 2));
}
return height;
}
diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc
index 258009347c0..453baa97b3a 100644
--- a/chromium/ui/gfx/image/image_unittest.cc
+++ b/chromium/ui/gfx/image/image_unittest.cc
@@ -18,14 +18,14 @@
#if defined(OS_IOS)
#include "base/mac/foundation_util.h"
#include "skia/ext/skia_utils_ios.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include "base/mac/foundation_util.h"
#include "skia/ext/skia_utils_mac.h"
#endif
namespace {
-#if defined(OS_IOS) || defined(OS_MACOSX)
+#if defined(OS_APPLE)
const bool kUsesSkiaNatively = false;
#else
const bool kUsesSkiaNatively = true;
@@ -56,7 +56,7 @@ TEST_F(ImageTest, EmptyImage) {
// Test constructing a gfx::Image from an empty PlatformImage.
TEST_F(ImageTest, EmptyImageFromEmptyPlatformImage) {
-#if defined(OS_IOS) || defined(OS_MACOSX)
+#if defined(OS_APPLE)
gfx::Image image1(nullptr);
EXPECT_TRUE(image1.IsEmpty());
EXPECT_EQ(0, image1.Width());
diff --git a/chromium/ui/gfx/image/image_unittest_util.cc b/chromium/ui/gfx/image/image_unittest_util.cc
index 56ca50f0694..957b431e84f 100644
--- a/chromium/ui/gfx/image/image_unittest_util.cc
+++ b/chromium/ui/gfx/image/image_unittest_util.cc
@@ -21,7 +21,7 @@
#if defined(OS_IOS)
#include "base/mac/scoped_cftyperef.h"
#include "skia/ext/skia_utils_ios.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include "base/mac/mac_util.h"
#include "skia/ext/skia_utils_mac.h"
#endif
@@ -221,7 +221,7 @@ PlatformImage CreatePlatformImage() {
UIImage* image =
skia::SkBitmapToUIImageWithColorSpace(bitmap, scale, color_space);
return image;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
NSImage* image = skia::SkBitmapToNSImageWithColorSpace(
bitmap, base::mac::GetGenericRGBColorSpace());
return image;
@@ -233,7 +233,7 @@ PlatformImage CreatePlatformImage() {
gfx::Image::RepresentationType GetPlatformRepresentationType() {
#if defined(OS_IOS)
return gfx::Image::kImageRepCocoaTouch;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
return gfx::Image::kImageRepCocoa;
#else
return gfx::Image::kImageRepSkia;
@@ -243,7 +243,7 @@ gfx::Image::RepresentationType GetPlatformRepresentationType() {
PlatformImage ToPlatformType(const gfx::Image& image) {
#if defined(OS_IOS)
return image.ToUIImage();
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
return image.ToNSImage();
#else
return image.AsImageSkia();
@@ -253,14 +253,14 @@ PlatformImage ToPlatformType(const gfx::Image& image) {
gfx::Image CopyViaPlatformType(const gfx::Image& image) {
#if defined(OS_IOS)
return gfx::Image(image.ToUIImage());
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
return gfx::Image(image.ToNSImage());
#else
return gfx::Image(image.AsImageSkia());
#endif
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Defined in image_unittest_util_mac.mm.
#else
SkColor GetPlatformImageColor(PlatformImage image, int x, int y) {
@@ -277,7 +277,7 @@ void CheckIsTransparent(SkColor color) {
}
bool IsPlatformImageValid(PlatformImage image) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return image != NULL;
#else
return !image.isNull();
@@ -285,7 +285,7 @@ bool IsPlatformImageValid(PlatformImage image) {
}
bool PlatformImagesEqual(PlatformImage image1, PlatformImage image2) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return image1 == image2;
#else
return image1.BackedBySameObjectAs(image2);
diff --git a/chromium/ui/gfx/image/image_unittest_util.h b/chromium/ui/gfx/image/image_unittest_util.h
index 9d8e2bac614..b068e9de59c 100644
--- a/chromium/ui/gfx/image/image_unittest_util.h
+++ b/chromium/ui/gfx/image/image_unittest_util.h
@@ -17,7 +17,7 @@ namespace test {
#if defined(OS_IOS)
typedef UIImage* PlatformImage;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
typedef NSImage* PlatformImage;
#else
typedef gfx::ImageSkia PlatformImage;
diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc
index 0b6cacfca49..a1a30a52872 100644
--- a/chromium/ui/gfx/image/image_util.cc
+++ b/chromium/ui/gfx/image/image_util.cc
@@ -52,13 +52,13 @@ Image ResizedImageForSearchByImage(const Image& image) {
}
// The MacOS implementation of this function is in image_utils_mac.mm.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
bool JPEG1xEncodedDataFromImage(const Image& image,
int quality,
std::vector<unsigned char>* dst) {
return JPEG1xEncodedDataFromSkiaRepresentation(image, quality, dst);
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
bool JPEG1xEncodedDataFromSkiaRepresentation(const Image& image,
int quality,
diff --git a/chromium/ui/gfx/interpolated_transform.cc b/chromium/ui/gfx/interpolated_transform.cc
index fcbbdff9081..099afb31801 100644
--- a/chromium/ui/gfx/interpolated_transform.cc
+++ b/chromium/ui/gfx/interpolated_transform.cc
@@ -7,8 +7,8 @@
#include <cmath>
#include "base/check.h"
+#include "base/numerics/safe_conversions.h"
#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
namespace {
@@ -33,7 +33,7 @@ bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform* rotation,
SkMatrix44& m = transform.matrix();
float degrees_by_ninety = degrees / 90.0f;
- int n = gfx::ToRoundedInt(degrees_by_ninety);
+ int n = base::ClampRound(degrees_by_ninety);
n %= 4;
if (n < 0)
diff --git a/chromium/ui/gfx/ipc/BUILD.gn b/chromium/ui/gfx/ipc/BUILD.gn
index 91f59bd132d..170b9fc42dd 100644
--- a/chromium/ui/gfx/ipc/BUILD.gn
+++ b/chromium/ui/gfx/ipc/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("ipc") {
+component("ipc") {
output_name = "gfx_ipc"
sources = [
diff --git a/chromium/ui/gfx/ipc/buffer_types/BUILD.gn b/chromium/ui/gfx/ipc/buffer_types/BUILD.gn
index a1c2234f0bb..3cf5eca77f3 100644
--- a/chromium/ui/gfx/ipc/buffer_types/BUILD.gn
+++ b/chromium/ui/gfx/ipc/buffer_types/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("buffer_types") {
+component("buffer_types") {
output_name = "gfx_ipc_buffer_types"
sources = [
diff --git a/chromium/ui/gfx/ipc/color/BUILD.gn b/chromium/ui/gfx/ipc/color/BUILD.gn
index 496398dd77a..a9ab0323013 100644
--- a/chromium/ui/gfx/ipc/color/BUILD.gn
+++ b/chromium/ui/gfx/ipc/color/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("color") {
+component("color") {
output_name = "gfx_ipc_color"
sources = [
@@ -20,5 +18,6 @@ jumbo_component("color") {
"//base",
"//ipc",
"//ui/gfx:color_space",
+ "//ui/gfx/ipc/buffer_types",
]
}
diff --git a/chromium/ui/gfx/ipc/color/gfx_param_traits.cc b/chromium/ui/gfx/ipc/color/gfx_param_traits.cc
index 0c45fb2e439..2e9b2b548c5 100644
--- a/chromium/ui/gfx/ipc/color/gfx_param_traits.cc
+++ b/chromium/ui/gfx/ipc/color/gfx_param_traits.cc
@@ -4,8 +4,12 @@
#include "ui/gfx/ipc/color/gfx_param_traits.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/param_traits_macros.h"
#include "ui/gfx/color_space.h"
-#include "ui/gfx/ipc/color/gfx_param_traits_macros.h"
+#include "ui/gfx/display_color_spaces.h"
+#include "ui/gfx/ipc/buffer_types/gfx_ipc_export.h"
+#include "ui/gfx/ipc/buffer_types/gfx_param_traits_macros.h"
namespace IPC {
@@ -42,6 +46,31 @@ void ParamTraits<gfx::ColorSpace>::Log(const gfx::ColorSpace& p,
l->append("<gfx::ColorSpace>");
}
+void ParamTraits<gfx::DisplayColorSpaces>::Write(
+ base::Pickle* m,
+ const gfx::DisplayColorSpaces& p) {
+ WriteParam(m, p.color_spaces_);
+ WriteParam(m, p.buffer_formats_);
+ WriteParam(m, p.sdr_white_level_);
+}
+
+bool ParamTraits<gfx::DisplayColorSpaces>::Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ gfx::DisplayColorSpaces* r) {
+ if (!ReadParam(m, iter, &r->color_spaces_))
+ return false;
+ if (!ReadParam(m, iter, &r->buffer_formats_))
+ return false;
+ if (!ReadParam(m, iter, &r->sdr_white_level_))
+ return false;
+ return true;
+}
+
+void ParamTraits<gfx::DisplayColorSpaces>::Log(const gfx::DisplayColorSpaces& p,
+ std::string* l) {
+ l->append("<gfx::DisplayColorSpaces>");
+}
+
} // namespace IPC
// Generate param traits write methods.
diff --git a/chromium/ui/gfx/ipc/color/gfx_param_traits.h b/chromium/ui/gfx/ipc/color/gfx_param_traits.h
index 17ca534a617..8969194c3db 100644
--- a/chromium/ui/gfx/ipc/color/gfx_param_traits.h
+++ b/chromium/ui/gfx/ipc/color/gfx_param_traits.h
@@ -14,6 +14,7 @@
namespace gfx {
class ColorSpace;
+class DisplayColorSpaces;
}
namespace IPC {
@@ -28,6 +29,16 @@ struct GFX_IPC_COLOR_EXPORT ParamTraits<gfx::ColorSpace> {
static void Log(const param_type& p, std::string* l);
};
+template <>
+struct GFX_IPC_COLOR_EXPORT ParamTraits<gfx::DisplayColorSpaces> {
+ typedef gfx::DisplayColorSpaces param_type;
+ static void Write(base::Pickle* m, const param_type& p);
+ static bool Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
} // namespace IPC
#endif // UI_GFX_IPC_COLOR_GFX_PARAM_TRAITS_H_
diff --git a/chromium/ui/gfx/ipc/geometry/BUILD.gn b/chromium/ui/gfx/ipc/geometry/BUILD.gn
index bd57c70f3e5..b8796b222cc 100644
--- a/chromium/ui/gfx/ipc/geometry/BUILD.gn
+++ b/chromium/ui/gfx/ipc/geometry/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("geometry") {
+component("geometry") {
output_name = "gfx_ipc_geometry"
sources = [
diff --git a/chromium/ui/gfx/ipc/gfx_param_traits.cc b/chromium/ui/gfx/ipc/gfx_param_traits.cc
index 3d24aaa109f..b9ca505d213 100644
--- a/chromium/ui/gfx/ipc/gfx_param_traits.cc
+++ b/chromium/ui/gfx/ipc/gfx_param_traits.cc
@@ -12,7 +12,7 @@
#include "ui/gfx/ipc/geometry/gfx_param_traits.h"
#include "ui/gfx/range/range.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ipc/mach_port_mac.h"
#endif
@@ -38,7 +38,7 @@ void ParamTraits<gfx::Range>::Log(const gfx::Range& r, std::string* l) {
l->append(base::StringPrintf("(%d, %d)", r.start(), r.end()));
}
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
void ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort>::Write(
base::Pickle* m,
const param_type p) {
@@ -63,7 +63,7 @@ void ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort>::Log(
l->append("IOSurface Mach send right: ");
LogParam(p.get(), l);
}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
void ParamTraits<gfx::SelectionBound>::Write(base::Pickle* m,
const param_type& p) {
diff --git a/chromium/ui/gfx/ipc/gfx_param_traits.h b/chromium/ui/gfx/ipc/gfx_param_traits.h
index 4f3a17ce75c..2f91eb5d92c 100644
--- a/chromium/ui/gfx/ipc/gfx_param_traits.h
+++ b/chromium/ui/gfx/ipc/gfx_param_traits.h
@@ -14,7 +14,7 @@
#include "ui/gfx/ipc/gfx_param_traits_macros.h"
#include "ui/gfx/selection_bound.h"
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
#include "ui/gfx/mac/io_surface.h"
#endif
@@ -34,7 +34,7 @@ struct GFX_IPC_EXPORT ParamTraits<gfx::Range> {
static void Log(const param_type& p, std::string* l);
};
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
template <>
struct GFX_IPC_EXPORT ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort> {
typedef gfx::ScopedRefCountedIOSurfaceMachPort param_type;
@@ -48,7 +48,7 @@ struct GFX_IPC_EXPORT ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort> {
param_type* r);
static void Log(const param_type& p, std::string* l);
};
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif // defined(OS_MAC)
template <>
struct GFX_IPC_EXPORT ParamTraits<gfx::SelectionBound> {
diff --git a/chromium/ui/gfx/ipc/gfx_param_traits_macros.h b/chromium/ui/gfx/ipc/gfx_param_traits_macros.h
index a75e96c3256..f311c8d199c 100644
--- a/chromium/ui/gfx/ipc/gfx_param_traits_macros.h
+++ b/chromium/ui/gfx/ipc/gfx_param_traits_macros.h
@@ -18,7 +18,7 @@
#include "ui/gfx/selection_bound.h"
#include "ui/gfx/swap_result.h"
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
#include "ui/gfx/native_pixmap_handle.h"
#endif
@@ -37,7 +37,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(gfx::GpuFenceHandleType,
IPC_STRUCT_TRAITS_BEGIN(gfx::CALayerParams)
IPC_STRUCT_TRAITS_MEMBER(is_empty)
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
IPC_STRUCT_TRAITS_MEMBER(ca_context_id)
IPC_STRUCT_TRAITS_MEMBER(io_surface_mach_port)
IPC_STRUCT_TRAITS_MEMBER(pixel_size)
@@ -51,9 +51,9 @@ IPC_STRUCT_TRAITS_BEGIN(gfx::GpuMemoryBufferHandle)
IPC_STRUCT_TRAITS_MEMBER(region)
IPC_STRUCT_TRAITS_MEMBER(offset)
IPC_STRUCT_TRAITS_MEMBER(stride)
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
IPC_STRUCT_TRAITS_MEMBER(native_pixmap_handle)
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
IPC_STRUCT_TRAITS_MEMBER(mach_port)
#elif defined(OS_WIN)
IPC_STRUCT_TRAITS_MEMBER(dxgi_handle)
@@ -66,12 +66,12 @@ IPC_STRUCT_TRAITS_BEGIN(gfx::GpuMemoryBufferId)
IPC_STRUCT_TRAITS_MEMBER(id)
IPC_STRUCT_TRAITS_END()
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
IPC_STRUCT_TRAITS_BEGIN(gfx::NativePixmapPlane)
IPC_STRUCT_TRAITS_MEMBER(stride)
IPC_STRUCT_TRAITS_MEMBER(offset)
IPC_STRUCT_TRAITS_MEMBER(size)
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
IPC_STRUCT_TRAITS_MEMBER(fd)
#elif defined(OS_FUCHSIA)
IPC_STRUCT_TRAITS_MEMBER(vmo)
@@ -80,7 +80,7 @@ IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(gfx::NativePixmapHandle)
IPC_STRUCT_TRAITS_MEMBER(planes)
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
IPC_STRUCT_TRAITS_MEMBER(modifier)
#endif
#if defined(OS_FUCHSIA)
diff --git a/chromium/ui/gfx/ipc/skia/BUILD.gn b/chromium/ui/gfx/ipc/skia/BUILD.gn
index 2e4df260f7a..fcb670e0225 100644
--- a/chromium/ui/gfx/ipc/skia/BUILD.gn
+++ b/chromium/ui/gfx/ipc/skia/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("skia") {
+component("skia") {
output_name = "gfx_ipc_skia"
sources = [
diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
index 6a2a1883513..1774840ffbc 100644
--- a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
+++ b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
@@ -10,7 +10,6 @@
#include "ipc/ipc_message_utils.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImageInfo.h"
-#include "ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h"
#include "ui/gfx/transform.h"
// Generate param traits write methods.
diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h
index 3b0d4a82cd3..1026b729f8c 100644
--- a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h
+++ b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits.h
@@ -10,6 +10,7 @@
#include "ipc/ipc_message_utils.h"
#include "ipc/ipc_param_traits.h"
#include "ui/gfx/ipc/skia/gfx_skia_ipc_export.h"
+#include "ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h"
class SkBitmap;
struct SkImageInfo;
diff --git a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
index 410d37bce4b..4f1605b8554 100644
--- a/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
+++ b/chromium/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
@@ -10,7 +10,13 @@
#include "ipc/ipc_message_macros.h"
#include "third_party/skia/include/core/SkImageInfo.h"
+#undef IPC_MESSAGE_EXPORT
+#define IPC_MESSAGE_EXPORT GFX_SKIA_IPC_EXPORT
+
IPC_ENUM_TRAITS_VALIDATE(SkColorType, kLastEnum_SkColorType)
IPC_ENUM_TRAITS_VALIDATE(SkAlphaType, kLastEnum_SkAlphaType)
+#undef IPC_MESSAGE_EXPORT
+#define IPC_MESSAGE_EXPORT
+
#endif // UI_GFX_IPC_SKIA_GFX_SKIA_PARAM_TRAITS_MACROS_H_
diff --git a/chromium/ui/gfx/linux/gpu_memory_buffer_support_x11.cc b/chromium/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
index 49fa919ed79..38a438a4c17 100644
--- a/chromium/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
+++ b/chromium/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
@@ -5,7 +5,6 @@
#include "ui/gfx/linux/gpu_memory_buffer_support_x11.h"
#include <fcntl.h>
-#include <xcb/dri3.h>
#include <xcb/xcb.h>
#include <memory>
@@ -21,6 +20,8 @@
#include "ui/gfx/linux/gbm_device.h"
#include "ui/gfx/linux/gbm_util.h"
#include "ui/gfx/linux/gbm_wrapper.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/dri3.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
@@ -30,39 +31,26 @@ namespace {
// Obtain an authenticated DRM fd from X11 and create a GbmDevice with it.
std::unique_ptr<ui::GbmDevice> CreateX11GbmDevice() {
- XDisplay* display = gfx::GetXDisplay();
- // |display| may be nullptr in headless mode.
- if (!display)
+ auto* connection = x11::Connection::Get();
+ // |connection| may be nullptr in headless mode.
+ if (!connection)
return nullptr;
- xcb_connection_t* connection = XGetXCBConnection(display);
- DCHECK(connection);
- const auto* dri3_extension = xcb_get_extension_data(connection, &xcb_dri3_id);
- if (!dri3_extension || !dri3_extension->present)
+ auto& dri3 = connection->dri3();
+ if (!dri3.present())
return nullptr;
// Let the X11 server know the DRI3 client version. This is required to use
// the DRI3 extension. We don't care about the returned server version because
// we only use features from the original DRI3 interface.
- auto version_cookie = xcb_dri3_query_version(
- connection, XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION);
- auto* version_reply =
- xcb_dri3_query_version_reply(connection, version_cookie, nullptr);
- if (!version_reply)
- return nullptr;
- free(version_reply);
+ dri3.QueryVersion({x11::Dri3::major_version, x11::Dri3::minor_version});
// Obtain an authenticated DRM fd.
- auto open_cookie =
- xcb_dri3_open(connection, DefaultRootWindow(display), x11::None);
- auto* open_reply = xcb_dri3_open_reply(connection, open_cookie, nullptr);
- if (!open_reply)
- return nullptr;
- if (open_reply->nfd != 1)
+ auto reply = dri3.Open({connection->default_root(), 0}).Sync();
+ if (!reply)
return nullptr;
- int fd = xcb_dri3_open_reply_fds(connection, open_reply)[0];
- free(open_reply);
+ int fd = reply->device_fd.release();
if (fd < 0)
return nullptr;
if (HANDLE_EINTR(fcntl(fd, F_SETFD, FD_CLOEXEC)) == -1)
diff --git a/chromium/ui/gfx/mac/io_surface.cc b/chromium/ui/gfx/mac/io_surface.cc
index 2674784dca1..4f780768e5c 100644
--- a/chromium/ui/gfx/mac/io_surface.cc
+++ b/chromium/ui/gfx/mac/io_surface.cc
@@ -4,6 +4,8 @@
#include "ui/gfx/mac/io_surface.h"
+#include <Availability.h>
+#include <CoreGraphics/CoreGraphics.h>
#include <stddef.h>
#include <stdint.h>
@@ -43,10 +45,16 @@ int32_t BytesPerElement(gfx::BufferFormat format, int plane) {
case gfx::BufferFormat::RGBA_F16:
DCHECK_EQ(plane, 0);
return 8;
- case gfx::BufferFormat::YUV_420_BIPLANAR:
- static int32_t bytes_per_element[] = {1, 2};
+ case gfx::BufferFormat::YUV_420_BIPLANAR: {
+ constexpr int32_t bytes_per_element[] = {1, 2};
+ DCHECK_LT(static_cast<size_t>(plane), base::size(bytes_per_element));
+ return bytes_per_element[plane];
+ }
+ case gfx::BufferFormat::P010: {
+ constexpr int32_t bytes_per_element[] = {2, 4};
DCHECK_LT(static_cast<size_t>(plane), base::size(bytes_per_element));
return bytes_per_element[plane];
+ }
case gfx::BufferFormat::R_16:
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::BGR_565:
@@ -54,7 +62,6 @@ int32_t BytesPerElement(gfx::BufferFormat format, int plane) {
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_1010102:
case gfx::BufferFormat::YVU_420:
- case gfx::BufferFormat::P010:
NOTREACHED();
return 0;
}
@@ -77,6 +84,8 @@ int32_t PixelFormat(gfx::BufferFormat format) {
return 'RGhA';
case gfx::BufferFormat::YUV_420_BIPLANAR:
return '420v';
+ case gfx::BufferFormat::P010:
+ return 'x420';
case gfx::BufferFormat::R_16:
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::BGR_565:
@@ -86,7 +95,6 @@ int32_t PixelFormat(gfx::BufferFormat format) {
// Technically RGBA_1010102 should be accepted as 'R10k', but then it won't
// be supported by CGLTexImageIOSurface2D(), so it's best to reject it here.
case gfx::BufferFormat::YVU_420:
- case gfx::BufferFormat::P010:
NOTREACHED();
return 0;
}
@@ -136,21 +144,25 @@ bool IOSurfaceSetColorSpace(IOSurfaceRef io_surface,
color_space_name = kCGColorSpaceExtendedLinearSRGB;
}
}
+ // The symbols kCGColorSpaceITUR_2020_PQ_EOTF and kCGColorSpaceITUR_2020_HLG
+ // have been deprecated. Claim that we were able to set the color space,
+ // because the path that will render these color spaces will use the
+ // HDRCopier, which will manually convert them to a non-deprecated format.
+ // https://crbug.com/1108627: Bug wherein these symbols are deprecated and
+ // also not available in some SDK versions.
+ // https://crbug.com/1101041: Introduces the HDR copier.
+ // https://crbug.com/1061723: Discussion of issues related to HLG.
if (__builtin_available(macos 10.15, *)) {
if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
ColorSpace::TransferID::SMPTEST2084,
ColorSpace::MatrixID::BT2020_NCL,
ColorSpace::RangeID::LIMITED)) {
- color_space_name = kCGColorSpaceITUR_2020_PQ_EOTF;
+ return true;
} else if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
ColorSpace::TransferID::ARIB_STD_B67,
ColorSpace::MatrixID::BT2020_NCL,
ColorSpace::RangeID::LIMITED)) {
- // The CGColorSpace kCGColorSpaceITUR_2020_HLG cannot be used here because
- // it expects that "pixel values should be between 0.0 and 12.0", while
- // Chrome uses pixel values between 0.0 and 1.0.
- // https://crbug.com/1061723.
- return false;
+ return true;
}
}
if (color_space_name) {
diff --git a/chromium/ui/gfx/mojom/BUILD.gn b/chromium/ui/gfx/mojom/BUILD.gn
index f8d73762c53..723312a2b51 100644
--- a/chromium/ui/gfx/mojom/BUILD.gn
+++ b/chromium/ui/gfx/mojom/BUILD.gn
@@ -212,7 +212,7 @@ mojom("mojom") {
mojom("native_handle_types") {
sources = [ "native_handle_types.mojom" ]
- if (is_linux || use_ozone) {
+ if (is_linux || is_chromeos || use_ozone) {
enabled_features = [ "supports_native_pixmap" ]
}
public_deps = [ "//mojo/public/mojom/base" ]
diff --git a/chromium/ui/gfx/mojom/accelerated_widget_mojom_traits.h b/chromium/ui/gfx/mojom/accelerated_widget_mojom_traits.h
index 1b3e278c57e..668459088eb 100644
--- a/chromium/ui/gfx/mojom/accelerated_widget_mojom_traits.h
+++ b/chromium/ui/gfx/mojom/accelerated_widget_mojom_traits.h
@@ -17,7 +17,7 @@ struct StructTraits<gfx::mojom::AcceleratedWidgetDataView,
static uint64_t widget(gfx::AcceleratedWidget widget) {
#if defined(OS_WIN) || defined(OS_ANDROID) || defined(OS_IOS)
return reinterpret_cast<uint64_t>(widget);
-#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MACOSX)
+#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MAC)
return static_cast<uint64_t>(widget);
#else
NOTREACHED();
@@ -30,7 +30,7 @@ struct StructTraits<gfx::mojom::AcceleratedWidgetDataView,
#if defined(OS_WIN) || defined(OS_ANDROID) || defined(OS_IOS)
*out = reinterpret_cast<gfx::AcceleratedWidget>(data.widget());
return true;
-#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MACOSX)
+#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MAC)
*out = static_cast<gfx::AcceleratedWidget>(data.widget());
return true;
#else
diff --git a/chromium/ui/gfx/mojom/buffer_types_mojom_traits.cc b/chromium/ui/gfx/mojom/buffer_types_mojom_traits.cc
index 3bf27b79216..007297ee205 100644
--- a/chromium/ui/gfx/mojom/buffer_types_mojom_traits.cc
+++ b/chromium/ui/gfx/mojom/buffer_types_mojom_traits.cc
@@ -33,14 +33,14 @@ gfx::mojom::GpuMemoryBufferPlatformHandlePtr StructTraits<
return gfx::mojom::GpuMemoryBufferPlatformHandle::NewSharedMemoryHandle(
std::move(handle.region));
case gfx::NATIVE_PIXMAP:
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
return gfx::mojom::GpuMemoryBufferPlatformHandle::NewNativePixmapHandle(
std::move(handle.native_pixmap_handle));
#else
break;
#endif
case gfx::IO_SURFACE_BUFFER:
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
return gfx::mojom::GpuMemoryBufferPlatformHandle::NewMachPort(
mojo::PlatformHandle(
base::mac::RetainMachSendRight(handle.mach_port.get())));
@@ -109,14 +109,14 @@ bool StructTraits<gfx::mojom::GpuMemoryBufferHandleDataView,
out->type = gfx::SHARED_MEMORY_BUFFER;
out->region = std::move(platform_handle->get_shared_memory_handle());
return true;
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::
NATIVE_PIXMAP_HANDLE:
out->type = gfx::NATIVE_PIXMAP;
out->native_pixmap_handle =
std::move(platform_handle->get_native_pixmap_handle());
return true;
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_MAC)
case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::MACH_PORT: {
out->type = gfx::IO_SURFACE_BUFFER;
if (!platform_handle->get_mach_port().is_mach_send())
diff --git a/chromium/ui/gfx/mojom/ca_layer_params_mojom_traits.cc b/chromium/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
index d6ec9ef087f..c1f21d7f434 100644
--- a/chromium/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
+++ b/chromium/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
@@ -13,7 +13,7 @@ namespace mojo {
gfx::mojom::CALayerContentPtr
StructTraits<gfx::mojom::CALayerParamsDataView, gfx::CALayerParams>::content(
const gfx::CALayerParams& ca_layer_params) {
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
if (ca_layer_params.io_surface_mach_port) {
DCHECK(!ca_layer_params.ca_context_id);
return gfx::mojom::CALayerContent::NewIoSurfaceMachPort(
@@ -37,7 +37,7 @@ bool StructTraits<gfx::mojom::CALayerParamsDataView, gfx::CALayerParams>::Read(
out->ca_context_id = content_data.ca_context_id();
break;
case gfx::mojom::CALayerContentDataView::Tag::IO_SURFACE_MACH_PORT:
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
mojo::PlatformHandle platform_handle =
content_data.TakeIoSurfaceMachPort();
if (!platform_handle.is_mach_send())
diff --git a/chromium/ui/gfx/mojom/mojom_traits_unittest.cc b/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
index 4bd41e851d7..73c764ffb75 100644
--- a/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
+++ b/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
@@ -25,7 +25,7 @@ namespace gfx {
namespace {
gfx::AcceleratedWidget CastToAcceleratedWidget(int i) {
-#if defined(USE_OZONE) || defined(USE_X11) || defined(OS_MACOSX)
+#if defined(USE_OZONE) || defined(USE_X11) || defined(OS_APPLE)
return static_cast<gfx::AcceleratedWidget>(i);
#else
return reinterpret_cast<gfx::AcceleratedWidget>(i);
@@ -170,14 +170,14 @@ TEST_F(StructTraitsTest, GpuMemoryBufferHandle) {
base::UnsafeSharedMemoryRegion output_memory = std::move(output.region);
EXPECT_TRUE(output_memory.Map().IsValid());
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
gfx::GpuMemoryBufferHandle handle2;
const uint64_t kSize = kOffset + kStride;
handle2.type = gfx::NATIVE_PIXMAP;
handle2.id = kId;
handle2.offset = kOffset;
handle2.stride = kStride;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
const uint64_t kModifier = 2;
base::ScopedFD buffer_handle;
handle2.native_pixmap_handle.modifier = kModifier;
@@ -192,7 +192,7 @@ TEST_F(StructTraitsTest, GpuMemoryBufferHandle) {
std::move(buffer_handle));
remote->EchoGpuMemoryBufferHandle(std::move(handle2), &output);
EXPECT_EQ(gfx::NATIVE_PIXMAP, output.type);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EXPECT_EQ(kModifier, output.native_pixmap_handle.modifier);
#elif defined(OS_FUCHSIA)
EXPECT_EQ(handle2.native_pixmap_handle.buffer_collection_id,
diff --git a/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.cc b/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.cc
index 6263574a4cd..15e5b8b9766 100644
--- a/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.cc
+++ b/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.cc
@@ -8,15 +8,15 @@
namespace mojo {
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
mojo::PlatformHandle StructTraits<
gfx::mojom::NativePixmapPlaneDataView,
gfx::NativePixmapPlane>::buffer_handle(gfx::NativePixmapPlane& plane) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
return mojo::PlatformHandle(std::move(plane.fd));
#elif defined(OS_FUCHSIA)
return mojo::PlatformHandle(std::move(plane.vmo));
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
bool StructTraits<
@@ -28,7 +28,7 @@ bool StructTraits<
out->size = data.size();
mojo::PlatformHandle handle = data.TakeBufferHandle();
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
if (!handle.is_fd())
return false;
out->fd = handle.TakeFD();
@@ -36,7 +36,7 @@ bool StructTraits<
if (!handle.is_handle())
return false;
out->vmo = zx::vmo(handle.TakeHandle());
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
return true;
}
@@ -45,7 +45,7 @@ bool StructTraits<
gfx::mojom::NativePixmapHandleDataView,
gfx::NativePixmapHandle>::Read(gfx::mojom::NativePixmapHandleDataView data,
gfx::NativePixmapHandle* out) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
out->modifier = data.modifier();
#endif
@@ -58,6 +58,6 @@ bool StructTraits<
return data.ReadPlanes(&out->planes);
}
-#endif // defined(OS_LINUX) || defined(USE_OZONE)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
} // namespace mojo
diff --git a/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.h b/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.h
index 48369574f92..6e9ed3d64ed 100644
--- a/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.h
+++ b/chromium/ui/gfx/mojom/native_handle_types_mojom_traits.h
@@ -14,13 +14,13 @@
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/mojom/native_handle_types.mojom-shared.h"
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
#include "ui/gfx/native_pixmap_handle.h"
#endif
namespace mojo {
-#if defined(OS_LINUX) || defined(USE_OZONE)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
template <>
struct COMPONENT_EXPORT(GFX_NATIVE_HANDLE_TYPES_SHARED_MOJOM_TRAITS)
StructTraits<gfx::mojom::NativePixmapPlaneDataView,
@@ -48,7 +48,7 @@ struct COMPONENT_EXPORT(GFX_NATIVE_HANDLE_TYPES_SHARED_MOJOM_TRAITS)
return pixmap_handle.planes;
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
static uint64_t modifier(const gfx::NativePixmapHandle& pixmap_handle) {
return pixmap_handle.modifier;
}
@@ -72,7 +72,7 @@ struct COMPONENT_EXPORT(GFX_NATIVE_HANDLE_TYPES_SHARED_MOJOM_TRAITS)
static bool Read(gfx::mojom::NativePixmapHandleDataView data,
gfx::NativePixmapHandle* out);
};
-#endif // defined(OS_LINUX) || defined(USE_OZONE)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
} // namespace mojo
diff --git a/chromium/ui/gfx/native_pixmap_handle.cc b/chromium/ui/gfx/native_pixmap_handle.cc
index fa41b717ea7..137056bcdcd 100644
--- a/chromium/ui/gfx/native_pixmap_handle.cc
+++ b/chromium/ui/gfx/native_pixmap_handle.cc
@@ -9,7 +9,7 @@
#include "base/logging.h"
#include "build/build_config.h"
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
#include <drm_fourcc.h>
#include "base/posix/eintr_wrapper.h"
#endif
@@ -21,7 +21,7 @@
namespace gfx {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
static_assert(NativePixmapHandle::kNoModifier == DRM_FORMAT_MOD_INVALID,
"gfx::NativePixmapHandle::kNoModifier should be an alias for"
"DRM_FORMAT_MOD_INVALID");
@@ -32,7 +32,7 @@ NativePixmapPlane::NativePixmapPlane() : stride(0), offset(0), size(0) {}
NativePixmapPlane::NativePixmapPlane(int stride,
int offset,
uint64_t size
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
,
base::ScopedFD fd
#elif defined(OS_FUCHSIA)
@@ -43,7 +43,7 @@ NativePixmapPlane::NativePixmapPlane(int stride,
: stride(stride),
offset(offset),
size(size)
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
,
fd(std::move(fd))
#elif defined(OS_FUCHSIA)
@@ -71,7 +71,7 @@ NativePixmapHandle& NativePixmapHandle::operator=(NativePixmapHandle&& other) =
NativePixmapHandle CloneHandleForIPC(const NativePixmapHandle& handle) {
NativePixmapHandle clone;
for (auto& plane : handle.planes) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
DCHECK(plane.fd.is_valid());
base::ScopedFD fd_dup(HANDLE_EINTR(dup(plane.fd.get())));
if (!fd_dup.is_valid()) {
@@ -97,7 +97,7 @@ NativePixmapHandle CloneHandleForIPC(const NativePixmapHandle& handle) {
#endif
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
clone.modifier = handle.modifier;
#endif
diff --git a/chromium/ui/gfx/native_pixmap_handle.h b/chromium/ui/gfx/native_pixmap_handle.h
index b7f4c18ba13..1748dd32f7a 100644
--- a/chromium/ui/gfx/native_pixmap_handle.h
+++ b/chromium/ui/gfx/native_pixmap_handle.h
@@ -15,7 +15,7 @@
#include "build/build_config.h"
#include "ui/gfx/gfx_export.h"
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
#include "base/files/scoped_file.h"
#endif
@@ -32,7 +32,7 @@ struct GFX_EXPORT NativePixmapPlane {
NativePixmapPlane(int stride,
int offset,
uint64_t size
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
,
base::ScopedFD fd
#elif defined(OS_FUCHSIA)
@@ -53,7 +53,7 @@ struct GFX_EXPORT NativePixmapPlane {
// This is necessary to map the buffers.
uint64_t size;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// File descriptor for the underlying memory object (usually dmabuf).
base::ScopedFD fd;
#elif defined(OS_FUCHSIA)
@@ -82,7 +82,7 @@ struct GFX_EXPORT NativePixmapHandle {
std::vector<NativePixmapPlane> planes;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// The modifier is retrieved from GBM library and passed to EGL driver.
// Generally it's platform specific, and we don't need to modify it in
// Chromium code. Also one per plane per entry.
diff --git a/chromium/ui/gfx/native_widget_types.h b/chromium/ui/gfx/native_widget_types.h
index eb5af9df8ee..f823bb318de 100644
--- a/chromium/ui/gfx/native_widget_types.h
+++ b/chromium/ui/gfx/native_widget_types.h
@@ -12,7 +12,7 @@
#if defined(OS_ANDROID)
#include "base/android/scoped_java_ref.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <objc/objc.h>
#elif defined(OS_WIN)
#include "base/win/windows_types.h"
@@ -73,7 +73,7 @@ class UIView;
class UIWindow;
class UITextField;
#endif // __OBJC__
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
struct CGContext;
#ifdef __OBJC__
@class NSCursor;
@@ -110,12 +110,6 @@ typedef struct _AtkObject AtkObject;
}
#endif
-#if defined(USE_X11)
-namespace x11 {
-enum class Window : uint32_t;
-}
-#endif
-
namespace gfx {
#if defined(USE_AURA)
@@ -132,7 +126,7 @@ typedef UIWindow* NativeWindow;
typedef UIEvent* NativeEvent;
constexpr NativeView kNullNativeView = nullptr;
constexpr NativeWindow kNullNativeWindow = nullptr;
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
typedef NSCursor* NativeCursor;
typedef NSEvent* NativeEvent;
// NativeViews and NativeWindows on macOS are not necessarily in the same
@@ -207,7 +201,7 @@ typedef IAccessible* NativeViewAccessible;
#elif defined(OS_IOS)
typedef UIFont* NativeFont;
typedef id NativeViewAccessible;
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
typedef NSFont* NativeFont;
typedef id NativeViewAccessible;
#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -239,21 +233,17 @@ typedef intptr_t NativeViewId;
#if defined(OS_WIN)
typedef HWND AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = nullptr;
-#elif defined(USE_X11)
-typedef x11::Window AcceleratedWidget;
-constexpr AcceleratedWidget kNullAcceleratedWidget =
- static_cast<x11::Window>(0);
#elif defined(OS_IOS)
typedef UIView* AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
typedef uint64_t AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
#elif defined(OS_ANDROID)
typedef ANativeWindow* AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(USE_OZONE)
-typedef int32_t AcceleratedWidget;
+#elif defined(USE_OZONE) || defined(USE_X11)
+typedef uint32_t AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
#else
#error unknown platform
diff --git a/chromium/ui/gfx/nine_image_painter.cc b/chromium/ui/gfx/nine_image_painter.cc
index db940f4b37d..8e9e99eaa50 100644
--- a/chromium/ui/gfx/nine_image_painter.cc
+++ b/chromium/ui/gfx/nine_image_painter.cc
@@ -8,6 +8,7 @@
#include <limits>
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "cc/paint/paint_flags.h"
#include "third_party/skia/include/core/SkRect.h"
@@ -16,7 +17,6 @@
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_util.h"
@@ -99,10 +99,10 @@ void NineImagePainter::Paint(Canvas* canvas,
// Since the drawing from the following Fill() calls assumes the mapped origin
// is at (0,0), we need to translate the canvas to the mapped origin.
- const int left_in_pixels = ToRoundedInt(bounds.x() * scale);
- const int top_in_pixels = ToRoundedInt(bounds.y() * scale);
- const int right_in_pixels = ToRoundedInt(bounds.right() * scale);
- const int bottom_in_pixels = ToRoundedInt(bounds.bottom() * scale);
+ const int left_in_pixels = base::ClampRound(bounds.x() * scale);
+ const int top_in_pixels = base::ClampRound(bounds.y() * scale);
+ const int right_in_pixels = base::ClampRound(bounds.right() * scale);
+ const int bottom_in_pixels = base::ClampRound(bounds.bottom() * scale);
const int width_in_pixels = right_in_pixels - left_in_pixels;
const int height_in_pixels = bottom_in_pixels - top_in_pixels;
diff --git a/chromium/ui/gfx/paint_throbber.cc b/chromium/ui/gfx/paint_throbber.cc
index e59b25bf820..e6f6f2082d0 100644
--- a/chromium/ui/gfx/paint_throbber.cc
+++ b/chromium/ui/gfx/paint_throbber.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "cc/paint/paint_flags.h"
#include "third_party/skia/include/core/SkPath.h"
@@ -71,7 +72,8 @@ void CalculateWaitingAngles(const base::TimeDelta& elapsed_time,
constexpr auto kRevolutionTime = base::TimeDelta::FromMilliseconds(1320);
int64_t twelve_oclock = 90;
int64_t finish_angle_cc =
- twelve_oclock + 360 * elapsed_time / kRevolutionTime;
+ twelve_oclock +
+ base::ClampRound<int64_t>(elapsed_time / kRevolutionTime * 360);
int64_t start_angle_cc = std::max(finish_angle_cc - 180, twelve_oclock);
// Negate the angles to convert to the clockwise numbers Skia expects.
@@ -93,13 +95,13 @@ void PaintThrobberSpinningWithStartAngle(
// The sweep angle ranges from -270 to 270 over 1333ms. CSS
// animation timing functions apply in between key frames, so we have to
// break up the 1333ms into two keyframes (-270 to 0, then 0 to 270).
- const double arc_progress =
- (elapsed_time % kArcTime).InMicrosecondsF() / kArcTime.InMicrosecondsF();
+ const double elapsed_ratio = elapsed_time / kArcTime;
+ const int64_t sweep_frame = base::ClampFloor<int64_t>(elapsed_ratio);
+ const double arc_progress = elapsed_ratio - sweep_frame;
// This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1).
double sweep = kMaxArcSize *
Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN, arc_progress);
- const int64_t sweep_keyframe = (elapsed_time / kArcTime) % 2;
- if (sweep_keyframe == 0)
+ if (sweep_frame % 2 == 0)
sweep -= kMaxArcSize;
// This part makes sure the sweep is at least 5 degrees long. Roughly
@@ -115,7 +117,7 @@ void PaintThrobberSpinningWithStartAngle(
// To keep the sweep smooth, we have an additional rotation after each
// arc period has elapsed. See SVG's 'rot' animation.
- const int64_t rot_keyframe = (elapsed_time / (kArcTime * 2)) % 4;
+ const int64_t rot_keyframe = (sweep_frame / 2) % 4;
PaintArc(canvas, bounds, color, start_angle + rot_keyframe * kMaxArcSize,
sweep, stroke_width);
}
@@ -127,7 +129,8 @@ void PaintThrobberSpinning(Canvas* canvas,
SkColor color,
const base::TimeDelta& elapsed_time,
base::Optional<SkScalar> stroke_width) {
- const int64_t start_angle = 270 + 360 * elapsed_time / kRotationTime;
+ const int64_t start_angle =
+ 270 + base::ClampRound<int64_t>(elapsed_time / kRotationTime * 360);
PaintThrobberSpinningWithStartAngle(canvas, bounds, color, elapsed_time,
start_angle, stroke_width);
}
@@ -157,13 +160,13 @@ void PaintThrobberSpinningAfterWaiting(Canvas* canvas,
if (waiting_state->arc_time_offset.is_zero()) {
for (int64_t arc_ms = 0; arc_ms <= kArcTime.InMillisecondsRoundedUp();
++arc_ms) {
- double arc_size_progress =
- std::min(1.0, arc_ms / kArcTime.InMillisecondsF());
+ const base::TimeDelta arc_time =
+ std::min(base::TimeDelta::FromMilliseconds(arc_ms), kArcTime);
if (kMaxArcSize * Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN,
- arc_size_progress) >=
+ arc_time / kArcTime) >=
waiting_sweep) {
// Add kArcTime to sidestep the |sweep_keyframe == 0| offset below.
- waiting_state->arc_time_offset = kArcTime * (arc_size_progress + 1);
+ waiting_state->arc_time_offset = kArcTime + arc_time;
break;
}
}
@@ -171,17 +174,14 @@ void PaintThrobberSpinningAfterWaiting(Canvas* canvas,
// Blend the color between "waiting" and "spinning" states.
constexpr auto kColorFadeTime = base::TimeDelta::FromMilliseconds(900);
- float color_progress = 1.0f;
- if (elapsed_time < kColorFadeTime) {
- color_progress = float{Tween::CalculateValue(
- Tween::LINEAR_OUT_SLOW_IN,
- elapsed_time.InMicrosecondsF() / kColorFadeTime.InMicrosecondsF())};
- }
+ const float color_progress = float{Tween::CalculateValue(
+ Tween::LINEAR_OUT_SLOW_IN, std::min(elapsed_time / kColorFadeTime, 1.0))};
const SkColor blend_color =
color_utils::AlphaBlend(color, waiting_state->color, color_progress);
const int64_t start_angle =
- waiting_start_angle + 360 * elapsed_time / kRotationTime;
+ waiting_start_angle +
+ base::ClampRound<int64_t>(elapsed_time / kRotationTime * 360);
const base::TimeDelta effective_elapsed_time =
elapsed_time + waiting_state->arc_time_offset;
@@ -200,9 +200,8 @@ GFX_EXPORT void PaintNewThrobberWaiting(Canvas* canvas,
// The throbber bounces back and forth. We map the elapsed time to 0->2. Time
// 0->1 represents when the throbber moves left to right, time 1->2 represents
// right to left.
- float time = 2.0f *
- (elapsed_time % kNewThrobberWaitingCycleTime).InMicrosecondsF() /
- kNewThrobberWaitingCycleTime.InMicrosecondsF();
+ float time = 2.0f * (elapsed_time % kNewThrobberWaitingCycleTime) /
+ kNewThrobberWaitingCycleTime;
// 1 -> 2 values mirror back to 1 -> 0 values to represent right-to-left.
const bool going_back = time > 1.0f;
if (going_back)
diff --git a/chromium/ui/gfx/paint_vector_icon.cc b/chromium/ui/gfx/paint_vector_icon.cc
index 01c19741433..eff2ad62ac3 100644
--- a/chromium/ui/gfx/paint_vector_icon.cc
+++ b/chromium/ui/gfx/paint_vector_icon.cc
@@ -11,6 +11,7 @@
#include "base/i18n/rtl.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/trace_event/trace_event.h"
@@ -561,14 +562,14 @@ void PaintVectorIcon(Canvas* canvas,
DCHECK(!icon.is_empty());
for (size_t i = 0; i < icon.reps_size; ++i)
DCHECK(icon.reps[i].path_size > 0);
- const int px_size = gfx::ToCeiledInt(canvas->image_scale() * dip_size);
+ const int px_size = base::ClampCeil(canvas->image_scale() * dip_size);
const VectorIconRep* rep = GetRepForPxSize(icon, px_size);
PaintPath(canvas, rep->path, rep->path_size, dip_size, color);
}
ImageSkia CreateVectorIcon(const IconDescription& params) {
if (params.icon.is_empty())
- return gfx::ImageSkia();
+ return ImageSkia();
return g_icon_cache.Get().GetOrCreateIcon(params);
}
diff --git a/chromium/ui/gfx/platform_font.h b/chromium/ui/gfx/platform_font.h
index 16c69abe891..10dcc7ea900 100644
--- a/chromium/ui/gfx/platform_font.h
+++ b/chromium/ui/gfx/platform_font.h
@@ -25,7 +25,7 @@ class GFX_EXPORT PlatformFont : public base::RefCounted<PlatformFont> {
// configuration. This allows UI that wants to target a particular size of font
// to obtain that size for the majority of users, while still compensating for a
// user preference for a larger or smaller font.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
static constexpr int kDefaultBaseFontSize = 13;
#else
static constexpr int kDefaultBaseFontSize = 12;
@@ -33,7 +33,7 @@ class GFX_EXPORT PlatformFont : public base::RefCounted<PlatformFont> {
// Creates an appropriate PlatformFont implementation.
static PlatformFont* CreateDefault();
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
static PlatformFont* CreateFromNativeFont(NativeFont native_font);
#endif
// Creates a PlatformFont implementation with the specified |font_name|
@@ -96,7 +96,7 @@ class GFX_EXPORT PlatformFont : public base::RefCounted<PlatformFont> {
// Returns an object describing how the font should be rendered.
virtual const FontRenderParams& GetFontRenderParams() = 0;
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_APPLE)
// Returns the native font handle.
virtual NativeFont GetNativeFont() const = 0;
#endif
diff --git a/chromium/ui/gfx/platform_font_mac.h b/chromium/ui/gfx/platform_font_mac.h
index 1854a953588..667b8dbaa22 100644
--- a/chromium/ui/gfx/platform_font_mac.h
+++ b/chromium/ui/gfx/platform_font_mac.h
@@ -8,18 +8,39 @@
#include "base/compiler_specific.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "ui/gfx/font_render_params.h"
#include "ui/gfx/platform_font.h"
namespace gfx {
-class PlatformFontMac : public PlatformFont {
+class GFX_EXPORT PlatformFontMac : public PlatformFont {
public:
- PlatformFontMac();
+ // An enum indicating a type of system-specified font.
+ // - kGeneral: +[NSFont systemFontOfSize:(weight:)]
+ // - kMenu: +[NSFont menuFontOfSize:]
+ // - kToolTip: +[NSFont toolTipsFontOfSize:]
+ enum class SystemFontType { kGeneral, kMenu, kToolTip };
+
+ // Constructs a PlatformFontMac for a system-specified font of
+ // |system_font_type| type. For a non-system-specified font, use any other
+ // constructor.
+ explicit PlatformFontMac(SystemFontType system_font_type);
+
+ // Constructs a PlatformFontMac for containing the NSFont* |native_font|. Do
+ // not call this for a system-specified font; use the |SystemFontType|
+ // constructor for that. |native_font| must not be null.
explicit PlatformFontMac(NativeFont native_font);
+
+ // Constructs a PlatformFontMac representing the font with name |font_name|
+ // and the size |font_size|. Do not call this for a system-specified font; use
+ // the |SystemFontType| constructor for that.
PlatformFontMac(const std::string& font_name,
int font_size);
+ // Constructs a PlatformFontMac representing the font specified by |typeface|
+ // and the size |font_size_pixels|. Do not call this for a system-specified
+ // font; use the |SystemFontType| constructor for that.
PlatformFontMac(sk_sp<SkTypeface> typeface,
int font_size_pixels,
const base::Optional<FontRenderParams>& params);
@@ -41,35 +62,44 @@ class PlatformFontMac : public PlatformFont {
NativeFont GetNativeFont() const override;
sk_sp<SkTypeface> GetNativeSkTypeface() const override;
+ // A utility function to get the weight of an NSFont. Used by the unit test.
+ static Font::Weight GetFontWeightFromNSFontForTesting(NSFont* font);
+
private:
- PlatformFontMac(const std::string& font_name,
- int font_size,
- int font_style,
- Font::Weight font_weight);
+ struct FontSpec {
+ std::string name; // Corresponds to -[NSFont fontFamily].
+ int size;
+ int style;
+ Font::Weight weight;
+ };
PlatformFontMac(NativeFont font,
- const std::string& font_name,
- int font_size,
- int font_style,
- Font::Weight font_weight);
+ base::Optional<SystemFontType> system_font_type);
+
+ PlatformFontMac(NativeFont font,
+ base::Optional<SystemFontType> system_font_type,
+ FontSpec spec);
~PlatformFontMac() override;
- // Calculates and caches the font metrics and inits |render_params_|.
+ // Calculates and caches the font metrics and initializes |render_params_|.
void CalculateMetricsAndInitRenderParams();
+ // Returns an autoreleased NSFont created with the passed-in specifications.
+ NSFont* NSFontWithSpec(FontSpec font_spec) const;
+
// The NSFont instance for this object. If this object was constructed from an
// NSFont instance, this holds that NSFont instance. Otherwise this NSFont
// instance is constructed from the name, size, and style. If there is no
// active font that matched those criteria a default font is used.
base::scoped_nsobject<NSFont> native_font_;
- // The name/size/style trio that specify the font. Initialized in the
- // constructors.
- const std::string font_name_; // Corresponds to -[NSFont fontFamily].
- const int font_size_;
- const int font_style_;
- const Font::Weight font_weight_;
+ // If the font is a system font, and if so, what kind.
+ const base::Optional<SystemFontType> system_font_type_;
+
+ // The name/size/style/weight quartet that specify the font. Initialized in
+ // the constructors.
+ const FontSpec font_spec_;
// Cached metrics, generated in CalculateMetrics().
int height_;
diff --git a/chromium/ui/gfx/platform_font_mac.mm b/chromium/ui/gfx/platform_font_mac.mm
index be3ac0b03ed..fc6047806e3 100644
--- a/chromium/ui/gfx/platform_font_mac.mm
+++ b/chromium/ui/gfx/platform_font_mac.mm
@@ -5,12 +5,15 @@
#include "ui/gfx/platform_font_mac.h"
#include <cmath>
+#include <set>
#include <Cocoa/Cocoa.h>
+#include "base/bit_cast.h"
#import "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#import "base/mac/scoped_nsobject.h"
+#include "base/no_destructor.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/ports/SkTypeface_mac.h"
@@ -20,62 +23,14 @@
namespace gfx {
-namespace {
-
-// How to get from NORMAL weight to a fine-grained font weight using calls to
-// -[NSFontManager convertWeight:(BOOL)upFlag ofFont:(NSFont)].
-struct WeightSolver {
- int steps_up; // Times to call with upFlag:YES.
- int steps_down; // Times to call with upFlag:NO.
- // Either NORMAL or BOLD: whether to set the NSBoldFontMask symbolic trait.
- Font::Weight nearest;
-};
+using Weight = Font::Weight;
-// Solve changes to the font weight according to the following table, from
-// https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
-// 1. ultralight | none
-// 2. thin | W1. ultralight
-// 3. light, extralight | W2. extralight
-// 4. book | W3. light
-// 5. regular, plain, display, roman | W4. semilight
-// 6. medium | W5. medium
-// 7. demi, demibold | none
-// 8. semi, semibold | W6. semibold
-// 9. bold | W7. bold
-// 10. extra, extrabold | W8. extrabold
-// 11. heavy, heavyface | none
-// 12. black, super | W9. ultrabold
-// 13. ultra, ultrablack, fat | none
-// 14. extrablack, obese, nord | none
-WeightSolver WeightChangeFromNormal(Font::Weight desired) {
- using Weight = Font::Weight;
- switch (desired) {
- case Weight::THIN:
- // It's weird, but to get LIGHT and THIN fonts, first go up a step.
- // Without this, the font stays stuck at NORMAL. See
- // PlatformFontMacTest, FontWeightAPIConsistency.
- return {1, 3, Weight::NORMAL};
- case Weight::EXTRA_LIGHT:
- return {1, 2, Weight::NORMAL};
- case Weight::LIGHT:
- return {1, 1, Weight::NORMAL};
- case Weight::NORMAL:
- return {0, 0, Weight::NORMAL};
- case Weight::MEDIUM:
- return {1, 0, Weight::NORMAL};
- case Weight::SEMIBOLD:
- return {0, 1, Weight::BOLD};
- case Weight::BOLD:
- return {0, 0, Weight::BOLD};
- case Weight::EXTRA_BOLD:
- return {1, 0, Weight::BOLD};
- case Weight::BLACK:
- return {3, 0, Weight::BOLD}; // Skip row 12.
- case Weight::INVALID:
- return {0, 0, Weight::NORMAL};
- }
+extern "C" {
+bool CTFontDescriptorIsSystemUIFont(CTFontDescriptorRef);
}
+namespace {
+
// Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont
// does not support it as a trait.
int GetFontStyleFromNSFont(NSFont* font) {
@@ -86,36 +41,66 @@ int GetFontStyleFromNSFont(NSFont* font) {
return font_style;
}
-// Returns the Font weight for |font|.
-Font::Weight GetFontWeightFromNSFont(NSFont* font) {
+// Returns the Font::Weight for |font|.
+Weight GetFontWeightFromNSFont(NSFont* font) {
DCHECK(font);
// Map CoreText weights in a manner similar to ct_weight_to_fontstyle() from
- // SkFontHost_mac.cpp, but adjust for MEDIUM so that the San Francisco's
- // custom MEDIUM weight can be picked out. San Francisco has weights:
- // [0.23, 0.23, 0.3, 0.4, 0.56, 0.62, 0.62, ...] (no thin weights).
- // See PlatformFontMacTest.FontWeightAPIConsistency for details.
- // Note that the table Skia uses is also determined by experiment.
+ // SkFontHost_mac.cpp, but adjusted for the weights actually used by the
+ // system fonts. See PlatformFontMacTest.FontWeightAPIConsistency for details.
+ // macOS uses specific float values in its constants, but individual fonts can
+ // and do specify arbitrary values in the -1.0 to 1.0 range. Therefore, to
+ // accomodate that, and to avoid float comparison issues, use ranges.
constexpr struct {
- CGFloat ct_weight;
- Font::Weight gfx_weight;
+ // A range of CoreText weights.
+ CGFloat weight_lower;
+ CGFloat weight_upper;
+ Weight gfx_weight;
} weight_map[] = {
- // Missing: Apple "ultralight".
- {-0.70, Font::Weight::THIN},
- {-0.50, Font::Weight::EXTRA_LIGHT},
- {-0.23, Font::Weight::LIGHT},
- {0.00, Font::Weight::NORMAL},
- {0.23, Font::Weight::MEDIUM}, // Note: adjusted from 0.20 vs Skia.
- // Missing: Apple "demibold".
- {0.30, Font::Weight::SEMIBOLD},
- {0.40, Font::Weight::BOLD},
- {0.60, Font::Weight::EXTRA_BOLD},
- // Missing: Apple "heavyface".
- // Values will be capped to BLACK (this entry is here for consistency).
- {0.80, Font::Weight::BLACK},
- // Missing: Apple "ultrablack".
- // Missing: Apple "extrablack".
+ // NSFontWeight constants introduced in 10.11:
+ // NSFontWeightUltraLight: -0.80
+ // NSFontWeightThin: -0.60
+ // NSFontWeightLight: -0.40
+ // NSFontWeightRegular: 0.0
+ // NSFontWeightMedium: 0.23
+ // NSFontWeightSemibold: 0.30
+ // NSFontWeightBold: 0.40
+ // NSFontWeightHeavy: 0.56
+ // NSFontWeightBlack: 0.62
+ //
+ // Actual system font weights:
+ // 10.10:
+ // .HelveticaNeueDeskInterface-UltraLightP2: -0.80
+ // .HelveticaNeueDeskInterface-Thin: -0.50
+ // .HelveticaNeueDeskInterface-Light: -0.425
+ // .HelveticaNeueDeskInterface-Regular: 0.0
+ // .HelveticaNeueDeskInterface-MediumP4: 0.23
+ // .HelveticaNeueDeskInterface-Bold (if requested as semibold): 0.24
+ // .HelveticaNeueDeskInterface-Bold (if requested as bold): 0.4
+ // .HelveticaNeueDeskInterface-Heavy (if requested as heavy): 0.576
+ // .HelveticaNeueDeskInterface-Heavy (if requested as black): 0.662
+ // 10.11-:
+ // .AppleSystemUIFontUltraLight: -0.80
+ // .AppleSystemUIFontThin: -0.60
+ // .AppleSystemUIFontLight: -0.40
+ // .AppleSystemUIFont: 0.0
+ // .AppleSystemUIFontMedium: 0.23
+ // .AppleSystemUIFontDemi: 0.30
+ // .AppleSystemUIFontBold (10.11): 0.40
+ // .AppleSystemUIFontEmphasized (10.12-): 0.40
+ // .AppleSystemUIFontHeavy: 0.56
+ // .AppleSystemUIFontBlack: 0.62
+ {-1.0, -0.70, Weight::THIN}, // NSFontWeightUltraLight
+ {-0.70, -0.45, Weight::EXTRA_LIGHT}, // NSFontWeightThin
+ {-0.45, -0.10, Weight::LIGHT}, // NSFontWeightLight
+ {-0.10, 0.10, Weight::NORMAL}, // NSFontWeightRegular
+ {0.10, 0.27, Weight::MEDIUM}, // NSFontWeightMedium
+ {0.27, 0.35, Weight::SEMIBOLD}, // NSFontWeightSemibold
+ {0.35, 0.50, Weight::BOLD}, // NSFontWeightBold
+ {0.50, 0.60, Weight::EXTRA_BOLD}, // NSFontWeightHeavy
+ {0.60, 1.0, Weight::BLACK}, // NSFontWeightBlack
};
+
base::ScopedCFTypeRef<CFDictionaryRef> traits(
CTFontCopyTraits(base::mac::NSToCFCast(font)));
DCHECK(traits);
@@ -123,62 +108,129 @@ Font::Weight GetFontWeightFromNSFont(NSFont* font) {
traits, kCTFontWeightTrait);
// A missing weight attribute just means 0 -> NORMAL.
if (!cf_weight)
- return Font::Weight::NORMAL;
-
- // Documentation is vague about what sized floating point type should be used.
- // However, numeric_limits::epsilon() for 64-bit types is too small to match
- // the above table, so use 32-bit float. Do not check for the success of
- // CFNumberGetValue(). CFNumberGetValue() returns false for *any* loss of
- // value, and a float is used here deliberately to coarsen the accuracy.
- // There's no guarantee that any particular value for the kCTFontWeightTrait
- // will be able to be accurately represented with a float.
- float weight_value;
- CFNumberGetValue(cf_weight, kCFNumberFloatType, &weight_value);
+ return Weight::NORMAL;
+
+ // The value of kCTFontWeightTrait empirically is a kCFNumberFloat64Type
+ // (double) on all tested versions of macOS. However, that doesn't really
+ // matter as only the first two decimal digits need to be tested. Do not check
+ // for the success of CFNumberGetValue() as it returns false for any loss of
+ // value and all that is needed here is two digits of accuracy.
+ CGFloat weight;
+ CFNumberGetValue(cf_weight, kCFNumberCGFloatType, &weight);
for (const auto& item : weight_map) {
- if (weight_value - item.ct_weight <= std::numeric_limits<float>::epsilon())
+ if (item.weight_lower <= weight && weight <= item.weight_upper)
return item.gfx_weight;
}
- return Font::Weight::BLACK;
+ return Weight::INVALID;
}
-// Returns an autoreleased NSFont created with the passed-in specifications.
-NSFont* NSFontWithSpec(const std::string& font_name,
- int font_size,
- int font_style,
- Font::Weight font_weight) {
- NSFontSymbolicTraits trait_bits = 0;
- // TODO(mboc): Add support for other weights as well.
- if (font_weight >= Font::Weight::BOLD)
- trait_bits |= NSFontBoldTrait;
- if (font_style & Font::ITALIC)
- trait_bits |= NSFontItalicTrait;
- // The Mac doesn't support underline as a font trait, so just drop it.
- // (Underlines must be added as an attribute on an NSAttributedString.)
- NSDictionary* traits = @{ NSFontSymbolicTrait : @(trait_bits) };
-
- NSDictionary* attrs = @{
- NSFontFamilyAttribute : base::SysUTF8ToNSString(font_name),
- NSFontTraitsAttribute : traits
- };
- NSFontDescriptor* descriptor =
- [NSFontDescriptor fontDescriptorWithFontAttributes:attrs];
- NSFont* font = [NSFont fontWithDescriptor:descriptor size:font_size];
- if (font)
- return font;
-
- // Make one fallback attempt by looking up via font name rather than font
- // family name.
- attrs = @{
- NSFontNameAttribute : base::SysUTF8ToNSString(font_name),
- NSFontTraitsAttribute : traits
- };
- descriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:attrs];
- return [NSFont fontWithDescriptor:descriptor size:font_size];
+// Converts a Font::Weight value to the corresponding NSFontWeight value.
+NSFontWeight ToNSFontWeight(Weight weight) {
+ if (@available(macOS 10.11, *)) {
+ switch (weight) {
+ case Weight::THIN:
+ return NSFontWeightUltraLight;
+ case Weight::EXTRA_LIGHT:
+ return NSFontWeightThin;
+ case Weight::LIGHT:
+ return NSFontWeightLight;
+ case Weight::INVALID:
+ case Weight::NORMAL:
+ return NSFontWeightRegular;
+ case Weight::MEDIUM:
+ return NSFontWeightMedium;
+ case Weight::SEMIBOLD:
+ return NSFontWeightSemibold;
+ case Weight::BOLD:
+ return NSFontWeightBold;
+ case Weight::EXTRA_BOLD:
+ return NSFontWeightHeavy;
+ case Weight::BLACK:
+ return NSFontWeightBlack;
+ }
+ } else {
+ // See third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm.
+ uint64_t int_value = 0;
+ switch (weight) {
+ case Weight::THIN:
+ int_value = 0xbfe99999a0000000; // NSFontWeightUltraLight;
+ break;
+ case Weight::EXTRA_LIGHT:
+ int_value = 0xbfe3333340000000; // NSFontWeightThin;
+ break;
+ case Weight::LIGHT:
+ int_value = 0xbfd99999a0000000; // NSFontWeightLight;
+ break;
+ case Weight::INVALID:
+ case Weight::NORMAL:
+ int_value = 0x0000000000000000; // NSFontWeightRegular;
+ break;
+ case Weight::MEDIUM:
+ int_value = 0x3fcd70a3e0000000; // NSFontWeightMedium;
+ break;
+ case Weight::SEMIBOLD:
+ int_value = 0x3fd3333340000000; // NSFontWeightSemibold;
+ break;
+ case Weight::BOLD:
+ int_value = 0x3fd99999a0000000; // NSFontWeightBold;
+ break;
+ case Weight::EXTRA_BOLD:
+ int_value = 0x3fe1eb8520000000; // NSFontWeightHeavy;
+ break;
+ case Weight::BLACK:
+ int_value = 0x3fe3d70a40000000; // NSFontWeightBlack;
+ break;
+ }
+
+ return bit_cast<CGFloat>(int_value);
+ }
}
-// Returns |font| or a default font if |font| is nil.
-NSFont* ValidateFont(NSFont* font) {
- return font ? font : [NSFont systemFontOfSize:[NSFont systemFontSize]];
+// Chromium uses the ISO-style, 9-value ladder of font weights (THIN-BLACK). The
+// new font API in macOS also uses these weights, though they are constants
+// defined in terms of CGFloat with values from -1.0 to 1.0.
+//
+// However, the old API used by the NSFontManager uses integer values on a
+// "scale of 0 to 15". These values are used in:
+//
+// -[NSFontManager availableMembersOfFontFamily:]
+// -[NSFontManager convertWeight:ofFont:]
+// -[NSFontManager fontWithFamily:traits:weight:size:]
+// -[NSFontManager weightOfFont:]
+//
+// Apple provides a chart of how the ISO values correspond:
+// https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
+// However, it's more complicated than that. A survey of fonts yields the
+// correspondence in this function, but the outliers imply that the ISO-style
+// weight is more along the lines of "weight role within the font family" vs
+// this number which is more like "how weighty is this font compared to all
+// other fonts".
+//
+// These numbers can't really be forced to line up; different fonts disagree on
+// how to map them. This function mostly follows the documented chart as
+// inspired by actual fonts, and should be good enough.
+NSInteger ToNSFontManagerWeight(Weight weight) {
+ switch (weight) {
+ case Weight::THIN:
+ return 2;
+ case Weight::EXTRA_LIGHT:
+ return 3;
+ case Weight::LIGHT:
+ return 4;
+ case Weight::INVALID:
+ case Weight::NORMAL:
+ return 5;
+ case Weight::MEDIUM:
+ return 6;
+ case Weight::SEMIBOLD:
+ return 8;
+ case Weight::BOLD:
+ return 9;
+ case Weight::EXTRA_BOLD:
+ return 10;
+ case Weight::BLACK:
+ return 11;
+ }
}
std::string GetFamilyNameFromTypeface(sk_sp<SkTypeface> typeface) {
@@ -187,88 +239,155 @@ std::string GetFamilyNameFromTypeface(sk_sp<SkTypeface> typeface) {
return family.c_str();
}
+NSFont* SystemFontForConstructorOfType(PlatformFontMac::SystemFontType type) {
+ switch (type) {
+ case PlatformFontMac::SystemFontType::kGeneral:
+ return [NSFont systemFontOfSize:[NSFont systemFontSize]];
+ case PlatformFontMac::SystemFontType::kMenu:
+ return [NSFont menuFontOfSize:0];
+ case PlatformFontMac::SystemFontType::kToolTip:
+ return [NSFont toolTipsFontOfSize:0];
+ }
+}
+
+base::Optional<PlatformFontMac::SystemFontType>
+SystemFontTypeFromUndocumentedCTFontRefInternals(CTFontRef font) {
+ // The macOS APIs can't reliably derive one font from another. That's why for
+ // non-system fonts PlatformFontMac::DeriveFont() uses the family name of the
+ // font to find look up new fonts from scratch, and why, for system fonts, it
+ // uses the system font APIs to generate new system fonts.
+ //
+ // Skia's font handling assumes that given a font object, new fonts can be
+ // derived from it. That's absolutely not true on the Mac. However, this needs
+ // to be fixed, and a rewrite of how Skia handles fonts is not on the table.
+ //
+ // Therefore this sad hack. If Skia provides an SkTypeface, dig into the
+ // undocumented bowels of CoreText and magically determine if the font is a
+ // system font. This allows PlatformFontMac to correctly derive variants of
+ // the provided font.
+ //
+ // TODO(avi, etienneb): Figure out this font stuff.
+ base::ScopedCFTypeRef<CTFontDescriptorRef> descriptor(
+ CTFontCopyFontDescriptor(font));
+ if (CTFontDescriptorIsSystemUIFont(descriptor.get())) {
+ // Assume it's the standard system font. The fact that this much is known is
+ // enough.
+ return PlatformFontMac::SystemFontType::kGeneral;
+ } else {
+ return base::nullopt;
+ }
+}
+
+#if DCHECK_IS_ON()
+
+const std::set<std::string>& SystemFontNames() {
+ static const base::NoDestructor<std::set<std::string>> names([] {
+ std::set<std::string> names;
+ names.insert(base::SysNSStringToUTF8(
+ [NSFont systemFontOfSize:[NSFont systemFontSize]].familyName));
+ names.insert(base::SysNSStringToUTF8([NSFont menuFontOfSize:0].familyName));
+ names.insert(
+ base::SysNSStringToUTF8([NSFont toolTipsFontOfSize:0].familyName));
+ return names;
+ }());
+
+ return *names;
+}
+
+#endif // DCHECK_IS_ON()
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
// PlatformFontMac, public:
-PlatformFontMac::PlatformFontMac()
- : PlatformFontMac([NSFont systemFontOfSize:[NSFont systemFontSize]]) {
-}
+PlatformFontMac::PlatformFontMac(SystemFontType system_font_type)
+ : PlatformFontMac(SystemFontForConstructorOfType(system_font_type),
+ system_font_type) {}
PlatformFontMac::PlatformFontMac(NativeFont native_font)
- : PlatformFontMac(native_font,
- base::SysNSStringToUTF8([native_font familyName]),
- [native_font pointSize],
- GetFontStyleFromNSFont(native_font),
- GetFontWeightFromNSFont(native_font)) {
- DCHECK(native_font); // Null should not be passed to this constructor.
+ : PlatformFontMac(native_font, base::nullopt) {
+ DCHECK(native_font); // nil should not be passed to this constructor.
}
PlatformFontMac::PlatformFontMac(const std::string& font_name, int font_size)
- : PlatformFontMac(font_name,
- font_size,
- Font::NORMAL,
- Font::Weight::NORMAL) {}
+ : PlatformFontMac(
+ NSFontWithSpec({font_name, font_size, Font::NORMAL, Weight::NORMAL}),
+ base::nullopt,
+ {font_name, font_size, Font::NORMAL, Weight::NORMAL}) {}
PlatformFontMac::PlatformFontMac(sk_sp<SkTypeface> typeface,
int font_size_pixels,
const base::Optional<FontRenderParams>& params)
: PlatformFontMac(
base::mac::CFToNSCast(SkTypeface_GetCTFontRef(typeface.get())),
- GetFamilyNameFromTypeface(typeface),
- font_size_pixels,
- (typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
- FontWeightFromInt(typeface->fontStyle().weight())) {}
+ SystemFontTypeFromUndocumentedCTFontRefInternals(
+ SkTypeface_GetCTFontRef(typeface.get())),
+ {GetFamilyNameFromTypeface(typeface), font_size_pixels,
+ (typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
+ FontWeightFromInt(typeface->fontStyle().weight())}) {}
////////////////////////////////////////////////////////////////////////////////
// PlatformFontMac, PlatformFont implementation:
Font PlatformFontMac::DeriveFont(int size_delta,
int style,
- Font::Weight weight) const {
- // For some reason, creating fonts using the NSFontDescriptor API's seem to be
- // unreliable. Hence use the NSFontManager.
- NSFont* derived = native_font_;
- NSFontManager* font_manager = [NSFontManager sharedFontManager];
-
- if (weight != font_weight_) {
- // Find a font without any bold traits. Ideally, all bold traits are
- // removed here, but non-symbolic traits are read-only properties of a
- // particular set of glyphs. And attempting to "reset" the attribute with a
- // new font descriptor will lose internal properties that Mac decorates its
- // UI fonts with. E.g., solving the plans below from NORMAL result in a
- // CTFontDescriptor attribute entry of NSCTFontUIUsageAttribute in
- // CTFont{Regular,Medium,Demi,Emphasized,Heavy,Black}Usage. Attempting to
- // "solve" weights starting at other than NORMAL has unpredictable results.
- if (font_weight_ != Font::Weight::NORMAL)
- derived = [font_manager convertFont:derived toHaveTrait:NSUnboldFontMask];
- DLOG_IF(WARNING, GetFontWeightFromNSFont(derived) != Font::Weight::NORMAL)
- << "Deriving from a font with an internal unmodifiable weight.";
-
- WeightSolver plan = WeightChangeFromNormal(weight);
- if (plan.nearest == Font::Weight::BOLD)
- derived = [font_manager convertFont:derived toHaveTrait:NSBoldFontMask];
- for (int i = 0; i < plan.steps_up; ++i)
- derived = [font_manager convertWeight:YES ofFont:derived];
- for (int i = 0; i < plan.steps_down; ++i)
- derived = [font_manager convertWeight:NO ofFont:derived];
- }
-
- // Always apply the italic trait, even if the italic trait is not changing.
- // it's possible for a change in the weight to trigger the font to go italic.
- // This is due to an AppKit bug. See http://crbug.com/742261.
- if (style != font_style_ || weight != font_weight_) {
+ Weight weight) const {
+ // What doesn't work?
+ //
+ // For all fonts, -[NSFontManager convertWeight:ofFont:] will reliably
+ // misbehave, skipping over particular weights of fonts, refusing to go
+ // lighter than regular unless you go heavier first, and in earlier versions
+ // of the system would accidentally introduce italic fonts when changing
+ // weights from a non-italic instance.
+ //
+ // For system fonts, -[NSFontManager convertFont:to(Not)HaveTrait:], if used
+ // to change weight, will sometimes switch to a compatibility system font that
+ // does not have all the weights available.
+ //
+ // For system fonts, the most reliable call to use is +[NSFont
+ // systemFontOfSize:weight:]. This uses the new-style NSFontWeight which maps
+ // perfectly to the ISO weights that Chromium uses. For non-system fonts,
+ // -[NSFontManager fontWithFamily:traits:weight:size:] is the only reasonable
+ // way to query fonts with more granularity than bold/non-bold short of
+ // walking the font family and querying their kCTFontWeightTrait values. Font
+ // descriptors hold promise but querying using them often fails to find fonts
+ // that match; hopefully their matching abilities will improve in future
+ // versions of the macOS.
+
+ if (system_font_type_ == SystemFontType::kGeneral) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+ // +[NSFont systemFontOfSize:weight:] is declared as available on 10.11+,
+ // but actually it is there and works on 10.10.
+ NSFont* derived = [NSFont systemFontOfSize:font_spec_.size + size_delta
+ weight:ToNSFontWeight(weight)];
+#pragma clang diagnostic pop
NSFontTraitMask italic_trait_mask =
(style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask;
- derived = [font_manager convertFont:derived toHaveTrait:italic_trait_mask];
+ derived = [[NSFontManager sharedFontManager] convertFont:derived
+ toHaveTrait:italic_trait_mask];
+
+ return Font(new PlatformFontMac(
+ derived, SystemFontType::kGeneral,
+ {font_spec_.name, font_spec_.size + size_delta, style, weight}));
+ } else if (system_font_type_ == SystemFontType::kMenu) {
+ NSFont* derived = [NSFont menuFontOfSize:font_spec_.size + size_delta];
+ return Font(new PlatformFontMac(
+ derived, SystemFontType::kMenu,
+ {font_spec_.name, font_spec_.size + size_delta, style, weight}));
+ } else if (system_font_type_ == SystemFontType::kToolTip) {
+ NSFont* derived = [NSFont toolTipsFontOfSize:font_spec_.size + size_delta];
+ return Font(new PlatformFontMac(
+ derived, SystemFontType::kToolTip,
+ {font_spec_.name, font_spec_.size + size_delta, style, weight}));
+ } else {
+ NSFont* derived = NSFontWithSpec(
+ {font_spec_.name, font_spec_.size + size_delta, style, weight});
+ return Font(new PlatformFontMac(
+ derived, base::nullopt,
+ {font_spec_.name, font_spec_.size + size_delta, style, weight}));
}
-
- if (size_delta != 0)
- derived = [font_manager convertFont:derived toSize:font_size_ + size_delta];
-
- return Font(new PlatformFontMac(derived, font_name_, font_size_ + size_delta,
- style, weight));
}
int PlatformFontMac::GetHeight() {
@@ -301,15 +420,15 @@ int PlatformFontMac::GetExpectedTextWidth(int length) {
}
int PlatformFontMac::GetStyle() const {
- return font_style_;
+ return font_spec_.style;
}
-Font::Weight PlatformFontMac::GetWeight() const {
- return font_weight_;
+Weight PlatformFontMac::GetWeight() const {
+ return font_spec_.weight;
}
const std::string& PlatformFontMac::GetFontName() const {
- return font_name_;
+ return font_spec_.name;
}
std::string PlatformFontMac::GetActualFontName() const {
@@ -317,7 +436,7 @@ std::string PlatformFontMac::GetActualFontName() const {
}
int PlatformFontMac::GetFontSize() const {
- return font_size_;
+ return font_spec_.size;
}
const FontRenderParams& PlatformFontMac::GetFontRenderParams() {
@@ -332,30 +451,37 @@ sk_sp<SkTypeface> PlatformFontMac::GetNativeSkTypeface() const {
return SkMakeTypefaceFromCTFont(base::mac::NSToCFCast(GetNativeFont()));
}
+// static
+Weight PlatformFontMac::GetFontWeightFromNSFontForTesting(NSFont* font) {
+ return GetFontWeightFromNSFont(font);
+}
+
////////////////////////////////////////////////////////////////////////////////
// PlatformFontMac, private:
-PlatformFontMac::PlatformFontMac(const std::string& font_name,
- int font_size,
- int font_style,
- Font::Weight font_weight)
+PlatformFontMac::PlatformFontMac(
+ NativeFont font,
+ base::Optional<SystemFontType> system_font_type)
: PlatformFontMac(
- NSFontWithSpec(font_name, font_size, font_style, font_weight),
- font_name,
- font_size,
- font_style,
- font_weight) {}
-
-PlatformFontMac::PlatformFontMac(NativeFont font,
- const std::string& font_name,
- int font_size,
- int font_style,
- Font::Weight font_weight)
- : native_font_([ValidateFont(font) retain]),
- font_name_(font_name),
- font_size_(font_size),
- font_style_(font_style),
- font_weight_(font_weight) {
+ font,
+ system_font_type,
+ {base::SysNSStringToUTF8([font familyName]), [font pointSize],
+ GetFontStyleFromNSFont(font), GetFontWeightFromNSFont(font)}) {}
+
+PlatformFontMac::PlatformFontMac(
+ NativeFont font,
+ base::Optional<SystemFontType> system_font_type,
+ FontSpec spec)
+ : native_font_([font retain]),
+ system_font_type_(system_font_type),
+ font_spec_(spec) {
+#if DCHECK_IS_ON()
+ DCHECK(system_font_type.has_value() ||
+ SystemFontNames().count(spec.name) == 0)
+ << "Do not pass a system font (" << spec.name << ") to PlatformFontMac; "
+ << "use the SystemFontType constructor. Extend the SystemFontType enum "
+ << "if necessary.";
+#endif // DCHECK_IS_ON()
CalculateMetricsAndInitRenderParams();
}
@@ -378,11 +504,71 @@ void PlatformFontMac::CalculateMetricsAndInitRenderParams() {
height_ = ceil(ascent_ + std::abs([font descender]) + [font leading]);
FontRenderParamsQuery query;
- query.families.push_back(font_name_);
- query.pixel_size = font_size_;
- query.style = font_style_;
- query.weight = font_weight_;
- render_params_ = gfx::GetFontRenderParams(query, NULL);
+ query.families.push_back(font_spec_.name);
+ query.pixel_size = font_spec_.size;
+ query.style = font_spec_.style;
+ query.weight = font_spec_.weight;
+ render_params_ = gfx::GetFontRenderParams(query, nullptr);
+}
+
+NSFont* PlatformFontMac::NSFontWithSpec(FontSpec font_spec) const {
+ // One might think that a font descriptor with the NSFontWeightTrait/
+ // kCTFontWeightTrait trait could be used to look up a font with a specific
+ // weight. That doesn't work, though. You can ask a font for its weight, but
+ // you can't use weight to query for the font.
+ //
+ // The way that does work is to use the old-style integer weight API.
+
+ NSFontManager* font_manager = [NSFontManager sharedFontManager];
+
+ NSFontTraitMask traits = 0;
+ if (font_spec.style & Font::ITALIC)
+ traits |= NSItalicFontMask;
+ // The Mac doesn't support underline as a font trait, so just drop it.
+ // (Underlines must be added as an attribute on an NSAttributedString.) Do not
+ // add NSBoldFontMask here; if it is added then the weight parameter below
+ // will be ignored.
+
+ NSFont* font =
+ [font_manager fontWithFamily:base::SysUTF8ToNSString(font_spec.name)
+ traits:traits
+ weight:ToNSFontManagerWeight(font_spec.weight)
+ size:font_spec.size];
+ if (font)
+ return font;
+
+ // Make one fallback attempt by looking up via font name rather than font
+ // family name. With this API, the available granularity of font weight is
+ // bold/not-bold, but that's what's available.
+ NSFontSymbolicTraits trait_bits = 0;
+ if (font_spec.weight >= Weight::BOLD)
+ trait_bits |= NSFontBoldTrait;
+ if (font_spec.style & Font::ITALIC)
+ trait_bits |= NSFontItalicTrait;
+
+ NSDictionary* attrs = @{
+ NSFontNameAttribute : base::SysUTF8ToNSString(font_spec.name),
+ NSFontTraitsAttribute : @{NSFontSymbolicTrait : @(trait_bits)},
+ };
+ NSFontDescriptor* descriptor =
+ [NSFontDescriptor fontDescriptorWithFontAttributes:attrs];
+
+ font = [NSFont fontWithDescriptor:descriptor size:font_spec.size];
+ if (font)
+ return font;
+
+ // If that doesn't find a font, whip up a system font to stand in for the
+ // specified font.
+ if (@available(macOS 10.11, *)) {
+ font = [NSFont systemFontOfSize:font_spec.size
+ weight:ToNSFontWeight(font_spec.weight)];
+ return [font_manager convertFont:font toHaveTrait:traits];
+ } else {
+ font = [NSFont systemFontOfSize:font_spec.size];
+ if (font_spec.weight >= Weight::BOLD)
+ traits |= NSBoldFontMask;
+ return [font_manager convertFont:font toHaveTrait:traits];
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -390,12 +576,12 @@ void PlatformFontMac::CalculateMetricsAndInitRenderParams() {
// static
PlatformFont* PlatformFont::CreateDefault() {
- return new PlatformFontMac;
+ return new PlatformFontMac(PlatformFontMac::SystemFontType::kGeneral);
}
// static
PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
- return new PlatformFontMac(ValidateFont(native_font));
+ return new PlatformFontMac(native_font);
}
// static
diff --git a/chromium/ui/gfx/platform_font_mac_unittest.mm b/chromium/ui/gfx/platform_font_mac_unittest.mm
index 8df42d81578..0f812e9e70b 100644
--- a/chromium/ui/gfx/platform_font_mac_unittest.mm
+++ b/chromium/ui/gfx/platform_font_mac_unittest.mm
@@ -7,277 +7,187 @@
#include <Cocoa/Cocoa.h>
#include <stddef.h>
+#import "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/font.h"
-// TODO(tapted): Remove gfx:: prefixes.
namespace gfx {
+using Weight = Font::Weight;
+
TEST(PlatformFontMacTest, DeriveFont) {
- // Use a base font that support all traits.
- gfx::Font base_font("Helvetica", 13);
+ // |weight_tri| is either -1, 0, or 1 meaning "light", "normal", or "bold".
+ auto CheckExpected = [](const Font& font, int weight_tri, bool isItalic) {
+ base::ScopedCFTypeRef<CFDictionaryRef> traits(
+ CTFontCopyTraits(base::mac::NSToCFCast(font.GetNativeFont())));
+ DCHECK(traits);
+
+ CFNumberRef cf_slant = base::mac::GetValueFromDictionary<CFNumberRef>(
+ traits, kCTFontSlantTrait);
+ CGFloat slant;
+ CFNumberGetValue(cf_slant, kCFNumberCGFloatType, &slant);
+ if (isItalic)
+ EXPECT_GT(slant, 0);
+ else
+ EXPECT_EQ(slant, 0);
+
+ CFNumberRef cf_weight = base::mac::GetValueFromDictionary<CFNumberRef>(
+ traits, kCTFontWeightTrait);
+ CGFloat weight;
+ CFNumberGetValue(cf_weight, kCFNumberCGFloatType, &weight);
+ if (weight_tri < 0)
+ EXPECT_LT(weight, 0);
+ else if (weight_tri == 0)
+ EXPECT_EQ(weight, 0);
+ else
+ EXPECT_GT(weight, 0);
+ };
- // Bold
- gfx::Font bold_font(
- base_font.Derive(0, gfx::Font::NORMAL, gfx::Font::Weight::BOLD));
- NSFontTraitMask traits = [[NSFontManager sharedFontManager]
- traitsOfFont:bold_font.GetNativeFont()];
- EXPECT_EQ(NSBoldFontMask, traits);
+ // Use a base font that support all traits.
+ Font base_font("Helvetica", 13);
+ {
+ SCOPED_TRACE("plain font");
+ CheckExpected(base_font, 0, false);
+ }
// Italic
- gfx::Font italic_font(
- base_font.Derive(0, gfx::Font::ITALIC, gfx::Font::Weight::NORMAL));
- traits = [[NSFontManager sharedFontManager]
- traitsOfFont:italic_font.GetNativeFont()];
- EXPECT_EQ(NSItalicFontMask, traits);
+ Font italic_font(base_font.Derive(0, Font::ITALIC, Weight::NORMAL));
+ {
+ SCOPED_TRACE("italic font");
+ CheckExpected(italic_font, 0, true);
+ }
+
+ // Bold
+ Font bold_font(base_font.Derive(0, Font::NORMAL, Weight::BOLD));
+ {
+ SCOPED_TRACE("bold font");
+ CheckExpected(bold_font, 1, false);
+ }
// Bold italic
- gfx::Font bold_italic_font(
- base_font.Derive(0, gfx::Font::ITALIC, gfx::Font::Weight::BOLD));
- traits = [[NSFontManager sharedFontManager]
- traitsOfFont:bold_italic_font.GetNativeFont()];
- EXPECT_EQ(static_cast<NSFontTraitMask>(NSBoldFontMask | NSItalicFontMask),
- traits);
+ Font bold_italic_font(base_font.Derive(0, Font::ITALIC, Weight::BOLD));
+ {
+ SCOPED_TRACE("bold italic font");
+ CheckExpected(bold_italic_font, 1, true);
+ }
+
+ // Non-existent thin will return the closest weight, light
+ Font thin_font(base_font.Derive(0, Font::NORMAL, Weight::THIN));
+ {
+ SCOPED_TRACE("thin font");
+ CheckExpected(thin_font, -1, false);
+ }
+
+ // Non-existent black will return the closest weight, bold
+ Font black_font(base_font.Derive(0, Font::NORMAL, Weight::BLACK));
+ {
+ SCOPED_TRACE("black font");
+ CheckExpected(black_font, 1, false);
+ }
}
TEST(PlatformFontMacTest, DeriveFontUnderline) {
// Create a default font.
- gfx::Font base_font;
+ Font base_font;
// Make the font underlined.
- gfx::Font derived_font(base_font.Derive(
- 0, base_font.GetStyle() | gfx::Font::UNDERLINE, base_font.GetWeight()));
+ Font derived_font(base_font.Derive(0, base_font.GetStyle() | Font::UNDERLINE,
+ base_font.GetWeight()));
// Validate the derived font properties against its native font instance.
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:derived_font.GetNativeFont()];
- gfx::Font::Weight actual_weight = (traits & NSFontBoldTrait)
- ? gfx::Font::Weight::BOLD
- : gfx::Font::Weight::NORMAL;
+ Weight actual_weight =
+ (traits & NSFontBoldTrait) ? Weight::BOLD : Weight::NORMAL;
- int actual_style = gfx::Font::UNDERLINE;
+ int actual_style = Font::UNDERLINE;
if (traits & NSFontItalicTrait)
- actual_style |= gfx::Font::ITALIC;
+ actual_style |= Font::ITALIC;
- EXPECT_TRUE(derived_font.GetStyle() & gfx::Font::UNDERLINE);
+ EXPECT_TRUE(derived_font.GetStyle() & Font::UNDERLINE);
EXPECT_EQ(derived_font.GetStyle(), actual_style);
EXPECT_EQ(derived_font.GetWeight(), actual_weight);
}
-// Tests internal methods for extracting gfx::Font properties from the
+// Tests internal methods for extracting Font properties from the
// underlying CTFont representation.
TEST(PlatformFontMacTest, ConstructFromNativeFont) {
+ Font light_font([NSFont fontWithName:@"Helvetica-Light" size:12]);
+ EXPECT_EQ(12, light_font.GetFontSize());
+ EXPECT_EQ("Helvetica", light_font.GetFontName());
+ EXPECT_EQ(Font::NORMAL, light_font.GetStyle());
+ EXPECT_EQ(Weight::LIGHT, light_font.GetWeight());
+
+ Font light_italic_font([NSFont fontWithName:@"Helvetica-LightOblique"
+ size:14]);
+ EXPECT_EQ(14, light_italic_font.GetFontSize());
+ EXPECT_EQ("Helvetica", light_italic_font.GetFontName());
+ EXPECT_EQ(Font::ITALIC, light_italic_font.GetStyle());
+ EXPECT_EQ(Weight::LIGHT, light_italic_font.GetWeight());
+
Font normal_font([NSFont fontWithName:@"Helvetica" size:12]);
EXPECT_EQ(12, normal_font.GetFontSize());
EXPECT_EQ("Helvetica", normal_font.GetFontName());
EXPECT_EQ(Font::NORMAL, normal_font.GetStyle());
- EXPECT_EQ(Font::Weight::NORMAL, normal_font.GetWeight());
-
- Font bold_font([NSFont fontWithName:@"Helvetica-Bold" size:14]);
- EXPECT_EQ(14, bold_font.GetFontSize());
- EXPECT_EQ("Helvetica", bold_font.GetFontName());
- EXPECT_EQ(Font::NORMAL, bold_font.GetStyle());
- EXPECT_EQ(Font::Weight::BOLD, bold_font.GetWeight());
+ EXPECT_EQ(Weight::NORMAL, normal_font.GetWeight());
Font italic_font([NSFont fontWithName:@"Helvetica-Oblique" size:14]);
EXPECT_EQ(14, italic_font.GetFontSize());
EXPECT_EQ("Helvetica", italic_font.GetFontName());
EXPECT_EQ(Font::ITALIC, italic_font.GetStyle());
- EXPECT_EQ(Font::Weight::NORMAL, italic_font.GetWeight());
+ EXPECT_EQ(Weight::NORMAL, italic_font.GetWeight());
+
+ Font bold_font([NSFont fontWithName:@"Helvetica-Bold" size:12]);
+ EXPECT_EQ(12, bold_font.GetFontSize());
+ EXPECT_EQ("Helvetica", bold_font.GetFontName());
+ EXPECT_EQ(Font::NORMAL, bold_font.GetStyle());
+ EXPECT_EQ(Weight::BOLD, bold_font.GetWeight());
Font bold_italic_font([NSFont fontWithName:@"Helvetica-BoldOblique" size:14]);
EXPECT_EQ(14, bold_italic_font.GetFontSize());
EXPECT_EQ("Helvetica", bold_italic_font.GetFontName());
EXPECT_EQ(Font::ITALIC, bold_italic_font.GetStyle());
- EXPECT_EQ(Font::Weight::BOLD, bold_italic_font.GetWeight());
-}
-
-// Specific test for the mapping from the NSFont weight API to gfx::Font::Weight
-// values.
-TEST(PlatformFontMacTest, FontWeightAPIConsistency) {
- // Vanilla Helvetica only has bold and normal, so use a system font.
- NSFont* ns_font = [NSFont systemFontOfSize:13];
- NSFontManager* manager = [NSFontManager sharedFontManager];
-
- // -[NSFontManager convertWeight:ofFont] supposedly steps the font up and down
- // in weight values according to a table at
- // https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
- // Apple Terminology | ISO Equivalent
- // 1. ultralight | none
- // 2. thin | W1. ultralight
- // 3. light, extralight | W2. extralight
- // 4. book | W3. light
- // 5. regular, plain, display, roman | W4. semilight
- // 6. medium | W5. medium
- // 7. demi, demibold | none
- // 8. semi, semibold | W6. semibold
- // 9. bold | W7. bold
- // 10. extra, extrabold | W8. extrabold
- // 11. heavy, heavyface | none
- // 12. black, super | W9. ultrabold
- // 13. ultra, ultrablack, fat | none
- // 14. extrablack, obese, nord | none
- EXPECT_EQ(Font::Weight::NORMAL, Font(ns_font).GetWeight()); // Row 5.
-
- // Ensure the Bold "symbolic" trait from the NSFont traits API maps correctly
- // to the weight (non-symbolic) trait from the CTFont API.
- NSFont* bold_ns_font =
- [manager convertFont:ns_font toHaveTrait:NSFontBoldTrait];
- Font bold_font(bold_ns_font);
- EXPECT_EQ(Font::Weight::BOLD, bold_font.GetWeight());
-
- // No thin fonts on the lower rows of the table for San Francisco or earlier
- // system fonts.
- BOOL down = NO;
- ns_font = [NSFont systemFontOfSize:13];
- for (int row = 4; row > 0; --row) {
- SCOPED_TRACE(testing::Message() << "Row: " << row);
- ns_font = [manager convertWeight:down ofFont:ns_font];
- EXPECT_EQ(Font::Weight::NORMAL, Font(ns_font).GetWeight());
- }
-
- BOOL up = YES;
- // That is... unless we first go up by one and then down. A LIGHT and a THIN
- // font reveal themselves somehow. Only tested on 10.12.
- if (base::mac::IsAtLeastOS10_12()) {
- ns_font = [NSFont systemFontOfSize:13];
- ns_font = [manager convertWeight:up ofFont:ns_font];
- ns_font = [manager convertWeight:down ofFont:ns_font];
- EXPECT_EQ(Font::Weight::LIGHT, Font(ns_font).GetWeight());
- ns_font = [manager convertWeight:down ofFont:ns_font];
- EXPECT_EQ(Font::Weight::THIN, Font(ns_font).GetWeight());
- }
-
- ns_font = [NSFont systemFontOfSize:13];
-
- if (base::mac::IsOS10_11()) {
- // On 10.11 the API jumps to BOLD, but has heavier weights as well.
- ns_font = [manager convertWeight:up ofFont:ns_font];
- EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight());
- ns_font = [manager convertWeight:up ofFont:ns_font];
- EXPECT_EQ(Font::Weight::EXTRA_BOLD, Font(ns_font).GetWeight());
- ns_font = [manager convertWeight:up ofFont:ns_font];
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight());
- return;
- }
-
- // Each typeface maps weight notches differently, and the weight is actually a
- // floating point value that may not map directly to a gfx::Font::Weight. For
- // example San Francisco on macOS 10.12 goes up from 0 in the sequence: [0.23,
- // 0.23, 0.3, 0.4, 0.56, 0.62, 0.62, ...] and has no "thin" weights. But also
- // iterating over weights does weird stuff sometimes - before macOS 10.15,
- // occasionally the font goes italic, but going up another step goes back to
- // non-italic, at a heavier weight.
-
- // NSCTFontUIUsageAttribute = CTFontMediumUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.23.
- EXPECT_EQ(Font::Weight::MEDIUM, Font(ns_font).GetWeight()); // Row 6.
-
- // 10.15 fixed the bug where the step up from medium created a medium italic.
- if (base::mac::IsAtMostOS10_14()) {
- // Goes italic: NSCTFontUIUsageAttribute = CTFontMediumItalicUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.23.
- EXPECT_EQ(Font::Weight::MEDIUM, Font(ns_font).GetWeight()); // Row 7.
- }
-
- // NSCTFontUIUsageAttribute = CTFontDemiUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.3.
- if (base::mac::IsOS10_10()) {
- // 10.10 is Helvetica Neue. It only has NORMAL, MEDIUM, BOLD and BLACK.
- EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight()); // Row 8.
- } else {
- EXPECT_EQ(Font::Weight::SEMIBOLD, Font(ns_font).GetWeight()); // Row 8.
- }
-
- // NSCTFontUIUsageAttribute = CTFontEmphasizedUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.4 on 10.11+.
-
- if (base::mac::IsOS10_10()) {
- // Remaining rows are all BLACK on 10.10.
- for (int row = 9; row <= 14; ++row) {
- SCOPED_TRACE(testing::Message() << "Row: " << row);
- ns_font = [manager convertWeight:up ofFont:ns_font];
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight());
- }
- return;
- }
- EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight()); // Row 9.
-
- // NSCTFontUIUsageAttribute = CTFontHeavyUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.56.
- EXPECT_EQ(Font::Weight::EXTRA_BOLD, Font(ns_font).GetWeight()); // Row 10.
-
- // NSCTFontUIUsageAttribute = CTFontBlackUsage.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 11.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 12.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 13.
- ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
- EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 14.
+ EXPECT_EQ(Weight::BOLD, bold_italic_font.GetWeight());
}
// Test font derivation for fine-grained font weights.
TEST(PlatformFontMacTest, DerivedFineGrainedFonts) {
- // Only test where San Francisco is available.
- if (base::mac::IsAtMostOS10_10())
- return;
-
- using Weight = Font::Weight;
- Font base([NSFont systemFontOfSize:13]);
-
// The resulting, actual font weight after deriving |weight| from |base|.
- auto DerivedWeight = [&](Weight weight) {
+ auto DerivedIntWeight = [](Weight weight) {
+ Font base; // The default system font.
Font derived(base.Derive(0, 0, weight));
// PlatformFont should always pass the requested weight, not what the OS
// could provide. This just checks a constructor argument, so not very
// interesting.
- EXPECT_EQ(weight, derived.GetWeight());
+ EXPECT_EQ(static_cast<int>(weight), static_cast<int>(derived.GetWeight()));
- // Return the weight enum value that PlatformFontMac internally derives from
- // the floating point weight given by the kCTFontWeightTrait of |font|. Do
- // this by creating a new font based only off the NSFont in |derived|.
- return Font(derived.GetNativeFont()).GetWeight();
+ return static_cast<int>(PlatformFontMac::GetFontWeightFromNSFontForTesting(
+ derived.GetNativeFont()));
};
- // Only use NORMAL or BOLD as a base font. Mac font APIs go whacky otherwise.
- // See comments in PlatformFontMac::DeriveFont().
- for (Weight base_weight : {Weight::NORMAL, Weight::BOLD}) {
- SCOPED_TRACE(testing::Message()
- << "BaseWeight: " << static_cast<int>(base_weight));
- if (base_weight != Weight::NORMAL) {
- base = base.Derive(0, 0, base_weight);
- EXPECT_EQ(base_weight, base.GetWeight());
- }
-
- // Normal and heavy weights map correctly on 10.11 and 10.12.
- EXPECT_EQ(Weight::NORMAL, DerivedWeight(Weight::NORMAL));
- EXPECT_EQ(Weight::BOLD, DerivedWeight(Weight::BOLD));
- EXPECT_EQ(Weight::EXTRA_BOLD, DerivedWeight(Weight::EXTRA_BOLD));
- EXPECT_EQ(Weight::BLACK, DerivedWeight(Weight::BLACK));
-
- if (base::mac::IsAtMostOS10_11()) {
- // The fine-grained font weights on 10.11 are incomplete.
- EXPECT_EQ(Weight::NORMAL, DerivedWeight(Weight::EXTRA_LIGHT));
- EXPECT_EQ(Weight::NORMAL, DerivedWeight(Weight::THIN));
- EXPECT_EQ(Weight::NORMAL, DerivedWeight(Weight::LIGHT));
- EXPECT_EQ(Weight::BOLD, DerivedWeight(Weight::MEDIUM));
- EXPECT_EQ(Weight::BOLD, DerivedWeight(Weight::SEMIBOLD));
- continue;
- }
-
- // San Francisco doesn't offer anything between THIN and LIGHT.
- EXPECT_EQ(Weight::THIN, DerivedWeight(Weight::EXTRA_LIGHT));
-
- // All the rest should map correctly.
- EXPECT_EQ(Weight::THIN, DerivedWeight(Weight::THIN));
- EXPECT_EQ(Weight::LIGHT, DerivedWeight(Weight::LIGHT));
- EXPECT_EQ(Weight::MEDIUM, DerivedWeight(Weight::MEDIUM));
- EXPECT_EQ(Weight::SEMIBOLD, DerivedWeight(Weight::SEMIBOLD));
+ EXPECT_EQ(static_cast<int>(Weight::THIN), DerivedIntWeight(Weight::THIN));
+ EXPECT_EQ(static_cast<int>(Weight::EXTRA_LIGHT),
+ DerivedIntWeight(Weight::EXTRA_LIGHT));
+ EXPECT_EQ(static_cast<int>(Weight::LIGHT), DerivedIntWeight(Weight::LIGHT));
+ EXPECT_EQ(static_cast<int>(Weight::NORMAL), DerivedIntWeight(Weight::NORMAL));
+ EXPECT_EQ(static_cast<int>(Weight::MEDIUM), DerivedIntWeight(Weight::MEDIUM));
+ if (base::mac::IsAtMostOS10_10()) {
+ // If a SEMIBOLD system font is requested, 10.10 will return the bold system
+ // font, but somehow bearing a weight number of 0.24, which is really a
+ // medium weight (0.23).
+ EXPECT_EQ(static_cast<int>(Weight::MEDIUM),
+ DerivedIntWeight(Weight::SEMIBOLD));
+ } else {
+ EXPECT_EQ(static_cast<int>(Weight::SEMIBOLD),
+ DerivedIntWeight(Weight::SEMIBOLD));
}
+ EXPECT_EQ(static_cast<int>(Weight::BOLD), DerivedIntWeight(Weight::BOLD));
+ EXPECT_EQ(static_cast<int>(Weight::EXTRA_BOLD),
+ DerivedIntWeight(Weight::EXTRA_BOLD));
+ EXPECT_EQ(static_cast<int>(Weight::BLACK), DerivedIntWeight(Weight::BLACK));
}
// Ensures that the Font's reported height is consistent with the native font's
@@ -285,16 +195,14 @@ TEST(PlatformFontMacTest, DerivedFineGrainedFonts) {
TEST(PlatformFontMacTest, ValidateFontHeight) {
// Use the default ResourceBundle system font. E.g. Helvetica Neue in 10.10,
// Lucida Grande before that, and San Francisco after.
- gfx::Font default_font;
- gfx::Font::FontStyle styles[] = {gfx::Font::NORMAL, gfx::Font::ITALIC,
- gfx::Font::UNDERLINE};
+ Font default_font;
+ Font::FontStyle styles[] = {Font::NORMAL, Font::ITALIC, Font::UNDERLINE};
for (size_t i = 0; i < base::size(styles); ++i) {
SCOPED_TRACE(testing::Message() << "Font::FontStyle: " << styles[i]);
// Include the range of sizes used by ResourceBundle::FontStyle (-1 to +8).
for (int delta = -1; delta <= 8; ++delta) {
- gfx::Font font =
- default_font.Derive(delta, styles[i], gfx::Font::Weight::NORMAL);
+ Font font = default_font.Derive(delta, styles[i], Weight::NORMAL);
SCOPED_TRACE(testing::Message() << "FontSize(): " << font.GetFontSize());
NSFont* native_font = font.GetNativeFont();
@@ -328,15 +236,14 @@ TEST(PlatformFontMacTest, ValidateFontHeight) {
// Appkit's bug was detected on macOS 10.10 which uses Helvetica Neue as the
// system font.
TEST(PlatformFontMacTest, DerivedSemiboldFontIsNotItalic) {
- gfx::Font base_font;
+ Font base_font;
{
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:base_font.GetNativeFont()];
ASSERT_FALSE(traits & NSItalicFontMask);
}
- gfx::Font semibold_font(
- base_font.Derive(0, gfx::Font::NORMAL, gfx::Font::Weight::SEMIBOLD));
+ Font semibold_font(base_font.Derive(0, Font::NORMAL, Weight::SEMIBOLD));
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:semibold_font.GetNativeFont()];
EXPECT_FALSE(traits & NSItalicFontMask);
diff --git a/chromium/ui/gfx/range/BUILD.gn b/chromium/ui/gfx/range/BUILD.gn
index 16a87158e5c..9d7814c49d3 100644
--- a/chromium/ui/gfx/range/BUILD.gn
+++ b/chromium/ui/gfx/range/BUILD.gn
@@ -2,15 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
# Reset sources_assignment_filter for the BUILD.gn file to prevent
# regression during the migration of Chromium away from the feature.
# See docs/no_sources_assignment_filter.md for more information.
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("range") {
+component("range") {
sources = [
"gfx_range_export.h",
"range.cc",
@@ -19,7 +17,7 @@ jumbo_component("range") {
"range_f.h",
]
- if (is_ios || is_mac) {
+ if (is_apple) {
sources += [ "range_mac.mm" ]
}
diff --git a/chromium/ui/gfx/range/range.h b/chromium/ui/gfx/range/range.h
index fbdefe56472..b2f6cc44c5c 100644
--- a/chromium/ui/gfx/range/range.h
+++ b/chromium/ui/gfx/range/range.h
@@ -15,13 +15,13 @@
#include "build/build_config.h"
#include "ui/gfx/range/gfx_range_export.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#if __OBJC__
#import <Foundation/Foundation.h>
#else
typedef struct _NSRange NSRange;
#endif
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
namespace gfx {
@@ -42,7 +42,7 @@ class GFX_RANGE_EXPORT Range {
constexpr explicit Range(uint32_t position) : Range(position, position) {}
// Platform constructors.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
explicit Range(const NSRange& range);
#endif
@@ -115,7 +115,7 @@ class GFX_RANGE_EXPORT Range {
: InvalidRange();
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
Range& operator=(const NSRange& range);
// NSRange does not store the directionality of a range, so if this
diff --git a/chromium/ui/gfx/range/range_f.cc b/chromium/ui/gfx/range/range_f.cc
index 553e511ccd6..f3af360b8d5 100644
--- a/chromium/ui/gfx/range/range_f.cc
+++ b/chromium/ui/gfx/range/range_f.cc
@@ -19,24 +19,6 @@ RangeF RangeF::Intersect(const Range& range) const {
return Intersect(range_f);
}
-Range RangeF::Floor() const {
- uint32_t start = start_ > 0 ? static_cast<uint32_t>(std::floor(start_)) : 0;
- uint32_t end = end_ > 0 ? static_cast<uint32_t>(std::floor(end_)) : 0;
- return Range(start, end);
-}
-
-Range RangeF::Ceil() const {
- uint32_t start = start_ > 0 ? static_cast<uint32_t>(std::ceil(start_)) : 0;
- uint32_t end = end_ > 0 ? static_cast<uint32_t>(std::ceil(end_)) : 0;
- return Range(start, end);
-}
-
-Range RangeF::Round() const {
- uint32_t start = start_ > 0 ? static_cast<uint32_t>(std::round(start_)) : 0;
- uint32_t end = end_ > 0 ? static_cast<uint32_t>(std::round(end_)) : 0;
- return Range(start, end);
-}
-
std::string RangeF::ToString() const {
return base::StringPrintf("{%f,%f}", start(), end());
}
diff --git a/chromium/ui/gfx/range/range_unittest.cc b/chromium/ui/gfx/range/range_unittest.cc
index 55fbbcb365c..8fba7406ee9 100644
--- a/chromium/ui/gfx/range/range_unittest.cc
+++ b/chromium/ui/gfx/range/range_unittest.cc
@@ -225,36 +225,6 @@ TYPED_TEST(RangeTest, ContainAndIntersect) {
EXPECT_FALSE(r1.Intersects(invalid));
}
-TEST(RangeTest, RangeFConverterTest) {
- gfx::RangeF range_f(1.2f, 3.9f);
- gfx::Range range = range_f.Floor();
- EXPECT_EQ(1U, range.start());
- EXPECT_EQ(3U, range.end());
-
- range = range_f.Ceil();
- EXPECT_EQ(2U, range.start());
- EXPECT_EQ(4U, range.end());
-
- range = range_f.Round();
- EXPECT_EQ(1U, range.start());
- EXPECT_EQ(4U, range.end());
-
- // Test for negative values.
- range_f.set_start(-1.2f);
- range_f.set_end(-3.8f);
- range = range_f.Floor();
- EXPECT_EQ(0U, range.start());
- EXPECT_EQ(0U, range.end());
-
- range = range_f.Ceil();
- EXPECT_EQ(0U, range.start());
- EXPECT_EQ(0U, range.end());
-
- range = range_f.Round();
- EXPECT_EQ(0U, range.start());
- EXPECT_EQ(0U, range.end());
-}
-
TEST(RangeTest, RangeOperations) {
constexpr gfx::Range invalid_range = gfx::Range::InvalidRange();
constexpr gfx::Range ranges[] = {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1},
diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc
index 38394380939..3dc9e4daf46 100644
--- a/chromium/ui/gfx/render_text.cc
+++ b/chromium/ui/gfx/render_text.cc
@@ -15,6 +15,7 @@
#include "base/i18n/char_iterator.h"
#include "base/notreached.h"
#include "base/numerics/ranges.h"
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -31,7 +32,7 @@
#include "third_party/skia/include/effects/SkGradientShader.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/platform_font.h"
#include "ui/gfx/render_text_harfbuzz.h"
#include "ui/gfx/scoped_canvas.h"
@@ -68,7 +69,7 @@ int CalculateFadeGradientWidth(const FontList& font_list, int display_width) {
// Use a 1/3 of the display width if the display width is very short.
const int narrow_width = font_list.GetExpectedTextWidth(3);
const int gradient_width =
- std::min(narrow_width, gfx::ToRoundedInt(display_width / 3.f));
+ std::min(narrow_width, base::ClampRound(display_width / 3.f));
DCHECK_GE(gradient_width, 0);
return gradient_width;
}
@@ -116,7 +117,7 @@ sk_sp<cc::PaintShader> CreateFadeShader(const FontList& font_list,
const SkAlpha kAlphaAtZeroWidth = 51;
const SkAlpha alpha =
(width_fraction < 1)
- ? gfx::ToRoundedInt((1 - width_fraction) * kAlphaAtZeroWidth)
+ ? base::ClampRound<SkAlpha>((1 - width_fraction) * kAlphaAtZeroWidth)
: 0;
const SkColor fade_color = SkColorSetA(color, alpha);
@@ -187,7 +188,7 @@ typename BreakList<T>::const_iterator IncrementBreakListIteratorToPosition(
typename BreakList<T>::const_iterator iter,
size_t position) {
for (; iter != break_list.breaks().end(); ++iter) {
- const gfx::Range range = break_list.GetRange(iter);
+ const Range range = break_list.GetRange(iter);
if (position >= range.start() && position < range.end())
break;
}
@@ -748,11 +749,11 @@ bool RenderText::SetSelection(const SelectionModel& model) {
return changed;
}
-bool RenderText::MoveCursorToPoint(const gfx::Point& point,
+bool RenderText::MoveCursorToPoint(const Point& point,
bool select,
- const gfx::Point& drag_origin) {
+ const Point& drag_origin) {
reset_cached_cursor_x();
- gfx::SelectionModel model = FindCursorPosition(point, drag_origin);
+ SelectionModel model = FindCursorPosition(point, drag_origin);
if (select)
model.set_selection_start(selection().start());
return SetSelection(model);
@@ -905,7 +906,8 @@ VisualCursorDirection RenderText::GetVisualDirectionOfLogicalBeginning() {
Size RenderText::GetStringSize() {
const SizeF size_f = GetStringSizeF();
- return Size(std::ceil(size_f.width()), size_f.height());
+ return Size(base::ClampCeil(size_f.width()),
+ base::ClampCeil(size_f.height()));
}
float RenderText::TotalLineWidth() {
@@ -923,7 +925,7 @@ float RenderText::GetContentWidthF() {
}
int RenderText::GetContentWidth() {
- return ToCeiledInt(GetContentWidthF());
+ return base::ClampCeil(GetContentWidthF());
}
int RenderText::GetBaseline() {
@@ -1100,12 +1102,13 @@ Rect RenderText::GetCursorBounds(const SelectionModel& caret,
x = xspan.GetMin();
// Ceil the start and end of the |xspan| because the cursor x-coordinates
// are always ceiled.
- width =
- std::ceil(Clamp(xspan.GetMax())) - std::ceil(Clamp(xspan.GetMin()));
+ width = base::ClampCeil(Clamp(xspan.GetMax())) -
+ base::ClampCeil(Clamp(xspan.GetMin()));
}
}
+ Size line_size = gfx::ToCeiledSize(GetLineSizeF(caret));
return Rect(ToViewPoint(PointF(x, 0), caret_affinity),
- Size(width, GetLineSize(caret).height()));
+ Size(width, line_size.height()));
}
const Rect& RenderText::GetUpdatedCursorBounds() {
@@ -1179,10 +1182,6 @@ SelectionModel RenderText::GetSelectionModelForSelectionStart() const {
sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
}
-RectF RenderText::GetStringRect() {
- return RectF(PointF(ToViewPoint(PointF(), CURSOR_FORWARD)), GetStringSizeF());
-}
-
const Vector2d& RenderText::GetUpdatedDisplayOffset() {
UpdateCachedBoundsAndOffset();
return display_offset_;
@@ -1403,6 +1402,7 @@ SelectionModel RenderText::EdgeSelectionModel(
SelectionModel RenderText::LineSelectionModel(size_t line_index,
VisualCursorDirection direction) {
DCHECK(direction == CURSOR_LEFT || direction == CURSOR_RIGHT);
+ DCHECK_LT(line_index, GetShapedText()->lines().size());
const internal::Line& line = GetShapedText()->lines()[line_index];
if (line.segments.empty()) {
// Only the last line can be empty.
@@ -1568,8 +1568,8 @@ void RenderText::EnsureLayoutTextUpdated() const {
}
// Apply an underline to the composition range in |underlines|.
- const Range grapheme_start_range(gfx::Range(
- text_grapheme_start_position, text_grapheme_start_position + 1));
+ const Range grapheme_start_range(text_grapheme_start_position,
+ text_grapheme_start_position + 1);
if (composition_range_.Contains(grapheme_start_range))
layout_styles_[TEXT_STYLE_HEAVY_UNDERLINE].ApplyValue(true, range);
@@ -1690,7 +1690,8 @@ Point RenderText::ToViewPoint(const PointF& point,
const size_t num_lines = GetNumLines();
if (num_lines == 1) {
- return Point(std::ceil(Clamp(point.x())), std::round(point.y())) +
+ return Point(base::ClampCeil(Clamp(point.x())),
+ base::ClampRound(point.y())) +
GetLineOffset(0);
}
@@ -1747,7 +1748,7 @@ Point RenderText::ToViewPoint(const PointF& point,
}
}
- return Point(std::ceil(Clamp(x)), std::round(point.y())) +
+ return Point(base::ClampCeil(Clamp(x)), base::ClampRound(point.y())) +
GetLineOffset(line);
}
@@ -1943,11 +1944,10 @@ int RenderText::DetermineBaselineCenteringText(const int display_height,
}
// static
-gfx::Rect RenderText::ExpandToBeVerticallySymmetric(
- const gfx::Rect& rect,
- const gfx::Rect& display_rect) {
+Rect RenderText::ExpandToBeVerticallySymmetric(const Rect& rect,
+ const Rect& display_rect) {
// Mirror |rect| across the horizontal line dividing |display_rect| in half.
- gfx::Rect result = rect;
+ Rect result = rect;
int mid_y = display_rect.CenterPoint().y();
// The top of the mirror rect must be equidistant with the bottom of the
// original rect from the mid-line.
@@ -2028,9 +2028,8 @@ base::string16 RenderText::Elide(const base::string16& text,
// |last_guess| is merely used to verify that we're not repeating guesses.
const size_t last_guess = guess;
if (hi_width != lo_width) {
- guess = lo + static_cast<size_t>(
- ToRoundedInt((available_width - lo_width) * (hi - lo) /
- (hi_width - lo_width)));
+ guess = lo + base::ClampRound<size_t>((available_width - lo_width) *
+ (hi - lo) / (hi_width - lo_width));
}
guess = base::ClampToRange(guess, lo, hi);
DCHECK_NE(last_guess, guess);
diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h
index 9ef49c1dfda..4b39abb9d16 100644
--- a/chromium/ui/gfx/render_text.h
+++ b/chromium/ui/gfx/render_text.h
@@ -210,7 +210,7 @@ void ApplyRenderParams(const FontRenderParams& params,
// for rendering and translation between logical and visual data.
class GFX_EXPORT RenderText {
public:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, while selecting text if the cursor is outside the vertical text
// bounds, drag to the end of the text.
static constexpr bool kDragToEndIfOutsideVerticalBounds = true;
@@ -484,7 +484,7 @@ class GFX_EXPORT RenderText {
virtual SizeF GetStringSizeF() = 0;
// Returns the size of the line containing |caret|.
- virtual Size GetLineSize(const SelectionModel& caret) = 0;
+ virtual SizeF GetLineSizeF(const SelectionModel& caret) = 0;
// Returns the sum of all the line widths.
float TotalLineWidth();
@@ -563,15 +563,14 @@ class GFX_EXPORT RenderText {
void set_shadows(const ShadowValues& shadows) { shadows_ = shadows; }
const ShadowValues& shadows() const { return shadows_; }
- // Returns rectangle surrounding the current string (from origin to size)
- RectF GetStringRect();
-
// Get the visual bounds containing the logical substring within the |range|.
- // If |range| is empty, the result is empty. These bounds could be visually
- // discontinuous if the substring is split by a LTR/RTL level change.
- // These bounds are in local coordinates, but may be outside the visible
- // region if the text is longer than the textfield. Subsequent text, cursor,
- // or bounds changes may invalidate returned values.
+ // If |range| is empty, the result is empty. This method rounds internally so
+ // the returned bounds may be slightly larger than the |range|, but are
+ // guaranteed not to be smaller. These bounds could be visually discontinuous
+ // if the substring is split by a LTR/RTL level change. These bounds are in
+ // local coordinates, but may be outside the visible region if the text is
+ // longer than the textfield. Subsequent text, cursor, or bounds changes may
+ // invalidate returned values.
virtual std::vector<Rect> GetSubstringBounds(const Range& range) = 0;
// Gets the horizontal span (relative to the left of the text, not the view)
@@ -819,6 +818,8 @@ class GFX_EXPORT RenderText {
// Fixed width of glyphs. This should only be set in test environments.
float glyph_width_for_test_ = 0;
+ // Fixed height of glyphs. This should only be set in test environments.
+ float glyph_height_for_test_ = 0;
private:
friend class test::RenderTextTestApi;
@@ -872,10 +873,13 @@ class GFX_EXPORT RenderText {
virtual bool GetDecoratedTextForRange(const Range& range,
DecoratedText* decorated_text) = 0;
- // Specify the width of a glyph for test. The width of glyphs is very
- // platform-dependent and environment-dependent. Otherwise multiline text
+ // Specify the width/height of a glyph for test. The width/height of glyphs is
+ // very platform-dependent and environment-dependent. Otherwise multiline text
// will become really flaky.
void set_glyph_width_for_test(float width) { glyph_width_for_test_ = width; }
+ void set_glyph_height_for_test(float height) {
+ glyph_height_for_test_ = height;
+ }
// Logical UTF-16 string data to be drawn.
base::string16 text_;
diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc
index 64d00af2c83..16b6f85909c 100644
--- a/chromium/ui/gfx/render_text_harfbuzz.cc
+++ b/chromium/ui/gfx/render_text_harfbuzz.cc
@@ -18,15 +18,15 @@
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/icu/source/common/unicode/ubidi.h"
@@ -41,7 +41,6 @@
#include "ui/gfx/font.h"
#include "ui/gfx/font_fallback.h"
#include "ui/gfx/font_render_params.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/harfbuzz_font_skia.h"
#include "ui/gfx/platform_font.h"
#include "ui/gfx/range/range_f.h"
@@ -50,7 +49,7 @@
#include "ui/gfx/text_utils.h"
#include "ui/gfx/utf16_indexing.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "third_party/skia/include/ports/SkTypeface_mac.h"
@@ -66,13 +65,6 @@ namespace gfx {
namespace {
-// Experiment to determine best cache size (see https://crbug.com/1050793).
-const base::Feature kShapeRunCacheSize = {"ShapeRunCacheSize",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::FeatureParam<int> kShapeRunCacheSizeParam = {&kShapeRunCacheSize,
- "CacheSize", 10000};
-
// Text length limit. Longer strings are slow and not fully tested.
const size_t kMaxTextLength = 10000;
@@ -419,6 +411,7 @@ class HarfBuzzLineBreaker {
HarfBuzzLineBreaker(size_t max_width,
int min_baseline,
float min_height,
+ float glyph_height_for_test,
WordWrapBehavior word_wrap_behavior,
const base::string16& text,
const BreakList<size_t>* words,
@@ -426,6 +419,7 @@ class HarfBuzzLineBreaker {
: max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)),
min_baseline_(min_baseline),
min_height_(min_height),
+ glyph_height_for_test_(glyph_height_for_test),
word_wrap_behavior_(word_wrap_behavior),
text_(text),
words_(words),
@@ -524,9 +518,14 @@ class HarfBuzzLineBreaker {
return run_list_.logical_to_visual(s1.run) <
run_list_.logical_to_visual(s2.run);
});
- line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_));
+
+ line->size.set_height(
+ glyph_height_for_test_
+ ? glyph_height_for_test_
+ : std::max(min_height_, max_descent_ + max_ascent_));
+
line->baseline = std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
- line->preceding_heights = std::ceil(total_size_.height());
+ line->preceding_heights = base::ClampCeil(total_size_.height());
// Subtract newline segment's width from |total_size_| because it's not
// drawn.
float line_width = line->size.width();
@@ -741,6 +740,7 @@ class HarfBuzzLineBreaker {
const SkScalar max_width_;
const int min_baseline_;
const float min_height_;
+ const float glyph_height_for_test_;
const WordWrapBehavior word_wrap_behavior_;
const base::string16& text_;
const BreakList<size_t>* const words_;
@@ -787,7 +787,7 @@ void ApplyForcedDirection(UBiDiLevel* level) {
}
internal::TextRunHarfBuzz::FontParams CreateFontParams(
- const gfx::Font& primary_font,
+ const Font& primary_font,
UBiDiLevel bidi_level,
UScriptCode script,
const internal::StyleIterator& style) {
@@ -813,7 +813,7 @@ namespace internal {
sk_sp<SkTypeface> CreateSkiaTypeface(const Font& font,
bool italic,
Font::Weight weight) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
const Font::FontStyle style = italic ? Font::ITALIC : Font::NORMAL;
Font font_with_style = font.Derive(0, style, weight);
if (!font_with_style.GetNativeFont())
@@ -860,14 +860,14 @@ void TextRunHarfBuzz::FontParams::
// Calculate a slightly smaller font. The ratio here is somewhat arbitrary.
// Proportions from 5/9 to 5/7 all look pretty good.
const float ratio = 5.0f / 9.0f;
- font_size = ToRoundedInt(font.GetFontSize() * ratio);
+ font_size = base::ClampRound(font.GetFontSize() * ratio);
switch (baseline_type) {
case SUPERSCRIPT:
baseline_offset = font.GetCapHeight() - font.GetHeight();
break;
case SUPERIOR:
baseline_offset =
- ToRoundedInt(font.GetCapHeight() * ratio) - font.GetCapHeight();
+ base::ClampRound(font.GetCapHeight() * ratio) - font.GetCapHeight();
break;
case SUBSCRIPT:
baseline_offset = font.GetHeight() - font.GetBaseline();
@@ -1262,14 +1262,16 @@ struct ShapeRunWithFontInput {
size_t hash = 0;
};
-// An MRU cache of the results from calling ShapeRunWithFont. Use the same
-// maximum cache size as is used in blink::ShapeCache.
+// An MRU cache of the results from calling ShapeRunWithFont. The maximum cache
+// size used in blink::ShapeCache is 10k. A Finch experiment showed that
+// reducing the cache size to 1k has no performance impact.
+constexpr int kShapeRunCacheSize = 1000;
using ShapeRunCacheBase = base::HashingMRUCache<ShapeRunWithFontInput,
TextRunHarfBuzz::ShapeOutput,
ShapeRunWithFontInput::Hash>;
class ShapeRunCache : public ShapeRunCacheBase {
public:
- ShapeRunCache() : ShapeRunCacheBase(kShapeRunCacheSizeParam.Get()) {}
+ ShapeRunCache() : ShapeRunCacheBase(kShapeRunCacheSize) {}
};
void ShapeRunWithFont(const ShapeRunWithFontInput& in,
@@ -1342,8 +1344,14 @@ void ShapeRunWithFont(const ShapeRunWithFontInput& in,
if (in.obscured)
out->width += in.obscured_glyph_spacing;
+ // When subpixel positioning is not enabled, glyph width is rounded to avoid
+ // fractional width. Disable this conversion when a glyph width is provided
+ // for testing. Using an integral glyph width has the same behavior as
+ // disabling the subpixel positioning.
+ const bool force_subpixel_for_test = in.glyph_width_for_test != 0;
+
// Round run widths if subpixel positioning is off to match native behavior.
- if (!in.render_params.subpixel_positioning)
+ if (!in.render_params.subpixel_positioning && !force_subpixel_for_test)
out->width = std::round(out->width);
}
@@ -1397,21 +1405,17 @@ SizeF RenderTextHarfBuzz::GetStringSizeF() {
return total_size_;
}
-Size RenderTextHarfBuzz::GetLineSize(const SelectionModel& caret) {
- const auto to_size = [](const internal::Line& line) {
- return Size(std::ceil(line.size.width()), line.size.height());
- };
-
+SizeF RenderTextHarfBuzz::GetLineSizeF(const SelectionModel& caret) {
const internal::ShapedText* shaped_text = GetShapedText();
const auto& caret_run = GetRunContainingCaret(caret);
for (const auto& line : shaped_text->lines()) {
for (const internal::LineSegment& segment : line.segments) {
if (segment.run == caret_run)
- return to_size(line);
+ return line.size;
}
}
- return to_size(shaped_text->lines().back());
+ return shaped_text->lines().back().size;
}
std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
@@ -1450,9 +1454,11 @@ std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
RangeF selected_span =
run.GetGraphemeSpanForCharRange(this, intersection);
- int start_x = std::ceil(selected_span.start() - line_start_x);
- int end_x = std::ceil(selected_span.end() - line_start_x);
- Rect rect(start_x, 0, end_x - start_x, std::ceil(line.size.height()));
+ DCHECK(!selected_span.is_reversed());
+ int start_x = base::ClampFloor(selected_span.start() - line_start_x);
+ int end_x = base::ClampCeil(selected_span.end() - line_start_x);
+ Rect rect(start_x, 0, end_x - start_x,
+ base::ClampCeil(line.size.height()));
rects.push_back(rect + GetLineOffset(line_index));
}
}
@@ -1508,6 +1514,11 @@ size_t RenderTextHarfBuzz::GetLineContainingCaret(const SelectionModel& caret) {
if (caret.caret_pos() == 0)
return 0;
+ if (!multiline()) {
+ DCHECK_EQ(1u, GetShapedText()->lines().size());
+ return 0;
+ }
+
size_t layout_position = TextIndexToDisplayIndex(caret.caret_pos());
LogicalCursorDirection affinity = caret.caret_affinity();
const internal::ShapedText* shaped_text = GetShapedText();
@@ -1675,7 +1686,7 @@ void RenderTextHarfBuzz::EnsureLayout() {
HarfBuzzLineBreaker line_breaker(
display_rect().width(),
DetermineBaselineCenteringText(height, font_list()), height,
- word_wrap_behavior(), GetDisplayText(),
+ glyph_height_for_test_, word_wrap_behavior(), GetDisplayText(),
multiline() ? &GetLineBreaks() : nullptr, *run_list);
if (multiline())
@@ -2153,7 +2164,7 @@ void RenderTextHarfBuzz::ShapeRunsWithFont(
std::vector<internal::TextRunHarfBuzz*> runs_with_missing_glyphs;
for (internal::TextRunHarfBuzz*& run : *in_out_runs) {
// First do a cache lookup.
- bool can_use_cache = base::MessageLoopCurrentForUI::IsSet() &&
+ bool can_use_cache = base::CurrentUIThread::IsSet() &&
run->range.length() <= kMaxRunLengthToCache;
bool found_in_cache = false;
const internal::ShapeRunWithFontInput cache_key(
diff --git a/chromium/ui/gfx/render_text_harfbuzz.h b/chromium/ui/gfx/render_text_harfbuzz.h
index 00e51764481..b0676fa76f1 100644
--- a/chromium/ui/gfx/render_text_harfbuzz.h
+++ b/chromium/ui/gfx/render_text_harfbuzz.h
@@ -209,7 +209,7 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
// RenderText:
const base::string16& GetDisplayText() override;
SizeF GetStringSizeF() override;
- Size GetLineSize(const SelectionModel& caret) override;
+ SizeF GetLineSizeF(const SelectionModel& caret) override;
std::vector<Rect> GetSubstringBounds(const Range& range) override;
RangeF GetCursorSpan(const Range& text_range) override;
size_t GetLineContainingCaret(const SelectionModel& caret) override;
diff --git a/chromium/ui/gfx/render_text_test_api.h b/chromium/ui/gfx/render_text_test_api.h
index 3111f1d494c..1577a69b864 100644
--- a/chromium/ui/gfx/render_text_test_api.h
+++ b/chromium/ui/gfx/render_text_test_api.h
@@ -88,12 +88,14 @@ class RenderTextTestApi {
return render_text_->GetDisplayTextBaseline();
}
- // Callers must ensure that the underlying RenderText object is a
- // RenderTextHarfBuzz instance.
void SetGlyphWidth(float test_width) {
render_text_->set_glyph_width_for_test(test_width);
}
+ void SetGlyphHeight(float test_height) {
+ render_text_->set_glyph_height_for_test(test_height);
+ }
+
static gfx::Rect ExpandToBeVerticallySymmetric(
const gfx::Rect& rect,
const gfx::Rect& display_rect) {
diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc
index 2fd63b1b75f..0c92673e372 100644
--- a/chromium/ui/gfx/render_text_unittest.cc
+++ b/chromium/ui/gfx/render_text_unittest.cc
@@ -38,6 +38,7 @@
#include "ui/gfx/font.h"
#include "ui/gfx/font_names_testing.h"
#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/range/range_f.h"
@@ -51,7 +52,7 @@
#include "base/win/windows_version.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "base/mac/mac_util.h"
#endif
@@ -601,6 +602,10 @@ class RenderTextTest : public testing::Test {
test_api()->SetGlyphWidth(test_width);
}
+ void SetGlyphHeight(float test_height) {
+ test_api()->SetGlyphHeight(test_height);
+ }
+
bool ShapeRunWithFont(const base::string16& text,
const Font& font,
const FontRenderParams& render_params,
@@ -698,7 +703,7 @@ TEST_F(RenderTextTest, SetStyles) {
TEST_F(RenderTextTest, ApplyStyles) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("012345678"));
+ render_text->SetText(ASCIIToUTF16("012345678"));
constexpr int kTestFontSizeOverride = 20;
@@ -753,21 +758,21 @@ TEST_F(RenderTextTest, ApplyStyles) {
expected_italic));
// Changing the text should clear any breaks except for the first one.
- render_text->SetText(UTF8ToUTF16("0123456"));
+ render_text->SetText(ASCIIToUTF16("0123456"));
expected_italic.resize(1);
EXPECT_TRUE(test_api()->styles()[TEXT_STYLE_ITALIC].EqualsForTesting(
expected_italic));
render_text->ApplyStyle(TEXT_STYLE_ITALIC, false, Range(2, 4));
- render_text->SetText(UTF8ToUTF16("012345678"));
+ render_text->SetText(ASCIIToUTF16("012345678"));
EXPECT_TRUE(test_api()->styles()[TEXT_STYLE_ITALIC].EqualsForTesting(
expected_italic));
render_text->ApplyStyle(TEXT_STYLE_ITALIC, false, Range(0, 1));
- render_text->SetText(UTF8ToUTF16("0123456"));
+ render_text->SetText(ASCIIToUTF16("0123456"));
expected_italic.begin()->second = false;
EXPECT_TRUE(test_api()->styles()[TEXT_STYLE_ITALIC].EqualsForTesting(
expected_italic));
render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, Range(2, 4));
- render_text->SetText(UTF8ToUTF16("012345678"));
+ render_text->SetText(ASCIIToUTF16("012345678"));
EXPECT_TRUE(test_api()->styles()[TEXT_STYLE_ITALIC].EqualsForTesting(
expected_italic));
@@ -802,7 +807,7 @@ TEST_F(RenderTextTest, ApplyStyleSurrogatePair) {
TEST_F(RenderTextTest, ApplyStyleGrapheme) {
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"\u0065\u0301"));
+ render_text->SetText(UTF8ToUTF16("\u0065\u0301"));
render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, gfx::Range(1, 2));
render_text->ApplyStyle(TEXT_STYLE_UNDERLINE, true, gfx::Range(0, 1));
Draw();
@@ -813,7 +818,7 @@ TEST_F(RenderTextTest, ApplyStyleGrapheme) {
TEST_F(RenderTextTest, ApplyStyleMultipleGraphemes) {
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"xx\u0065\u0301x"));
+ render_text->SetText(UTF8ToUTF16("xx\u0065\u0301x"));
// Apply the style in the middle of a grapheme.
gfx::Range range(1, 3);
render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, range);
@@ -908,7 +913,7 @@ TEST_F(RenderTextTest, ApplyColorArabicDiacritics) {
// Render an Arabic character with two diacritics. The color should be taken
// from the base character.
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"\u0628\u0651\u0650"));
+ render_text->SetText(UTF8ToUTF16("\u0628\u0651\u0650"));
render_text->ApplyColor(SK_ColorRED, Range(0, 1));
render_text->ApplyColor(SK_ColorBLACK, Range(1, 2));
render_text->ApplyColor(SK_ColorBLUE, Range(2, 3));
@@ -924,14 +929,14 @@ TEST_F(RenderTextTest, ApplyColorArabicLigature) {
// Render the isolated form of the first glyph.
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"\u0628"));
+ render_text->SetText(UTF8ToUTF16("\u0628"));
Draw();
ASSERT_EQ(1u, text_log().size());
ASSERT_EQ(1u, text_log()[0].glyphs().size());
uint16_t isolated_first_glyph = text_log()[0].glyphs()[0];
// Render a pair of glyphs (initial form and final form).
- render_text->SetText(WideToUTF16(L"\u0628\u0645"));
+ render_text->SetText(UTF8ToUTF16("\u0628\u0645"));
Draw();
ASSERT_EQ(1u, text_log().size());
ASSERT_LE(2u, text_log()[0].glyphs().size());
@@ -968,7 +973,7 @@ TEST_F(RenderTextTest, ApplyColorArabicLigature) {
TEST_F(RenderTextTest, AppendTextKeepsStyles) {
RenderText* render_text = GetRenderText();
// Setup basic functionality.
- render_text->SetText(UTF8ToUTF16("abcd"));
+ render_text->SetText(ASCIIToUTF16("abcd"));
render_text->ApplyColor(SK_ColorRED, Range(0, 1));
render_text->ApplyBaselineStyle(SUPERSCRIPT, Range(1, 2));
render_text->ApplyStyle(TEXT_STYLE_UNDERLINE, true, Range(2, 3));
@@ -990,8 +995,8 @@ TEST_F(RenderTextTest, AppendTextKeepsStyles) {
test_api()->font_size_overrides().EqualsForTesting(expected_font_size));
// Ensure AppendText maintains current text styles.
- render_text->AppendText(UTF8ToUTF16("efg"));
- EXPECT_EQ(render_text->GetDisplayText(), UTF8ToUTF16("abcdefg"));
+ render_text->AppendText(ASCIIToUTF16("efg"));
+ EXPECT_EQ(render_text->GetDisplayText(), ASCIIToUTF16("abcdefg"));
EXPECT_TRUE(test_api()->colors().EqualsForTesting(expected_color));
EXPECT_TRUE(test_api()->baselines().EqualsForTesting(expected_baseline));
EXPECT_TRUE(test_api()->styles()[TEXT_STYLE_UNDERLINE].EqualsForTesting(
@@ -1045,7 +1050,7 @@ TEST_F(RenderTextTest, SelectRangeColored) {
TEST_F(RenderTextTest, SelectRangeColoredGrapheme) {
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"x\u0065\u0301y"));
+ render_text->SetText(UTF8ToUTF16("x\u0065\u0301y"));
render_text->SetColor(SK_ColorBLACK);
render_text->set_selection_color(SK_ColorRED);
render_text->set_focused(true);
@@ -1154,7 +1159,7 @@ void TestVisualCursorMotionInObscuredField(
}
TEST_F(RenderTextTest, ObscuredText) {
- const base::string16 seuss = UTF8ToUTF16("hop on pop");
+ const base::string16 seuss = ASCIIToUTF16("hop on pop");
const base::string16 no_seuss = GetObscuredString(seuss.length());
RenderText* render_text = GetRenderText();
@@ -1223,7 +1228,7 @@ TEST_F(RenderTextTest, ObscuredText) {
}
TEST_F(RenderTextTest, ObscuredTextMultiline) {
- const base::string16 test = UTF8ToUTF16("a\nbc\ndef");
+ const base::string16 test = ASCIIToUTF16("a\nbc\ndef");
RenderText* render_text = GetRenderText();
render_text->SetText(test);
render_text->SetObscured(true);
@@ -1236,7 +1241,7 @@ TEST_F(RenderTextTest, ObscuredTextMultiline) {
}
TEST_F(RenderTextTest, ObscuredTextMultilineNewline) {
- const base::string16 test = UTF8ToUTF16("\r\r\n");
+ const base::string16 test = ASCIIToUTF16("\r\r\n");
RenderText* render_text = GetRenderText();
render_text->SetText(test);
render_text->SetObscured(true);
@@ -1250,7 +1255,7 @@ TEST_F(RenderTextTest, ObscuredTextMultilineNewline) {
}
TEST_F(RenderTextTest, RevealObscuredText) {
- const base::string16 seuss = UTF8ToUTF16("hop on pop");
+ const base::string16 seuss = ASCIIToUTF16("hop on pop");
const base::string16 no_seuss = GetObscuredString(seuss.length());
RenderText* render_text = GetRenderText();
@@ -1286,11 +1291,11 @@ TEST_F(RenderTextTest, RevealObscuredText) {
EXPECT_EQ(no_seuss, render_text->GetDisplayText());
// SetText clears the revealed index.
- render_text->SetText(UTF8ToUTF16("new"));
+ render_text->SetText(ASCIIToUTF16("new"));
EXPECT_EQ(GetObscuredString(3), render_text->GetDisplayText());
render_text->RenderText::SetObscuredRevealIndex(2);
EXPECT_EQ(GetObscuredString(3, 2, 'w'), render_text->GetDisplayText());
- render_text->SetText(UTF8ToUTF16("new longer"));
+ render_text->SetText(ASCIIToUTF16("new longer"));
EXPECT_EQ(GetObscuredString(10), render_text->GetDisplayText());
// Text with invalid surrogates (surrogates low 0xDC00 and high 0xD800).
@@ -1348,7 +1353,7 @@ TEST_F(RenderTextTest, ObscuredEmoji) {
render_text->Draw(canvas());
// Emoji codepoints are replaced by bullets (e.g. "\u2022\u2022").
- EXPECT_EQ(WideToUTF16(L"\u2022\u2022"), render_text->GetDisplayText());
+ EXPECT_EQ(UTF8ToUTF16("\u2022\u2022"), render_text->GetDisplayText());
EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(0U));
EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(1U));
EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U));
@@ -1366,7 +1371,7 @@ TEST_F(RenderTextTest, ObscuredEmoji) {
render_text->Draw(canvas());
// Emoji codepoints are replaced by bullets (e.g. "\u2022\u2022").
- EXPECT_EQ(WideToUTF16(L"\u2022\u2022"), render_text->GetDisplayText());
+ EXPECT_EQ(UTF8ToUTF16("\u2022\u2022"), render_text->GetDisplayText());
EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(0U));
EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(1U));
EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U));
@@ -1956,8 +1961,8 @@ TEST_F(RenderTextTest, ElidedText_NoTrimWhitespace) {
// [ ...]
// and not like:
// [... ]
- constexpr wchar_t kInputString[] = L" foo";
- const base::string16 input = WideToUTF16(kInputString);
+ constexpr char kInputString[] = " foo";
+ const base::string16 input = ASCIIToUTF16(kInputString);
render_text->SetText(input);
// Choose a width based on being able to display 12 characters (one of which
@@ -1989,8 +1994,8 @@ TEST_F(RenderTextTest, ElidedObscuredText) {
render_text->SetDisplayRect(
Rect(0, 0, expected_render_text->GetContentWidth(), 100));
render_text->SetObscured(true);
- render_text->SetText(UTF8ToUTF16("abcdef"));
- EXPECT_EQ(UTF8ToUTF16("abcdef"), render_text->text());
+ render_text->SetText(ASCIIToUTF16("abcdef"));
+ EXPECT_EQ(ASCIIToUTF16("abcdef"), render_text->text());
EXPECT_EQ(elided_obscured_text, render_text->GetDisplayText());
}
@@ -1999,7 +2004,7 @@ TEST_F(RenderTextTest, MultilineElide) {
base::string16 input_text;
// Aim for 3 lines of text.
for (int i = 0; i < 20; ++i)
- input_text.append(UTF8ToUTF16("hello world "));
+ input_text.append(ASCIIToUTF16("hello world "));
render_text->SetText(input_text);
// Apply a style that tweaks the layout to make sure elision is calculated
// with these styles. This can expose a behavior in layout where text is
@@ -2049,7 +2054,7 @@ TEST_F(RenderTextTest, MultilineElideWrap) {
RenderText* render_text = GetRenderText();
base::string16 input_text;
for (int i = 0; i < 20; ++i)
- input_text.append(UTF8ToUTF16("hello world "));
+ input_text.append(ASCIIToUTF16("hello world "));
render_text->SetText(input_text);
render_text->SetMultiline(true);
render_text->SetMaxLines(3);
@@ -2079,7 +2084,7 @@ TEST_F(RenderTextTest, DISABLED_MultilineElideWrapWithStyle) {
RenderText* render_text = GetRenderText();
base::string16 input_text;
for (int i = 0; i < 20; ++i)
- input_text.append(UTF8ToUTF16("hello world "));
+ input_text.append(ASCIIToUTF16("hello world "));
render_text->SetText(input_text);
render_text->ApplyWeight(Font::Weight::BOLD, Range(1, 20));
render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, Range(1, 20));
@@ -2107,7 +2112,7 @@ TEST_F(RenderTextTest, MultilineElideWrapStress) {
RenderText* render_text = GetRenderText();
base::string16 input_text;
for (int i = 0; i < 20; ++i)
- input_text.append(UTF8ToUTF16("hello world "));
+ input_text.append(ASCIIToUTF16("hello world "));
render_text->SetText(input_text);
render_text->SetMultiline(true);
render_text->SetMaxLines(3);
@@ -2141,7 +2146,7 @@ TEST_F(RenderTextTest, DISABLED_MultilineElideWrapStressWithStyle) {
RenderText* render_text = GetRenderText();
base::string16 input_text;
for (int i = 0; i < 20; ++i)
- input_text.append(UTF8ToUTF16("hello world "));
+ input_text.append(ASCIIToUTF16("hello world "));
render_text->SetText(input_text);
render_text->ApplyWeight(Font::Weight::BOLD, Range(1, 20));
render_text->SetMultiline(true);
@@ -2208,7 +2213,7 @@ TEST_F(RenderTextTest, MultilineElideLinebreak) {
RenderText* render_text = GetRenderText();
SetGlyphWidth(5);
- base::string16 input_text(UTF8ToUTF16("hello\nworld"));
+ base::string16 input_text(ASCIIToUTF16("hello\nworld"));
render_text->SetText(input_text);
render_text->SetCursorEnabled(false);
render_text->SetMultiline(true);
@@ -2264,11 +2269,11 @@ TEST_F(RenderTextTest, ElidedStyledTextRtl) {
TEST_F(RenderTextTest, ElidedEmail) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("test@example.com"));
+ render_text->SetText(ASCIIToUTF16("test@example.com"));
const Size size = render_text->GetStringSize();
const base::string16 long_email =
- UTF8ToUTF16("longemailaddresstest@example.com");
+ ASCIIToUTF16("longemailaddresstest@example.com");
render_text->SetText(long_email);
render_text->SetElideBehavior(ELIDE_EMAIL);
render_text->SetDisplayRect(Rect(size));
@@ -2341,8 +2346,8 @@ TEST_F(RenderTextTest, TruncatedObscuredText) {
RenderText* render_text = GetRenderText();
render_text->set_truncate_length(3);
render_text->SetObscured(true);
- render_text->SetText(UTF8ToUTF16("abcdef"));
- EXPECT_EQ(UTF8ToUTF16("abcdef"), render_text->text());
+ render_text->SetText(ASCIIToUTF16("abcdef"));
+ EXPECT_EQ(ASCIIToUTF16("abcdef"), render_text->text());
EXPECT_EQ(GetObscuredString(3, 2, kEllipsisUTF16[0]),
render_text->GetDisplayText());
}
@@ -2368,7 +2373,7 @@ TEST_F(RenderTextTest, TruncatedObscuredTextWithGraphemes) {
TEST_F(RenderTextTest, TruncatedCursorMovementLTR) {
RenderText* render_text = GetRenderText();
render_text->set_truncate_length(2);
- render_text->SetText(UTF8ToUTF16("abcd"));
+ render_text->SetText(ASCIIToUTF16("abcd"));
EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
@@ -2423,7 +2428,7 @@ TEST_F(RenderTextTest, TruncatedCursorMovementRTL) {
TEST_F(RenderTextTest, MoveCursor_Character) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("123 456 789"));
+ render_text->SetText(ASCIIToUTF16("123 456 789"));
std::vector<Range> expected;
// SELECTION_NONE.
@@ -2486,7 +2491,7 @@ TEST_F(RenderTextTest, MoveCursor_Character) {
TEST_F(RenderTextTest, MoveCursor_Word) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("123 456 789"));
+ render_text->SetText(ASCIIToUTF16("123 456 789"));
std::vector<Range> expected;
// SELECTION_NONE.
@@ -2676,7 +2681,7 @@ TEST_F(RenderTextTest, MoveCursor_Word_RTL) {
TEST_F(RenderTextTest, MoveCursor_Line) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("123 456 789"));
+ render_text->SetText(ASCIIToUTF16("123 456 789"));
std::vector<Range> expected;
for (auto break_type : {LINE_BREAK, FIELD_BREAK}) {
@@ -2893,6 +2898,22 @@ TEST_F(RenderTextTest, MoveCursor_UpDown_Cache) {
&expected_range);
}
+TEST_F(RenderTextTest, MoveCursorWithNewline) {
+ RenderText* render_text = GetRenderText();
+ render_text->SetText(ASCIIToUTF16("a\r\nb"));
+ render_text->SetMultiline(false);
+ EXPECT_EQ(1U, render_text->GetNumLines());
+
+ EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE);
+ EXPECT_EQ(SelectionModel(1, CURSOR_BACKWARD), render_text->selection_model());
+ render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE);
+ EXPECT_EQ(SelectionModel(3, CURSOR_BACKWARD), render_text->selection_model());
+
+ render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
+ EXPECT_EQ(SelectionModel(4, CURSOR_FORWARD), render_text->selection_model());
+}
+
TEST_F(RenderTextTest, GetTextDirectionInvalidation) {
RenderText* render_text = GetRenderText();
ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
@@ -2903,18 +2924,18 @@ TEST_F(RenderTextTest, GetTextDirectionInvalidation) {
render_text->SetText(ASCIIToUTF16("a"));
EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
- render_text->SetText(WideToUTF16(L"\u05d0"));
+ render_text->SetText(UTF8ToUTF16("\u05d0"));
EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
// The codepoints u+2026 (ellipsis) has no strong direction.
- render_text->SetText(WideToUTF16(L"\u2026"));
+ render_text->SetText(UTF8ToUTF16("\u2026"));
EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
render_text->AppendText(ASCIIToUTF16("a"));
EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
- render_text->SetText(WideToUTF16(L"\u2026"));
+ render_text->SetText(UTF8ToUTF16("\u2026"));
EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
- render_text->AppendText(WideToUTF16(L"\u05d0"));
+ render_text->AppendText(UTF8ToUTF16("\u05d0"));
EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
}
@@ -2928,18 +2949,18 @@ TEST_F(RenderTextTest, GetDisplayTextDirectionInvalidation) {
render_text->SetText(ASCIIToUTF16("a"));
EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
- render_text->SetText(WideToUTF16(L"\u05d0"));
+ render_text->SetText(UTF8ToUTF16("\u05d0"));
EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
// The codepoints u+2026 (ellipsis) has no strong direction.
- render_text->SetText(WideToUTF16(L"\u2026"));
+ render_text->SetText(UTF8ToUTF16("\u2026"));
EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
render_text->AppendText(ASCIIToUTF16("a"));
EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
- render_text->SetText(WideToUTF16(L"\u2026"));
+ render_text->SetText(UTF8ToUTF16("\u2026"));
EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
- render_text->AppendText(WideToUTF16(L"\u05d0"));
+ render_text->AppendText(UTF8ToUTF16("\u05d0"));
EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
}
@@ -2948,13 +2969,13 @@ TEST_F(RenderTextTest, GetTextDirectionWithDifferentDirection) {
RenderText* render_text = GetRenderText();
ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
render_text->SetWhitespaceElision(false);
- render_text->SetText(WideToUTF16(L"123\u0638xyz"));
+ render_text->SetText(UTF8ToUTF16("123\u0638xyz"));
render_text->SetElideBehavior(ELIDE_HEAD);
render_text->SetDisplayRect(Rect(25, 100));
// The elided text is an ellipsis with neutral directionality, and a 'z' with
// a strong LTR directionality.
- EXPECT_EQ(WideToUTF16(L"\u2026z"), render_text->GetDisplayText());
+ EXPECT_EQ(UTF8ToUTF16("\u2026z"), render_text->GetDisplayText());
EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
}
@@ -2964,7 +2985,7 @@ TEST_F(RenderTextTest, DirectionalityInvalidation) {
ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
// The codepoints u+2026 (ellipsis) has weak directionality.
- render_text->SetText(WideToUTF16(L"\u2026"));
+ render_text->SetText(UTF8ToUTF16("\u2026"));
const base::i18n::TextDirection original_text_direction =
render_text->GetTextDirection();
@@ -3130,7 +3151,7 @@ INSTANTIATE_TEST_SUITE_P(
TEST_F(RenderTextTest, MoveCursorLeftRightInLtr) {
RenderText* render_text = GetRenderText();
// Pure LTR.
- render_text->SetText(UTF8ToUTF16("abc"));
+ render_text->SetText(ASCIIToUTF16("abc"));
// |expected| saves the expected SelectionModel when moving cursor from left
// to right.
std::vector<SelectionModel> expected;
@@ -3297,7 +3318,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) {
// Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter
// (code point) has unique bounds, so mid-glyph cursoring should be possible.
render_text->SetFontList(FontList("Meiryo UI, 12px"));
- render_text->SetText(UTF8ToUTF16("ff ffi"));
+ render_text->SetText(ASCIIToUTF16("ff ffi"));
render_text->SetDisplayRect(gfx::Rect(100, 100));
EXPECT_EQ(0U, render_text->cursor_position());
@@ -3318,7 +3339,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRight_MeiryoUILigatures) {
TEST_F(RenderTextTest, GraphemeIterator) {
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"a\u0065\u0301b"));
+ render_text->SetText(UTF8ToUTF16("a\u0065\u0301b"));
internal::GraphemeIterator iterator =
render_text->GetGraphemeIteratorAtTextIndex(0);
@@ -3457,7 +3478,7 @@ TEST_F(RenderTextTest, GraphemeBoundaries) {
TEST_F(RenderTextTest, GraphemePositions) {
// LTR कि (DEVANAGARI KA with VOWEL I) (2-char grapheme), LTR abc, and LTR कि.
- const base::string16 kText1 = WideToUTF16(L"\u0915\u093fabc\u0915\u093f");
+ const base::string16 kText1 = UTF8ToUTF16("\u0915\u093fabc\u0915\u093f");
// LTR ab, LTR कि (DEVANAGARI KA with VOWEL I) (2-char grapheme), LTR cd.
const base::string16 kText2 = UTF8ToUTF16("ab\u0915\u093fcd");
@@ -3563,12 +3584,11 @@ TEST_F(RenderTextTest, FindCursorPosition) {
SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
for (size_t j = 0; j < render_text->text().length(); ++j) {
- const Range range(render_text->GetCursorSpan(Range(j, j + 1)).Round());
+ gfx::RangeF cursor_span = render_text->GetCursorSpan(Range(j, j + 1));
// Test a point just inside the leading edge of the glyph bounds.
- int x = range.is_reversed() ? range.GetMax() - 1 : range.GetMin() + 1;
- EXPECT_EQ(
- j, render_text->FindCursorPosition(Point(x, GetCursorYForTesting()))
- .caret_pos());
+ float x = cursor_span.start() + (cursor_span.is_reversed() ? -1 : 1);
+ Point point = gfx::ToCeiledPoint(PointF(x, GetCursorYForTesting()));
+ EXPECT_EQ(j, render_text->FindCursorPosition(point).caret_pos());
}
}
}
@@ -3638,7 +3658,7 @@ TEST_F(RenderTextTest, FindCursorPosition_GraphemeBoundaries) {
TEST_F(RenderTextTest, EdgeSelectionModels) {
// Simple Latin text.
- const base::string16 kLatin = UTF8ToUTF16("abc");
+ const base::string16 kLatin = ASCIIToUTF16("abc");
// LTR कि (DEVANAGARI KA with VOWEL I).
const base::string16 kLTRGrapheme = UTF8ToUTF16("\u0915\u093f");
// LTR कि (DEVANAGARI KA with VOWEL I), LTR a, LTR कि.
@@ -3750,7 +3770,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection_Multiline) {
RenderText* render_text = GetRenderText();
render_text->SetMultiline(true);
render_text->SetDisplayRect(Rect(20, 1000));
- render_text->SetText(UTF8ToUTF16("012 456\n\n90"));
+ render_text->SetText(ASCIIToUTF16("012 456\n\n90"));
EXPECT_EQ(4U, render_text->GetNumLines());
// Move cursor right to the end of the text.
@@ -3837,7 +3857,7 @@ TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection_Multiline) {
TEST_F(RenderTextTest, CenteredDisplayOffset) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefghij"));
+ render_text->SetText(ASCIIToUTF16("abcdefghij"));
render_text->SetHorizontalAlignment(ALIGN_CENTER);
const int kEnlargement = 10;
@@ -4016,7 +4036,7 @@ TEST_F(RenderTextTest, MoveLeftRightByWordInBidiText_TestEndOfText) {
TEST_F(RenderTextTest, MoveLeftRightByWordInTextWithMultiSpaces) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abc def"));
+ render_text->SetText(ASCIIToUTF16("abc def"));
render_text->SetCursorPosition(5);
render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, SELECTION_NONE);
#if defined(OS_WIN)
@@ -4099,14 +4119,14 @@ TEST_F(RenderTextTest, DirectedSelections) {
return GetSelectedText(render_text);
};
- render_text->SetText(UTF8ToUTF16("01234"));
+ render_text->SetText(ASCIIToUTF16("01234"));
// Test Right, then Left. LTR.
// Undirected, or forward when kSelectionIsAlwaysDirected.
render_text->SelectRange({2, 4});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text));
- EXPECT_EQ(UTF8ToUTF16("234"), ResultAfter(CURSOR_RIGHT));
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_LEFT));
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("234"), ResultAfter(CURSOR_RIGHT));
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_LEFT));
// Test collapsing the selection. This always ignores any existing direction.
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, SELECTION_NONE);
@@ -4114,12 +4134,12 @@ TEST_F(RenderTextTest, DirectedSelections) {
// Undirected, or backward when kSelectionIsAlwaysDirected.
render_text->SelectRange({4, 2});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text));
if (RenderText::kSelectionIsAlwaysDirected)
- EXPECT_EQ(UTF8ToUTF16("3"), ResultAfter(CURSOR_RIGHT)); // Keep left.
+ EXPECT_EQ(ASCIIToUTF16("3"), ResultAfter(CURSOR_RIGHT)); // Keep left.
else
- EXPECT_EQ(UTF8ToUTF16("234"), ResultAfter(CURSOR_RIGHT)); // Pick right.
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_LEFT));
+ EXPECT_EQ(ASCIIToUTF16("234"), ResultAfter(CURSOR_RIGHT)); // Pick right.
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_LEFT));
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, SELECTION_NONE);
EXPECT_EQ(Range(2, 2), render_text->selection()); // Collapse left.
@@ -4127,22 +4147,22 @@ TEST_F(RenderTextTest, DirectedSelections) {
// Test Left, then Right. LTR.
// Undirected, or forward when kSelectionIsAlwaysDirected.
render_text->SelectRange({2, 4});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text)); // Sanity check,
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text)); // Sanity check,
if (RenderText::kSelectionIsAlwaysDirected)
- EXPECT_EQ(UTF8ToUTF16("2"), ResultAfter(CURSOR_LEFT)); // Keep right.
+ EXPECT_EQ(ASCIIToUTF16("2"), ResultAfter(CURSOR_LEFT)); // Keep right.
else
- EXPECT_EQ(UTF8ToUTF16("123"), ResultAfter(CURSOR_LEFT)); // Pick left.
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_RIGHT));
+ EXPECT_EQ(ASCIIToUTF16("123"), ResultAfter(CURSOR_LEFT)); // Pick left.
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_RIGHT));
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE);
EXPECT_EQ(Range(4, 4), render_text->selection()); // Collapse right.
// Undirected, or backward when kSelectionIsAlwaysDirected.
render_text->SelectRange({4, 2});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text));
- EXPECT_EQ(UTF8ToUTF16("123"), ResultAfter(CURSOR_LEFT));
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_RIGHT));
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("123"), ResultAfter(CURSOR_LEFT));
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_RIGHT));
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE);
EXPECT_EQ(Range(4, 4), render_text->selection()); // Collapse right.
@@ -4211,7 +4231,7 @@ TEST_F(RenderTextTest, DirectedSelections_Multiline) {
return GetSelectedText(render_text);
};
- render_text->SetText(UTF8ToUTF16("01234\n56789\nabcde"));
+ render_text->SetText(ASCIIToUTF16("01234\n56789\nabcde"));
render_text->SetMultiline(true);
render_text->SetDisplayRect(Rect(500, 500));
ResetCursorX();
@@ -4219,9 +4239,9 @@ TEST_F(RenderTextTest, DirectedSelections_Multiline) {
// Test Down, then Up. LTR.
// Undirected, or forward when kSelectionIsAlwaysDirected.
render_text->SelectRange({2, 4});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text));
- EXPECT_EQ(UTF8ToUTF16("234\n5678"), ResultAfter(CURSOR_DOWN));
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_UP));
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("234\n5678"), ResultAfter(CURSOR_DOWN));
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_UP));
// Test collapsing the selection. This always ignores any existing direction.
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_LEFT, SELECTION_NONE);
@@ -4230,40 +4250,40 @@ TEST_F(RenderTextTest, DirectedSelections_Multiline) {
// Undirected, or backward when kSelectionIsAlwaysDirected.
ResetCursorX(); // Reset cached cursor x position.
render_text->SelectRange({4, 2});
- EXPECT_EQ(UTF8ToUTF16("23"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("23"), GetSelectedText(render_text));
if (RenderText::kSelectionIsAlwaysDirected) {
- EXPECT_EQ(UTF8ToUTF16("4\n56"), ResultAfter(CURSOR_DOWN)); // Keep left.
+ EXPECT_EQ(ASCIIToUTF16("4\n56"), ResultAfter(CURSOR_DOWN)); // Keep left.
} else {
- EXPECT_EQ(UTF8ToUTF16("234\n5678"),
+ EXPECT_EQ(ASCIIToUTF16("234\n5678"),
ResultAfter(CURSOR_DOWN)); // Pick right.
}
- EXPECT_EQ(UTF8ToUTF16("23"), ResultAfter(CURSOR_UP));
+ EXPECT_EQ(ASCIIToUTF16("23"), ResultAfter(CURSOR_UP));
// Test with multi-line selection.
// Undirected, or forward when kSelectionIsAlwaysDirected.
ResetCursorX();
render_text->SelectRange({2, 7}); // Select multi-line.
- EXPECT_EQ(UTF8ToUTF16("234\n5"), GetSelectedText(render_text));
- EXPECT_EQ(UTF8ToUTF16("234\n56789\na"), ResultAfter(CURSOR_DOWN));
- EXPECT_EQ(UTF8ToUTF16("234\n5"), ResultAfter(CURSOR_UP));
+ EXPECT_EQ(ASCIIToUTF16("234\n5"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("234\n56789\na"), ResultAfter(CURSOR_DOWN));
+ EXPECT_EQ(ASCIIToUTF16("234\n5"), ResultAfter(CURSOR_UP));
// Undirected, or backward when kSelectionIsAlwaysDirected.
ResetCursorX();
render_text->SelectRange({7, 2}); // Select multi-line.
- EXPECT_EQ(UTF8ToUTF16("234\n5"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("234\n5"), GetSelectedText(render_text));
if (RenderText::kSelectionIsAlwaysDirected) {
- EXPECT_EQ(UTF8ToUTF16("6"), ResultAfter(CURSOR_DOWN)); // Keep left.
+ EXPECT_EQ(ASCIIToUTF16("6"), ResultAfter(CURSOR_DOWN)); // Keep left.
} else {
- EXPECT_EQ(UTF8ToUTF16("234\n56789\na"),
+ EXPECT_EQ(ASCIIToUTF16("234\n56789\na"),
ResultAfter(CURSOR_DOWN)); // Pick right.
}
- EXPECT_EQ(UTF8ToUTF16("234\n5"), ResultAfter(CURSOR_UP));
+ EXPECT_EQ(ASCIIToUTF16("234\n5"), ResultAfter(CURSOR_UP));
}
TEST_F(RenderTextTest, StringSizeSanity) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("Hello World"));
+ render_text->SetText(ASCIIToUTF16("Hello World"));
const Size string_size = render_text->GetStringSize();
EXPECT_GT(string_size.width(), 0);
EXPECT_GT(string_size.height(), 0);
@@ -4298,7 +4318,7 @@ TEST_F(RenderTextTest, StringSizeEmptyString) {
EXPECT_EQ(0, render_text->GetStringSize().width());
EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
- render_text->SetText(UTF8ToUTF16(" "));
+ render_text->SetText(ASCIIToUTF16(" "));
EXPECT_EQ(font_list.GetHeight(), render_text->GetStringSize().height());
EXPECT_EQ(font_list.GetBaseline(), render_text->GetBaseline());
}
@@ -4359,7 +4379,7 @@ TEST_F(RenderTextTest, StringSizeRespectsFontListMetrics) {
TEST_F(RenderTextTest, StringSizeMultiline) {
SetGlyphWidth(5);
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("Hello\nWorld"));
+ render_text->SetText(ASCIIToUTF16("Hello\nWorld"));
const Size string_size = render_text->GetStringSize();
EXPECT_EQ(55, string_size.width());
@@ -4367,10 +4387,10 @@ TEST_F(RenderTextTest, StringSizeMultiline) {
render_text->SetMultiline(true);
EXPECT_EQ(55, render_text->TotalLineWidth());
- EXPECT_EQ(
- 30, render_text->GetLineSize(SelectionModel(0, CURSOR_FORWARD)).width());
- EXPECT_EQ(
- 25, render_text->GetLineSize(SelectionModel(6, CURSOR_FORWARD)).width());
+ EXPECT_FLOAT_EQ(
+ 30, render_text->GetLineSizeF(SelectionModel(0, CURSOR_FORWARD)).width());
+ EXPECT_FLOAT_EQ(
+ 25, render_text->GetLineSizeF(SelectionModel(6, CURSOR_FORWARD)).width());
// |GetStringSize()| of multi-line text does not include newline character.
EXPECT_EQ(25, render_text->GetStringSize().width());
// Expect height to be 2 times the font height. This assumes simple strings
@@ -4381,7 +4401,7 @@ TEST_F(RenderTextTest, StringSizeMultiline) {
TEST_F(RenderTextTest, MinLineHeight) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("Hello!"));
+ render_text->SetText(ASCIIToUTF16("Hello!"));
SizeF default_size = render_text->GetStringSizeF();
ASSERT_NE(0, default_size.height());
ASSERT_NE(0, default_size.width());
@@ -4401,9 +4421,9 @@ TEST_F(RenderTextTest, MinLineHeight) {
TEST_F(RenderTextTest, DefaultLineHeights) {
RenderText* render_text = GetRenderText();
render_text->SetText(
- UTF8ToUTF16("A quick brown fox jumped over the lazy dog!"));
+ ASCIIToUTF16("A quick brown fox jumped over the lazy dog!"));
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
const FontList body2_font = FontList().DeriveWithSizeDelta(-1);
#else
const FontList body2_font;
@@ -4433,6 +4453,151 @@ TEST_F(RenderTextTest, DefaultLineHeights) {
}
}
+TEST_F(RenderTextTest, TextSize) {
+ // Set a fractional glyph size to trigger floating rounding logic.
+ const float kGlyphWidth = 1.2;
+ const float kGlyphHeight = 9.2;
+ SetGlyphWidth(kGlyphWidth);
+ SetGlyphHeight(kGlyphHeight);
+
+ RenderText* render_text = GetRenderText();
+ for (size_t text_length = 0; text_length < 10; ++text_length) {
+ render_text->SetText(ASCIIToUTF16(std::string(text_length, 'x')));
+
+ // Ensures that conversion from float to integer ceils the values.
+ const float expected_width = text_length * kGlyphWidth;
+ const float expected_height = kGlyphHeight;
+ const int expected_ceiled_width = std::ceil(expected_width);
+ const int expected_ceiled_height = std::ceil(expected_height);
+
+ EXPECT_FLOAT_EQ(expected_width, render_text->GetStringSizeF().width());
+ EXPECT_FLOAT_EQ(expected_height, render_text->GetStringSizeF().height());
+ EXPECT_EQ(expected_ceiled_width, render_text->GetStringSize().width());
+ EXPECT_EQ(expected_ceiled_height, render_text->GetStringSize().height());
+
+ EXPECT_FLOAT_EQ(expected_width, render_text->TotalLineWidth());
+
+ // With cursor disabled, the content width is the same as string width.
+ render_text->SetCursorEnabled(false);
+ EXPECT_FLOAT_EQ(expected_width, render_text->GetContentWidthF());
+ EXPECT_EQ(expected_ceiled_width, render_text->GetContentWidth());
+
+ render_text->SetCursorEnabled(true);
+ // The cursor is drawn one pixel beyond the int-enclosing text bounds.
+ EXPECT_FLOAT_EQ(expected_ceiled_width + 1, render_text->GetContentWidthF());
+ EXPECT_EQ(expected_ceiled_width + 1, render_text->GetContentWidth());
+ }
+}
+
+TEST_F(RenderTextTest, TextSizeMultiline) {
+ // Set a fractional glyph size to trigger floating rounding logic.
+ const float kGlyphWidth = 1.2;
+ const float kGlyphHeight = 9.2;
+ SetGlyphWidth(kGlyphWidth);
+ SetGlyphHeight(kGlyphHeight);
+
+ RenderText* render_text = GetRenderText();
+ render_text->SetMultiline(true);
+
+ for (size_t line = 0; line < 10; ++line) {
+ if (line != 0)
+ render_text->AppendText(ASCIIToUTF16("\n"));
+ const int text_length = line;
+ render_text->AppendText(ASCIIToUTF16(std::string(text_length, 'x')));
+
+ // Ensures that conversion from float to integer ceils the values.
+ const float expected_width = text_length * kGlyphWidth;
+ const float expected_height = (line + 1) * kGlyphHeight;
+ const int expected_ceiled_width = std::ceil(expected_width);
+ const int expected_ceiled_height = std::ceil(expected_height);
+
+ EXPECT_FLOAT_EQ(expected_width, render_text->GetStringSizeF().width());
+ EXPECT_FLOAT_EQ(expected_height, render_text->GetStringSizeF().height());
+ EXPECT_EQ(expected_ceiled_width, render_text->GetStringSize().width());
+ EXPECT_EQ(expected_ceiled_height, render_text->GetStringSize().height());
+
+ const int total_glyphs = render_text->text().length();
+ EXPECT_FLOAT_EQ(total_glyphs * kGlyphWidth, render_text->TotalLineWidth());
+
+ // With cursor disabled, the content width is the same as string width.
+ render_text->SetCursorEnabled(false);
+ EXPECT_FLOAT_EQ(expected_width, render_text->GetContentWidthF());
+ EXPECT_EQ(expected_ceiled_width, render_text->GetContentWidth());
+
+ render_text->SetCursorEnabled(true);
+ // The cursor is drawn one pixel beyond the int-enclosing text bounds.
+ EXPECT_FLOAT_EQ(expected_ceiled_width + 1, render_text->GetContentWidthF());
+ EXPECT_EQ(expected_ceiled_width + 1, render_text->GetContentWidth());
+ }
+}
+
+TEST_F(RenderTextTest, LineSizeMultiline) {
+ // Set a fractional glyph size to trigger floating rounding logic.
+ const float kGlyphWidth = 1.2;
+ SetGlyphWidth(kGlyphWidth);
+
+ RenderText* render_text = GetRenderText();
+ render_text->SetMultiline(true);
+ render_text->SetText(ASCIIToUTF16("xx\nxxx\nxxxxx"));
+
+ const float expected_line1_size = 3 * kGlyphWidth;
+ const float expected_line2_size = 4 * kGlyphWidth;
+ const float expected_line3_size = 5 * kGlyphWidth;
+
+ EXPECT_FLOAT_EQ(
+ expected_line1_size,
+ render_text->GetLineSizeF(SelectionModel(1, CURSOR_FORWARD)).width());
+ EXPECT_FLOAT_EQ(
+ expected_line2_size,
+ render_text->GetLineSizeF(SelectionModel(4, CURSOR_FORWARD)).width());
+ EXPECT_FLOAT_EQ(
+ expected_line3_size,
+ render_text->GetLineSizeF(SelectionModel(10, CURSOR_FORWARD)).width());
+}
+
+TEST_F(RenderTextTest, TextPosition) {
+ // Set a fractional glyph size to trigger floating rounding logic.
+ // This test sets a fixed fractional width and height for glyphs to ensure
+ // that computations are fixed (i.e. not font or system dependent).
+ const float kGlyphWidth = 5.4;
+ const float kGlyphHeight = 9.2;
+ SetGlyphWidth(kGlyphWidth);
+ SetGlyphHeight(kGlyphHeight);
+
+ const int kGlyphCount = 3;
+
+ RenderText* render_text = GetRenderText();
+ render_text->SetText(ASCIIToUTF16(std::string(kGlyphCount, 'x')));
+ render_text->SetDisplayRect(Rect(1, 1, 25, 12));
+ render_text->SetCursorEnabled(false);
+ render_text->SetVerticalAlignment(ALIGN_TOP);
+
+ // Content width is 16.2px. Extra space inside display rect is 8.8px
+ // (i.e. 25px - 16.2px) which is used for alignment.
+ const float expected_content_width = kGlyphCount * kGlyphWidth;
+ EXPECT_FLOAT_EQ(expected_content_width, render_text->GetContentWidthF());
+ EXPECT_FLOAT_EQ(expected_content_width, render_text->TotalLineWidth());
+
+ render_text->SetHorizontalAlignment(ALIGN_LEFT);
+ EXPECT_EQ(1, render_text->GetLineOffset(0).x());
+
+ EXPECT_EQ(Rect(1, 1, 6, 10), GetSubstringBoundsUnion(Range(0, 1)));
+ EXPECT_EQ(Rect(6, 1, 6, 10), GetSubstringBoundsUnion(Range(1, 2)));
+ EXPECT_EQ(Rect(11, 1, 7, 10), GetSubstringBoundsUnion(Range(2, 3)));
+
+ render_text->SetHorizontalAlignment(ALIGN_CENTER);
+ EXPECT_EQ(5, render_text->GetLineOffset(0).x());
+ EXPECT_EQ(Rect(5, 1, 6, 10), GetSubstringBoundsUnion(Range(0, 1)));
+ EXPECT_EQ(Rect(10, 1, 6, 10), GetSubstringBoundsUnion(Range(1, 2)));
+ EXPECT_EQ(Rect(15, 1, 7, 10), GetSubstringBoundsUnion(Range(2, 3)));
+
+ render_text->SetHorizontalAlignment(ALIGN_RIGHT);
+ EXPECT_EQ(9, render_text->GetLineOffset(0).x());
+ EXPECT_EQ(Rect(9, 1, 6, 10), GetSubstringBoundsUnion(Range(0, 1)));
+ EXPECT_EQ(Rect(14, 1, 6, 10), GetSubstringBoundsUnion(Range(1, 2)));
+ EXPECT_EQ(Rect(19, 1, 7, 10), GetSubstringBoundsUnion(Range(2, 3)));
+}
+
TEST_F(RenderTextTest, SetFontList) {
RenderText* render_text = GetRenderText();
render_text->SetFontList(
@@ -4454,7 +4619,7 @@ TEST_F(RenderTextTest, StringSizeBoldWidth) {
render_text->SetFontList(FontList("Arial, 20px"));
#endif // defined(OS_FUCHSIA)
- render_text->SetText(UTF8ToUTF16("Hello World"));
+ render_text->SetText(ASCIIToUTF16("Hello World"));
const int plain_width = render_text->GetStringSize().width();
EXPECT_GT(plain_width, 0);
@@ -4479,7 +4644,7 @@ TEST_F(RenderTextTest, StringSizeBoldWidth) {
TEST_F(RenderTextTest, StringSizeHeight) {
base::string16 cases[] = {
- UTF8ToUTF16("Hello World!"), // English
+ ASCIIToUTF16("Hello World!"), // English
UTF8ToUTF16("\u6328\u62f6"), // Japanese 挨拶 (characters press & near)
UTF8ToUTF16("\u0915\u093f"), // Hindi कि (letter KA with vowel I)
UTF8ToUTF16("\u05e0\u05b8"), // Hebrew נָ (letter NUN and point QAMATS)
@@ -4507,7 +4672,7 @@ TEST_F(RenderTextTest, StringSizeHeight) {
TEST_F(RenderTextTest, GetBaselineSanity) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("Hello World"));
+ render_text->SetText(ASCIIToUTF16("Hello World"));
const int baseline = render_text->GetBaseline();
EXPECT_GT(baseline, 0);
}
@@ -4528,6 +4693,8 @@ TEST_F(RenderTextTest, GetCursorBoundsInReplacementMode) {
TEST_F(RenderTextTest, GetCursorBoundsWithGraphemes) {
constexpr int kGlyphWidth = 10;
SetGlyphWidth(kGlyphWidth);
+ constexpr int kGlyphHeight = 12;
+ SetGlyphHeight(kGlyphHeight);
RenderText* render_text = GetRenderText();
render_text->SetText(
@@ -4535,27 +4702,24 @@ TEST_F(RenderTextTest, GetCursorBoundsWithGraphemes) {
render_text->SetDisplayRect(Rect(100, 20));
render_text->SetVerticalAlignment(ALIGN_TOP);
- const int line_height =
- render_text->GetLineSize(SelectionModel(0, CURSOR_FORWARD)).height();
-
static const size_t kGraphemeBoundaries[] = {0, 2, 4, 6, 7};
for (size_t i = 0; i < base::size(kGraphemeBoundaries); ++i) {
const size_t text_offset = kGraphemeBoundaries[i];
EXPECT_EQ(render_text->GetCursorBounds(
SelectionModel(text_offset, CURSOR_FORWARD), true),
- Rect(i * kGlyphWidth, 0, 1, line_height));
+ Rect(i * kGlyphWidth, 0, 1, kGlyphHeight));
EXPECT_EQ(render_text->GetCursorBounds(
SelectionModel(text_offset, CURSOR_FORWARD), false),
- Rect(i * kGlyphWidth, 0, kGlyphWidth, line_height));
+ Rect(i * kGlyphWidth, 0, kGlyphWidth, kGlyphHeight));
}
// Check cursor bounds at end of text.
EXPECT_EQ(
render_text->GetCursorBounds(SelectionModel(10, CURSOR_FORWARD), true),
- Rect(50, 0, 1, line_height));
+ Rect(50, 0, 1, kGlyphHeight));
EXPECT_EQ(
render_text->GetCursorBounds(SelectionModel(10, CURSOR_FORWARD), false),
- Rect(50, 0, 1, line_height));
+ Rect(50, 0, 1, kGlyphHeight));
}
TEST_F(RenderTextTest, GetTextOffset) {
@@ -4569,7 +4733,7 @@ TEST_F(RenderTextTest, GetTextOffset) {
ResetRenderTextInstance();
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefg"));
render_text->SetFontList(FontList("Arial, 13px"));
// Set display area's size equal to the font size.
@@ -4623,7 +4787,7 @@ TEST_F(RenderTextTest, GetTextOffsetHorizontalDefaultInRTL) {
ResetRenderTextInstance();
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefg"));
render_text->SetFontList(FontList("Arial, 13px"));
const int kEnlargement = 2;
const Size font_size(render_text->GetContentWidth() + kEnlargement,
@@ -4635,9 +4799,9 @@ TEST_F(RenderTextTest, GetTextOffsetHorizontalDefaultInRTL) {
SetRTL(was_rtl);
}
-TEST_F(RenderTextTest, GetTextOffsetVerticalAignment) {
+TEST_F(RenderTextTest, GetTextOffsetVerticalAlignment) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefg"));
render_text->SetFontList(FontList("Arial, 13px"));
// Set display area's size equal to the font size.
@@ -4671,11 +4835,11 @@ TEST_F(RenderTextTest, GetTextOffsetVerticalAignment) {
EXPECT_EQ(kEnlargementY, offset.y());
}
-TEST_F(RenderTextTest, GetTextOffsetVerticalAignment_Multiline) {
+TEST_F(RenderTextTest, GetTextOffsetVerticalAlignment_Multiline) {
RenderText* render_text = GetRenderText();
render_text->SetMultiline(true);
render_text->SetMaxLines(2);
- render_text->SetText(UTF8ToUTF16("abcdefg hijklmn"));
+ render_text->SetText(ASCIIToUTF16("abcdefg hijklmn"));
render_text->SetFontList(FontList("Arial, 13px"));
// Set display area's size equal to the font size.
@@ -4711,7 +4875,7 @@ TEST_F(RenderTextTest, GetTextOffsetVerticalAignment_Multiline) {
TEST_F(RenderTextTest, SetDisplayOffset) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefg"));
render_text->SetFontList(FontList("Arial, 13px"));
const Size font_size(render_text->GetContentWidth(),
@@ -4796,9 +4960,9 @@ TEST_F(RenderTextTest, SameFontForParentheses) {
base::string16 text;
} cases[] = {
// English(English)
- {UTF8ToUTF16("Hello World(a)")},
+ {ASCIIToUTF16("Hello World(a)")},
// English(English)English
- {UTF8ToUTF16("Hello World(a)Hello World")},
+ {ASCIIToUTF16("Hello World(a)Hello World")},
// Japanese(English)
{UTF8ToUTF16("\u6328\u62f6(a)")},
@@ -4861,13 +5025,13 @@ TEST_F(RenderTextTest, SameFontForParentheses) {
// caret is drawn at high DPI. crbug.com/164100.
TEST_F(RenderTextTest, CaretWidth) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefg"));
EXPECT_GE(render_text->GetUpdatedCursorBounds().width(), 1);
}
TEST_F(RenderTextTest, SelectWord) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16(" foo a.bc.d bar"));
+ render_text->SetText(ASCIIToUTF16(" foo a.bc.d bar"));
struct {
size_t cursor;
@@ -4908,16 +5072,16 @@ TEST_F(RenderTextTest, LastWordSelected) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16(kTestURL1));
+ render_text->SetText(ASCIIToUTF16(kTestURL1));
render_text->SetCursorPosition(kTestURL1.length());
render_text->SelectWord();
- EXPECT_EQ(UTF8ToUTF16("com"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("com"), GetSelectedText(render_text));
EXPECT_FALSE(render_text->selection().is_reversed());
- render_text->SetText(UTF8ToUTF16(kTestURL2));
+ render_text->SetText(ASCIIToUTF16(kTestURL2));
render_text->SetCursorPosition(kTestURL2.length());
render_text->SelectWord();
- EXPECT_EQ(UTF8ToUTF16("/"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("/"), GetSelectedText(render_text));
EXPECT_FALSE(render_text->selection().is_reversed());
}
@@ -4928,16 +5092,16 @@ TEST_F(RenderTextTest, SelectMultipleWords) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16(kTestURL));
+ render_text->SetText(ASCIIToUTF16(kTestURL));
render_text->SelectRange(Range(16, 20));
render_text->SelectWord();
- EXPECT_EQ(UTF8ToUTF16("google.com"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text));
EXPECT_FALSE(render_text->selection().is_reversed());
// SelectWord should preserve the selection direction.
render_text->SelectRange(Range(20, 16));
render_text->SelectWord();
- EXPECT_EQ(UTF8ToUTF16("google.com"), GetSelectedText(render_text));
+ EXPECT_EQ(ASCIIToUTF16("google.com"), GetSelectedText(render_text));
EXPECT_TRUE(render_text->selection().is_reversed());
}
@@ -4946,7 +5110,7 @@ TEST_F(RenderTextTest, DisplayRectShowsCursorLTR) {
ASSERT_FALSE(base::i18n::ICUIsRTL());
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefghijklmnopqrstuvwxzyabcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefghijklmnopqrstuvwxzyabcdefg"));
render_text->SetCursorPosition(render_text->text().length());
int width = render_text->GetStringSize().width();
ASSERT_GT(width, 10);
@@ -5004,7 +5168,7 @@ TEST_F(RenderTextTest, DisplayRectShowsCursorRTL) {
// Reset the render text instance since the locale was changed.
ResetRenderTextInstance();
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefghijklmnopqrstuvwxzyabcdefg"));
+ render_text->SetText(ASCIIToUTF16("abcdefghijklmnopqrstuvwxzyabcdefg"));
render_text->SetCursorPosition(0);
int width = render_text->GetStringSize().width();
ASSERT_GT(width, 10);
@@ -5103,7 +5267,7 @@ TEST_F(RenderTextTest, WhitespaceDoesBreak) {
// Note that the hyphens that Wikipedia uses are different. English uses
// ASCII (U+002D) "hyphen minus", Hebrew uses the U+2013 "EN Dash".
const base::string16 ascii_space_he = UTF8ToUTF16("סיבית – ויקיפדיה");
- const base::string16 ascii_space_en = UTF8ToUTF16("Bit - Wikipedia");
+ const base::string16 ascii_space_en = ASCIIToUTF16("Bit - Wikipedia");
// This says "thank you very much" with a full-width non-ascii space (U+3000).
const base::string16 full_width_space = UTF8ToUTF16("ども ありがと");
@@ -5285,7 +5449,7 @@ TEST_F(RenderTextTest, Multiline_IgnoreElide) {
RenderText* render_text = GetRenderText();
render_text->SetElideBehavior(ELIDE_TAIL);
render_text->SetDisplayRect(Rect(20, 1000));
- render_text->SetText(base::UTF8ToUTF16(kTestString));
+ render_text->SetText(base::ASCIIToUTF16(kTestString));
EXPECT_NE(base::string16::npos,
render_text->GetDisplayText().find(base::UTF8ToUTF16(kEllipsis)));
@@ -5304,17 +5468,17 @@ TEST_F(RenderTextTest, Multiline_NewlineCharacterReplacement) {
ResetRenderTextInstance();
RenderText* render_text = GetRenderText();
render_text->SetDisplayRect(Rect(200, 1000));
- render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+ render_text->SetText(ASCIIToUTF16(kTestStrings[i]));
base::string16 display_text = render_text->GetDisplayText();
// If RenderText is not multiline, the newline characters are replaced
// by symbols, therefore the character should be changed.
- EXPECT_NE(UTF8ToUTF16(kTestStrings[i]), render_text->GetDisplayText());
+ EXPECT_NE(ASCIIToUTF16(kTestStrings[i]), render_text->GetDisplayText());
// Setting multiline will fix this, the newline characters will be back
// to the original text.
render_text->SetMultiline(true);
- EXPECT_EQ(UTF8ToUTF16(kTestStrings[i]), render_text->GetDisplayText());
+ EXPECT_EQ(ASCIIToUTF16(kTestStrings[i]), render_text->GetDisplayText());
}
}
@@ -5405,7 +5569,7 @@ TEST_F(RenderTextTest, Multiline_WordWrapBehavior) {
};
RenderTextHarfBuzz* render_text = GetRenderText();
render_text->SetMultiline(true);
- render_text->SetText(UTF8ToUTF16("foo fooooo foo"));
+ render_text->SetText(ASCIIToUTF16("foo fooooo foo"));
SetGlyphWidth(kGlyphSize);
render_text->SetDisplayRect(Rect(0, 0, kGlyphSize * 4, 0));
@@ -5522,12 +5686,12 @@ TEST_F(RenderTextTest, Multiline_SurrogatePairsOrCombiningChars) {
const int display_width;
const Range char_ranges[3];
} kTestScenarios[] = {
- { text_surrogate + text_surrogate + text_surrogate,
- kSurrogateWidth / 2 * 3,
- { Range(0, 2), Range(2, 4), Range(4, 6) } },
- { text_surrogate + UTF8ToUTF16(" ") + kCombiningChars,
- std::min(kSurrogateWidth, kCombiningCharsWidth) / 2,
- { Range(0, 2), Range(2, 3), Range(3, 5) } },
+ {text_surrogate + text_surrogate + text_surrogate,
+ kSurrogateWidth / 2 * 3,
+ {Range(0, 2), Range(2, 4), Range(4, 6)}},
+ {text_surrogate + ASCIIToUTF16(" ") + kCombiningChars,
+ std::min(kSurrogateWidth, kCombiningCharsWidth) / 2,
+ {Range(0, 2), Range(2, 3), Range(3, 5)}},
};
for (size_t i = 0; i < base::size(kTestScenarios); ++i) {
@@ -5550,7 +5714,7 @@ TEST_F(RenderTextTest, Multiline_SurrogatePairsOrCombiningChars) {
TEST_F(RenderTextTest, Multiline_ZeroWidthChars) {
RenderTextHarfBuzz* render_text = GetRenderText();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Don't use Helvetica Neue on 10.10 - it has a buggy zero-width space that
// actually gets some width. See http://crbug.com/799333.
if (base::mac::IsOS10_10())
@@ -5561,11 +5725,11 @@ TEST_F(RenderTextTest, Multiline_ZeroWidthChars) {
render_text->SetWordWrapBehavior(WRAP_LONG_WORDS);
const base::char16 kZeroWidthSpace = {0x200B};
- const base::string16 text(UTF8ToUTF16("test") + kZeroWidthSpace +
- UTF8ToUTF16("\n") + kZeroWidthSpace +
- UTF8ToUTF16("test."));
+ const base::string16 text(ASCIIToUTF16("test") + kZeroWidthSpace +
+ ASCIIToUTF16("\n") + kZeroWidthSpace +
+ ASCIIToUTF16("test."));
const int kTestWidth =
- GetStringWidth(UTF8ToUTF16("test"), render_text->font_list());
+ GetStringWidth(ASCIIToUTF16("test"), render_text->font_list());
const Range char_ranges[3] = {Range(0, 6), Range(6, 11), Range(11, 12)};
render_text->SetText(text);
@@ -5589,7 +5753,7 @@ TEST_F(RenderTextTest, Multiline_ZeroWidthNewline) {
RenderTextHarfBuzz* render_text = GetRenderText();
render_text->SetMultiline(true);
- const base::string16 text(UTF8ToUTF16("\n\n"));
+ const base::string16 text(ASCIIToUTF16("\n\n"));
render_text->SetText(text);
EXPECT_EQ(3u, render_text->GetNumLines());
for (const auto& line : test_api()->lines()) {
@@ -5658,7 +5822,7 @@ TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) {
for (size_t i = 0; i < base::size(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
- render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+ render_text->SetText(ASCIIToUTF16(kTestStrings[i]));
render_text->Draw(canvas());
EXPECT_EQ(1U, test_api()->lines().size());
@@ -5669,7 +5833,7 @@ TEST_F(RenderTextTest, ControlCharacterReplacement) {
static const char kTextWithControlCharacters[] = "\b\r\a\t\n\v\f";
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16(kTextWithControlCharacters));
+ render_text->SetText(ASCIIToUTF16(kTextWithControlCharacters));
// The control characters should have been replaced by their symbols.
EXPECT_EQ(WideToUTF16(L"␈␍␇␉␊␋␌"), render_text->GetDisplayText());
@@ -5680,8 +5844,8 @@ TEST_F(RenderTextTest, ControlCharacterReplacement) {
// The generic control characters should have been replaced by the replacement
// codepoints.
- render_text->SetText(WideToUTF16(L"\u008f\u0080"));
- EXPECT_EQ(WideToUTF16(L"\ufffd\ufffd"), render_text->GetDisplayText());
+ render_text->SetText(UTF8ToUTF16("\u008f\u0080"));
+ EXPECT_EQ(UTF8ToUTF16("\ufffd\ufffd"), render_text->GetDisplayText());
}
TEST_F(RenderTextTest, PrivateUseCharacterReplacement) {
@@ -5690,20 +5854,20 @@ TEST_F(RenderTextTest, PrivateUseCharacterReplacement) {
// The private use characters should have been replaced. If the code point is
// a surrogate pair, it needs to be replaced by two characters.
- EXPECT_EQ(WideToUTF16(L"xx\ufffd\ufffda\ufffdz"),
+ EXPECT_EQ(UTF8ToUTF16("xx\ufffd\ufffda\ufffdz"),
render_text->GetDisplayText());
// The private use characters from Area-B must be replaced. The rewrite step
// replaced 2 characters by 1 character.
render_text->SetText(WideToUTF16(L"x\U00100000\U00100001\U00100002"));
- EXPECT_EQ(WideToUTF16(L"x\ufffd\ufffd\ufffd"), render_text->GetDisplayText());
+ EXPECT_EQ(UTF8ToUTF16("x\ufffd\ufffd\ufffd"), render_text->GetDisplayText());
}
TEST_F(RenderTextTest, InvalidSurrogateCharacterReplacement) {
// Text with invalid surrogates (surrogates low 0xDC00 and high 0xD800).
RenderText* render_text = GetRenderText();
render_text->SetText(WideToUTF16(L"\xDC00\xD800"));
- EXPECT_EQ(WideToUTF16(L"\ufffd\ufffd"), render_text->GetDisplayText());
+ EXPECT_EQ(UTF8ToUTF16("\ufffd\ufffd"), render_text->GetDisplayText());
}
// Make sure the horizontal positions of runs in a line (left-to-right for
@@ -5878,7 +6042,7 @@ TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemePartition) {
run.shape.width = 20;
RenderTextHarfBuzz* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcd"));
+ render_text->SetText(ASCIIToUTF16("abcd"));
for (size_t i = 0; i < base::size(cases); ++i) {
std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 2,
@@ -5889,8 +6053,9 @@ TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemePartition) {
for (size_t j = 0; j < 4; ++j) {
SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j));
- EXPECT_EQ(cases[i].bounds[j],
- run.GetGraphemeBounds(render_text, j).Round());
+ RangeF bounds_f = run.GetGraphemeBounds(render_text, j);
+ Range bounds(std::round(bounds_f.start()), std::round(bounds_f.end()));
+ EXPECT_EQ(cases[i].bounds[j], bounds);
}
}
}
@@ -5969,7 +6134,7 @@ TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmoji) {
TEST_F(RenderTextTest, HarfBuzz_BreakRunsByNewline) {
RenderText* render_text = GetRenderText();
render_text->SetMultiline(true);
- render_text->SetText(WideToUTF16(L"x\ny"));
+ render_text->SetText(ASCIIToUTF16("x\ny"));
EXPECT_EQ(ToString16Vec({"x", "\n", "y"}), GetRunListStrings());
EXPECT_EQ("[0][1][2]", GetRunListStructureString());
@@ -6003,7 +6168,7 @@ TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmojiVariationSelectors) {
// ☎ (U+260E BLACK TELEPHONE) and U+FE0F (a variation selector) combine to
// form (on some platforms), ☎️, a red (or blue) telephone. The run can
// not break between the codepoints, or the incorrect glyph will be chosen.
- render_text->SetText(WideToUTF16(L"z\u260E\uFE0Fy"));
+ render_text->SetText(UTF8ToUTF16("z\u260E\uFE0Fy"));
render_text->SetDisplayRect(Rect(1000, 50));
EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F", "y"}), GetRunListStrings());
EXPECT_EQ("[0][1->2][3]", GetRunListStructureString());
@@ -6015,7 +6180,7 @@ TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmojiVariationSelectors) {
EXPECT_EQ(gfx::Range(1, 1), render_text->selection());
EXPECT_EQ(1 * kGlyphWidth, render_text->GetUpdatedCursorBounds().x());
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Early versions of macOS provide a tofu glyph for the variation selector.
// Bail out early except on 10.12 and above.
if (base::mac::IsAtMostOS10_11())
@@ -6045,7 +6210,7 @@ TEST_F(RenderTextTest, HarfBuzz_OrphanedVariationSelector) {
// It should never happen in normal usage, but a variation selector can appear
// by itself. In this case, it can form its own text run, with no glyphs.
- render_text->SetText(WideToUTF16(L"\uFE0F"));
+ render_text->SetText(UTF8ToUTF16("\uFE0F"));
EXPECT_EQ(ToString16Vec({"\uFE0F"}), GetRunListStrings());
EXPECT_EQ("[0]", GetRunListStructureString());
CheckBoundsForCursorPositions();
@@ -6053,7 +6218,7 @@ TEST_F(RenderTextTest, HarfBuzz_OrphanedVariationSelector) {
TEST_F(RenderTextTest, HarfBuzz_AsciiVariationSelector) {
RenderTextHarfBuzz* render_text = GetRenderText();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Don't use a system font on macOS - asking for a variation selector on
// ASCII glyphs can tickle OS bugs. See http://crbug.com/785522.
render_text->SetFontList(FontList("Arial, 12px"));
@@ -6061,7 +6226,7 @@ TEST_F(RenderTextTest, HarfBuzz_AsciiVariationSelector) {
// A variation selector doesn't have to appear with Emoji. It will probably
// cause the typesetter to render tofu in this case, but it should not break
// a text run.
- render_text->SetText(WideToUTF16(L"z\uFE0Fy"));
+ render_text->SetText(UTF8ToUTF16("z\uFE0Fy"));
EXPECT_EQ(ToString16Vec({"z\uFE0Fy"}), GetRunListStrings());
EXPECT_EQ("[0->2]", GetRunListStructureString());
CheckBoundsForCursorPositions();
@@ -6072,7 +6237,7 @@ TEST_F(RenderTextTest, HarfBuzz_LeadingVariationSelector) {
// When a variation selector appears either side of an emoji, ensure the one
// after is in the same run.
- render_text->SetText(WideToUTF16(L"\uFE0F\u260E\uFE0Fy"));
+ render_text->SetText(UTF8ToUTF16("\uFE0F\u260E\uFE0Fy"));
EXPECT_EQ(ToString16Vec({"\uFE0F", "☎\uFE0F", "y"}), GetRunListStrings());
EXPECT_EQ("[0][1->2][3]", GetRunListStructureString());
CheckBoundsForCursorPositions();
@@ -6085,7 +6250,7 @@ TEST_F(RenderTextTest, HarfBuzz_TrailingVariationSelector) {
// merged into the emoji run. Usually there should be no effect. That's
// ultimately up to the typeface but, however it choses, cursor and glyph
// positions should behave.
- render_text->SetText(WideToUTF16(L"z\u260E\uFE0F\uFE0Fy"));
+ render_text->SetText(UTF8ToUTF16("z\u260E\uFE0F\uFE0Fy"));
EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F\uFE0F", "y"}), GetRunListStrings());
EXPECT_EQ("[0][1->3][4]", GetRunListStructureString());
CheckBoundsForCursorPositions();
@@ -6096,7 +6261,7 @@ TEST_F(RenderTextTest, HarfBuzz_MultipleVariationSelectorEmoji) {
// Two emoji with variation selectors appearing in a correct sequence should
// be in the same run.
- render_text->SetText(WideToUTF16(L"z\u260E\uFE0F\u260E\uFE0Fy"));
+ render_text->SetText(UTF8ToUTF16("z\u260E\uFE0F\u260E\uFE0Fy"));
EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F☎\uFE0F", "y"}), GetRunListStrings());
EXPECT_EQ("[0][1->4][5]", GetRunListStructureString());
CheckBoundsForCursorPositions();
@@ -6144,7 +6309,7 @@ TEST_F(RenderTextTest, EmojiFlagGlyphCount) {
const internal::TextRunList* run_list = GetHarfBuzzRunList();
ASSERT_EQ(1U, run_list->runs().size());
-#if defined(OS_LINUX) || defined(OS_MACOSX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_APPLE)
// On Linux and macOS, the flags should be found, so two glyphs result.
EXPECT_EQ(2u, run_list->runs()[0]->shape.glyph_count);
#elif defined(OS_ANDROID)
@@ -6207,7 +6372,7 @@ TEST_F(RenderTextTest, GlyphBounds) {
// Ensure that shaping with a non-existent font does not cause a crash.
TEST_F(RenderTextTest, HarfBuzz_NonExistentFont) {
RenderTextHarfBuzz* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("test"));
+ render_text->SetText(ASCIIToUTF16("test"));
const internal::TextRunList* run_list = GetHarfBuzzRunList();
ASSERT_EQ(1U, run_list->size());
internal::TextRunHarfBuzz* run = run_list->runs()[0].get();
@@ -6219,17 +6384,17 @@ TEST_F(RenderTextTest, HarfBuzz_NonExistentFont) {
TEST_F(RenderTextTest, HarfBuzz_EmptyRun) {
internal::TextRunHarfBuzz run((Font()));
RenderTextHarfBuzz* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("abcdefgh"));
+ render_text->SetText(ASCIIToUTF16("abcdefgh"));
run.range = Range(3, 8);
run.shape.glyph_count = 0;
- EXPECT_EQ(Range(0, 0), run.CharRangeToGlyphRange(Range(4, 5)));
- EXPECT_EQ(Range(0, 0), run.GetGraphemeBounds(render_text, 4).Round());
+ EXPECT_EQ(Range(), run.CharRangeToGlyphRange(Range(4, 5)));
+ EXPECT_EQ(RangeF(), run.GetGraphemeBounds(render_text, 4));
Range chars;
Range glyphs;
run.GetClusterAt(4, &chars, &glyphs);
EXPECT_EQ(Range(3, 8), chars);
- EXPECT_EQ(Range(0, 0), glyphs);
+ EXPECT_EQ(Range(), glyphs);
}
// Ensure the line breaker doesn't compute the word's width bigger than the
@@ -6252,7 +6417,7 @@ TEST_F(RenderTextTest, HarfBuzz_WordWidthWithDiacritics) {
// Ensure a string fits in a display rect with a width equal to the string's.
TEST_F(RenderTextTest, StringFitsOwnWidth) {
RenderText* render_text = GetRenderText();
- const base::string16 kString = UTF8ToUTF16("www.example.com");
+ const base::string16 kString = ASCIIToUTF16("www.example.com");
render_text->SetText(kString);
render_text->ApplyWeight(Font::Weight::BOLD, Range(0, 3));
@@ -6294,7 +6459,7 @@ TEST_F(RenderTextTest, HarfBuzz_FontListFallback) {
// this test assumes the font "Arial" doesn't provide a unicode glyph for a
// particular character, and that there is a system fallback font which does.
// TODO(msw): Fallback doesn't find a glyph on Linux and Android.
-#if !defined(OS_LINUX) && !defined(OS_ANDROID)
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
TEST_F(RenderTextTest, HarfBuzz_UnicodeFallback) {
RenderTextHarfBuzz* render_text = GetRenderText();
render_text->SetFontList(FontList("Arial, 12px"));
@@ -6305,7 +6470,7 @@ TEST_F(RenderTextTest, HarfBuzz_UnicodeFallback) {
ASSERT_EQ(1U, run_list->size());
EXPECT_EQ(0U, run_list->runs()[0]->CountMissingGlyphs());
}
-#endif // !defined(OS_LINUX) && !defined(OS_ANDROID)
+#endif // !defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
// Ensure that the fallback fonts offered by GetFallbackFont() support glyphs
// for different languages.
@@ -6543,7 +6708,7 @@ const FallbackFontCase kCommonScriptCases[] = {
{"common29", L"\u2517\u2297\u2762\u2460\u25bd\u24a9\u21a7\ufe64"},
{"common30", L"\u2105\u2722\u275d\u249c\u21a2\u2590\u2260\uff5d"},
{"common31", L"\u33ba\u21c6\u2706\u02cb\ufe64\u02e6\u0374\u2493"},
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
{"common00", L"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
{"common01", L"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
{"common02", L"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
@@ -6618,12 +6783,12 @@ TEST_F(RenderTextTest, CJKFontWithLocale) {
TEST_F(RenderTextTest, SameFontAccrossIgnorableCodepoints) {
RenderText* render_text = GetRenderText();
- render_text->SetText(WideToUTF16(L"\u060F"));
+ render_text->SetText(UTF8ToUTF16("\u060F"));
const std::vector<FontSpan> spans1 = GetFontSpans();
ASSERT_EQ(1u, spans1.size());
Font font1 = spans1[0].first;
- render_text->SetText(WideToUTF16(L"\u060F\u200C\u060F"));
+ render_text->SetText(UTF8ToUTF16("\u060F\u200C\u060F"));
const std::vector<FontSpan> spans2 = GetFontSpans();
ASSERT_EQ(1u, spans2.size());
Font font2 = spans2[0].first;
@@ -6663,8 +6828,8 @@ TEST_F(RenderTextTest, DISABLED_TextDoesntClip) {
// "TEST_______",
"TEST some stuff", "WWWWWWWWWW", "gAXAXAXAXAXAXA",
"g\u00C5X\u00C5X\u00C5X\u00C5X\u00C5X\u00C5X\u00C5",
- "\u0647\u0654\u0647\u0654\u0647\u0654\u0647\u0654\u0645\u0631\u062D"
- "\u0628\u0627"};
+ ("\u0647\u0654\u0647\u0654\u0647\u0654\u0647\u0654\u0645\u0631\u062D"
+ "\u0628\u0627")};
const Size kCanvasSize(300, 50);
const int kTestSize = 10;
@@ -6790,7 +6955,7 @@ TEST_F(RenderTextTest, DISABLED_TextDoesClip) {
// Ensure color changes are picked up by the RenderText implementation.
TEST_F(RenderTextTest, ColorChange) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("x"));
+ render_text->SetText(ASCIIToUTF16("x"));
Draw();
ExpectTextLog({{1, SK_ColorBLACK}});
@@ -6808,7 +6973,7 @@ TEST_F(RenderTextTest, StylePropagated) {
// needed). They also vary depending on the OS version, so set a known font.
FontList font_list(Font("Arial", 10));
- render_text->SetText(UTF8ToUTF16("x"));
+ render_text->SetText(ASCIIToUTF16("x"));
render_text->SetFontList(font_list);
DrawVisualText();
@@ -6838,10 +7003,11 @@ TEST_F(RenderTextTest, SubpixelRenderingSuppressed) {
"your system fonts settings.";
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("x"));
+ render_text->SetText(ASCIIToUTF16("x"));
DrawVisualText();
-#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \
+ defined(OS_FUCHSIA)
// On Linux, whether subpixel AA is supported is determined by the platform
// FontConfig. Force it into a particular style after computing runs. Other
// platforms use a known default FontRenderParams from a static local.
@@ -6855,7 +7021,8 @@ TEST_F(RenderTextTest, SubpixelRenderingSuppressed) {
render_text->set_subpixel_rendering_suppressed(true);
DrawVisualText();
-#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \
+ defined(OS_FUCHSIA)
// For Linux, runs shouldn't be re-calculated, and the suppression of the
// SUBPIXEL_RENDERING_RGB set above should now take effect. But, after
// checking, apply the override anyway to be explicit that it is suppressed.
@@ -6911,7 +7078,13 @@ TEST_F(RenderTextTest, SkFontEdging) {
// Verify GetWordLookupDataAtPoint returns the correct baseline point and
// decorated word for an LTR string.
TEST_F(RenderTextTest, GetWordLookupDataAtPoint_LTR) {
- const base::string16 ltr = UTF8ToUTF16(" ab c ");
+ // Set an integer glyph width; GetCursorBounds() and
+ // GetWordLookupDataAtPoint() use different rounding internally.
+ //
+ // TODO(crbug.com/1111044): this shouldn't be necessary once RenderText keeps
+ // float precision through GetCursorBounds().
+ SetGlyphWidth(5);
+ const base::string16 ltr = ASCIIToUTF16(" ab c ");
const int kWordOneStartIndex = 2;
const int kWordTwoStartIndex = 6;
@@ -6928,7 +7101,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_LTR) {
// Create expected decorated text instances.
DecoratedText expected_word_1;
- expected_word_1.text = UTF8ToUTF16("ab");
+ expected_word_1.text = ASCIIToUTF16("ab");
// Attributes for the characters 'a' and 'b' at logical indices 2 and 3
// respectively.
expected_word_1.attributes.push_back(CreateRangedAttribute(
@@ -6941,7 +7114,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_LTR) {
SelectionModel(kWordOneStartIndex, CURSOR_FORWARD), false);
DecoratedText expected_word_2;
- expected_word_2.text = UTF8ToUTF16("c");
+ expected_word_2.text = ASCIIToUTF16("c");
// Attributes for character 'c' at logical index |kWordTwoStartIndex|.
expected_word_2.attributes.push_back(
CreateRangedAttribute(font_spans, 0, kWordTwoStartIndex,
@@ -6990,6 +7163,12 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_LTR) {
// Verify GetWordLookupDataAtPoint returns the correct baseline point and
// decorated word for an RTL string.
TEST_F(RenderTextTest, GetWordLookupDataAtPoint_RTL) {
+ // Set an integer glyph width; GetCursorBounds() and
+ // GetWordLookupDataAtPoint() use different rounding internally.
+ //
+ // TODO(crbug.com/1111044): this shouldn't be necessary once RenderText keeps
+ // float precision through GetCursorBounds().
+ SetGlyphWidth(5);
const base::string16 rtl = UTF8ToUTF16(" \u0634\u0632 \u0634");
const int kWordOneStartIndex = 1;
const int kWordTwoStartIndex = 5;
@@ -7067,7 +7246,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_RTL) {
// Test that GetWordLookupDataAtPoint behaves correctly for multiline text.
TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Multiline) {
- const base::string16 text = UTF8ToUTF16("a b\n..\ncd.");
+ const base::string16 text = ASCIIToUTF16("a b\n..\ncd.");
const size_t kWordOneIndex = 0; // Index of character 'a'.
const size_t kWordTwoIndex = 2; // Index of character 'b'.
const size_t kWordThreeIndex = 7; // Index of character 'c'.
@@ -7086,14 +7265,14 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Multiline) {
const std::vector<FontSpan> font_spans = GetFontSpans();
DecoratedText expected_word_1;
- expected_word_1.text = UTF8ToUTF16("a");
+ expected_word_1.text = ASCIIToUTF16("a");
expected_word_1.attributes.push_back(CreateRangedAttribute(
font_spans, 0, kWordOneIndex, Font::Weight::SEMIBOLD, 0));
const Rect left_glyph_word_1 =
GetSubstringBoundsUnion(Range(kWordOneIndex, kWordOneIndex + 1));
DecoratedText expected_word_2;
- expected_word_2.text = UTF8ToUTF16("b");
+ expected_word_2.text = ASCIIToUTF16("b");
expected_word_2.attributes.push_back(CreateRangedAttribute(
font_spans, 0, kWordTwoIndex, Font::Weight::SEMIBOLD,
UNDERLINE_MASK | STRIKE_MASK));
@@ -7101,7 +7280,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Multiline) {
GetSubstringBoundsUnion(Range(kWordTwoIndex, kWordTwoIndex + 1));
DecoratedText expected_word_3;
- expected_word_3.text = UTF8ToUTF16("cd");
+ expected_word_3.text = ASCIIToUTF16("cd");
expected_word_3.attributes.push_back(
CreateRangedAttribute(font_spans, 0, kWordThreeIndex,
Font::Weight::NORMAL, STRIKE_MASK | ITALIC_MASK));
@@ -7146,7 +7325,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Multiline) {
// Verify the boolean return value of GetWordLookupDataAtPoint.
TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Return) {
RenderText* render_text = GetRenderText();
- render_text->SetText(UTF8ToUTF16("..."));
+ render_text->SetText(ASCIIToUTF16("..."));
DecoratedText decorated_word;
Point baseline_point;
@@ -7158,7 +7337,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Return) {
EXPECT_FALSE(render_text->GetWordLookupDataAtPoint(query, &decorated_word,
&baseline_point));
- render_text->SetText(UTF8ToUTF16("abc"));
+ render_text->SetText(ASCIIToUTF16("abc"));
query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false)
.origin();
EXPECT_TRUE(render_text->GetWordLookupDataAtPoint(query, &decorated_word,
@@ -7174,7 +7353,7 @@ TEST_F(RenderTextTest, GetWordLookupDataAtPoint_Return) {
// Test that GetLookupDataAtPoint behaves correctly when the range spans lines.
TEST_F(RenderTextTest, GetLookupDataAtRange_Multiline) {
- const base::string16 text = UTF8ToUTF16("a\nb");
+ const base::string16 text = ASCIIToUTF16("a\nb");
constexpr Range kWordOneRange = Range(0, 1); // Range of character 'a'.
constexpr Range kWordTwoRange = Range(2, 3); // Range of character 'b'.
constexpr Range kTextRange = Range(0, 3); // Range of the entire text.
@@ -7192,20 +7371,20 @@ TEST_F(RenderTextTest, GetLookupDataAtRange_Multiline) {
const std::vector<FontSpan> font_spans = GetFontSpans();
DecoratedText expected_word_1;
- expected_word_1.text = UTF8ToUTF16("a");
+ expected_word_1.text = ASCIIToUTF16("a");
expected_word_1.attributes.push_back(CreateRangedAttribute(
font_spans, 0, kWordOneRange.start(), Font::Weight::SEMIBOLD, 0));
const Rect left_glyph_word_1 = GetSubstringBoundsUnion(kWordOneRange);
DecoratedText expected_word_2;
- expected_word_2.text = UTF8ToUTF16("b");
+ expected_word_2.text = ASCIIToUTF16("b");
expected_word_2.attributes.push_back(
CreateRangedAttribute(font_spans, 0, kWordTwoRange.start(),
Font::Weight::NORMAL, UNDERLINE_MASK));
const Rect left_glyph_word_2 = GetSubstringBoundsUnion(kWordTwoRange);
DecoratedText expected_entire_text;
- expected_entire_text.text = UTF8ToUTF16("a\nb");
+ expected_entire_text.text = ASCIIToUTF16("a\nb");
expected_entire_text.attributes.push_back(
CreateRangedAttribute(font_spans, kWordOneRange.start(),
kWordOneRange.start(), Font::Weight::SEMIBOLD, 0));
@@ -7296,12 +7475,39 @@ TEST_F(RenderTextTest, LineEndSelections) {
}
}
+// Tests that GetSubstringBounds rounds outward when glyphs have floating-point
+// widths.
+TEST_F(RenderTextTest, GetSubstringBoundsFloatingPoint) {
+ const float kGlyphWidth = 5.8;
+ SetGlyphWidth(kGlyphWidth);
+ RenderText* render_text = GetRenderText();
+ render_text->SetDisplayRect(Rect(200, 1000));
+ render_text->SetText(UTF8ToUTF16("abcdef"));
+ gfx::Rect bounds = GetSubstringBoundsUnion(Range(1, 2));
+ // The bounds should be rounded outwards so that the full substring is always
+ // contained in them.
+ EXPECT_EQ(base::ClampFloor(kGlyphWidth), bounds.x());
+ EXPECT_EQ(base::ClampCeil(2 * kGlyphWidth), bounds.right());
+}
+
+// Tests that GetSubstringBounds handles integer glypth widths correctly.
+TEST_F(RenderTextTest, GetSubstringBoundsInt) {
+ const float kGlyphWidth = 5;
+ SetGlyphWidth(kGlyphWidth);
+ RenderText* render_text = GetRenderText();
+ render_text->SetDisplayRect(Rect(200, 1000));
+ render_text->SetText(UTF8ToUTF16("abcdef"));
+ gfx::Rect bounds = GetSubstringBoundsUnion(Range(1, 2));
+ EXPECT_EQ(kGlyphWidth, bounds.x());
+ EXPECT_EQ(2 * kGlyphWidth, bounds.right());
+}
+
// Tests that GetSubstringBounds returns the correct bounds for multiline text.
TEST_F(RenderTextTest, GetSubstringBoundsMultiline) {
RenderText* render_text = GetRenderText();
render_text->SetMultiline(true);
render_text->SetDisplayRect(Rect(200, 1000));
- render_text->SetText(UTF8ToUTF16("abc\n\ndef"));
+ render_text->SetText(ASCIIToUTF16("abc\n\ndef"));
const std::vector<Range> line_char_range = {Range(0, 4), Range(4, 5),
Range(5, 8)};
@@ -7334,7 +7540,7 @@ TEST_F(RenderTextTest, InvalidFont) {
const int kFontSize = 13;
RenderText* render_text = GetRenderText();
render_text->SetFontList(FontList(Font(font_name, kFontSize)));
- render_text->SetText(UTF8ToUTF16("abc"));
+ render_text->SetText(ASCIIToUTF16("abc"));
DrawVisualText();
}
@@ -7377,7 +7583,7 @@ TEST_F(RenderTextTest, BaselineWithLineHeight) {
RenderText* render_text = GetRenderText();
const int font_height = render_text->font_list().GetHeight();
render_text->SetDisplayRect(Rect(500, font_height));
- render_text->SetText(UTF8ToUTF16("abc"));
+ render_text->SetText(ASCIIToUTF16("abc"));
// Select everything so the test can use GetSelectionBoundsUnion().
render_text->SelectAll(false);
@@ -7509,7 +7715,7 @@ TEST_F(RenderTextTest, MissingFlagEmoji) {
// but cursor navigation should still behave as though they are joined. To get
// placeholder glyphs, make up a non-existent country. The codes used are
// based on ISO 3166-1 alpha-2. Codes starting with X are user-assigned.
- base::string16 text(UTF8ToUTF16("🇽🇽🇽🇽"));
+ base::string16 text(WideToUTF16(L"🇽🇽🇽🇽"));
// Each flag is 4 UTF16 characters (2 surrogate pair code points).
EXPECT_EQ(8u, text.length());
@@ -7564,7 +7770,7 @@ TEST_F(RenderTextTest, MissingFlagEmoji) {
// Ensures that glyph spacing is correctly applied to obscured text.
TEST_F(RenderTextTest, ObscuredGlyphSpacing) {
- const base::string16 seuss = UTF8ToUTF16("hop on pop");
+ const base::string16 seuss = ASCIIToUTF16("hop on pop");
RenderTextHarfBuzz* render_text = GetRenderText();
render_text->SetText(seuss);
render_text->SetObscured(true);
@@ -7585,7 +7791,7 @@ TEST_F(RenderTextTest, ObscuredGlyphSpacing) {
// Ensures that glyph spacing is ignored for non-obscured text.
TEST_F(RenderTextTest, ObscuredGlyphSpacingOnNonObscuredText) {
- const base::string16 seuss = UTF8ToUTF16("hop on pop");
+ const base::string16 seuss = ASCIIToUTF16("hop on pop");
RenderTextHarfBuzz* render_text = GetRenderText();
render_text->SetText(seuss);
render_text->SetObscured(false);
@@ -7602,7 +7808,7 @@ TEST_F(RenderTextTest, FontSizeOverride) {
RenderTextHarfBuzz* render_text = GetRenderText();
const int default_font_size = render_text->font_list().GetFontSize();
const int test_font_size_override = default_font_size + 5;
- render_text->SetText(UTF8ToUTF16("0123456789"));
+ render_text->SetText(ASCIIToUTF16("0123456789"));
render_text->ApplyFontSizeOverride(test_font_size_override, gfx::Range(3, 7));
EXPECT_EQ(ToString16Vec({"012", "3456", "789"}), GetRunListStrings());
@@ -7668,7 +7874,7 @@ TEST_F(RenderTextTest, DrawSelectAll) {
ExpectTextLog(kUnselected);
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
TEST_F(RenderTextTest, StringSizeUpdatedWhenDeviceScaleFactorChanges) {
RenderText* render_text = GetRenderText();
render_text->SetText(ASCIIToUTF16("Test - 1"));
diff --git a/chromium/ui/gfx/shadow_value.cc b/chromium/ui/gfx/shadow_value.cc
index cfa2883d710..87e2ecb1b54 100644
--- a/chromium/ui/gfx/shadow_value.cc
+++ b/chromium/ui/gfx/shadow_value.cc
@@ -8,10 +8,10 @@
#include <algorithm>
+#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
namespace gfx {
@@ -30,7 +30,7 @@ Insets GetInsets(const ShadowValues& shadows, bool include_inner_blur) {
double blur = shadow.blur();
if (!include_inner_blur)
blur /= 2;
- int blur_length = ToRoundedInt(blur);
+ int blur_length = base::ClampRound(blur);
left = std::max(left, blur_length - shadow.x());
top = std::max(top, blur_length - shadow.y());
@@ -44,8 +44,7 @@ Insets GetInsets(const ShadowValues& shadows, bool include_inner_blur) {
} // namespace
ShadowValue ShadowValue::Scale(float scale) const {
- gfx::Vector2d scaled_offset =
- gfx::ToFlooredVector2d(gfx::ScaleVector2d(offset_, scale));
+ Vector2d scaled_offset = ToFlooredVector2d(ScaleVector2d(offset_, scale));
return ShadowValue(scaled_offset, blur_ * scale, color_);
}
@@ -62,7 +61,7 @@ std::string ShadowValue::ToString() const {
// static
Insets ShadowValue::GetMargin(const ShadowValues& shadows) {
- gfx::Insets margins = GetInsets(shadows, false);
+ Insets margins = GetInsets(shadows, false);
return -margins;
}
@@ -85,15 +84,15 @@ ShadowValues ShadowValue::MakeRefreshShadowValues(int elevation,
switch (elevation) {
case 3: {
- ShadowValue key = {gfx::Vector2d(0, 1), 12, SkColorSetA(color, 0x66)};
- ShadowValue ambient = {gfx::Vector2d(0, 4), 64, SkColorSetA(color, 0x40)};
+ ShadowValue key = {Vector2d(0, 1), 12, SkColorSetA(color, 0x66)};
+ ShadowValue ambient = {Vector2d(0, 4), 64, SkColorSetA(color, 0x40)};
return {key, ambient};
}
case 16: {
- gfx::ShadowValue key = {gfx::Vector2d(0, 0), kBlurCorrection * 16,
- SkColorSetA(color, 0x1a)};
- gfx::ShadowValue ambient = {gfx::Vector2d(0, 12), kBlurCorrection * 16,
- SkColorSetA(color, 0x3d)};
+ ShadowValue key = {Vector2d(0, 0), kBlurCorrection * 16,
+ SkColorSetA(color, 0x1a)};
+ ShadowValue ambient = {Vector2d(0, 12), kBlurCorrection * 16,
+ SkColorSetA(color, 0x3d)};
return {key, ambient};
}
default:
@@ -111,11 +110,11 @@ ShadowValues ShadowValue::MakeMdShadowValues(int elevation, SkColor color) {
// to double the designer-provided blur values.
const int kBlurCorrection = 2;
// "Key shadow": y offset is elevation and blur is twice the elevation.
- shadow_values.emplace_back(gfx::Vector2d(0, elevation),
+ shadow_values.emplace_back(Vector2d(0, elevation),
kBlurCorrection * elevation * 2,
SkColorSetA(color, 0x3d));
// "Ambient shadow": no offset and blur matches the elevation.
- shadow_values.emplace_back(gfx::Vector2d(), kBlurCorrection * elevation,
+ shadow_values.emplace_back(Vector2d(), kBlurCorrection * elevation,
SkColorSetA(color, 0x1f));
// To see what this looks like for elevation 24, try this CSS:
// box-shadow: 0 24px 48px rgba(0, 0, 0, .24),
diff --git a/chromium/ui/gfx/skbitmap_operations.cc b/chromium/ui/gfx/skbitmap_operations.cc
index c08079f585b..13abb4c0f8f 100644
--- a/chromium/ui/gfx/skbitmap_operations.cc
+++ b/chromium/ui/gfx/skbitmap_operations.cc
@@ -628,11 +628,11 @@ SkBitmap SkBitmapOperations::DownsampleByTwo(const SkBitmap& bitmap) {
SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) {
if (bitmap.isNull())
return bitmap;
- if (bitmap.isOpaque())
+ if (bitmap.alphaType() != kPremul_SkAlphaType)
return bitmap;
const SkImageInfo& opaque_info =
- bitmap.info().makeAlphaType(kOpaque_SkAlphaType);
+ bitmap.info().makeAlphaType(kUnpremul_SkAlphaType);
SkBitmap opaque_bitmap;
opaque_bitmap.allocPixels(opaque_info);
diff --git a/chromium/ui/gfx/skbitmap_operations_unittest.cc b/chromium/ui/gfx/skbitmap_operations_unittest.cc
index 65f684388cc..632550974b2 100644
--- a/chromium/ui/gfx/skbitmap_operations_unittest.cc
+++ b/chromium/ui/gfx/skbitmap_operations_unittest.cc
@@ -437,6 +437,7 @@ TEST(SkBitmapOperationsTest, DownsampleByTwoUntilSize) {
TEST(SkBitmapOperationsTest, UnPreMultiply) {
SkBitmap input;
input.allocN32Pixels(2, 2);
+ EXPECT_EQ(input.alphaType(), kPremul_SkAlphaType);
// Set PMColors into the bitmap
*input.getAddr32(0, 0) = SkPackARGB32NoCheck(0x80, 0x00, 0x00, 0x00);
@@ -445,8 +446,10 @@ TEST(SkBitmapOperationsTest, UnPreMultiply) {
*input.getAddr32(1, 1) = SkPackARGB32NoCheck(0x00, 0x00, 0xCC, 0x88);
SkBitmap result = SkBitmapOperations::UnPreMultiply(input);
+ EXPECT_EQ(result.alphaType(), kUnpremul_SkAlphaType);
EXPECT_EQ(2, result.width());
EXPECT_EQ(2, result.height());
+ EXPECT_NE(result.getPixels(), input.getPixels());
EXPECT_EQ(0x80000000, *result.getAddr32(0, 0));
EXPECT_EQ(0x80FFFFFF, *result.getAddr32(1, 0));
@@ -454,6 +457,27 @@ TEST(SkBitmapOperationsTest, UnPreMultiply) {
EXPECT_EQ(0x00000000u, *result.getAddr32(1, 1)); // "Division by zero".
}
+TEST(SkBitmapOperationsTest, UnPreMultiplyOpaque) {
+ SkBitmap input;
+ input.allocN32Pixels(2, 2, true);
+ EXPECT_EQ(input.alphaType(), kOpaque_SkAlphaType);
+
+ SkBitmap result = SkBitmapOperations::UnPreMultiply(input);
+ EXPECT_EQ(result.alphaType(), kOpaque_SkAlphaType);
+ EXPECT_EQ(result.getPixels(), input.getPixels());
+}
+
+TEST(SkBitmapOperationsTest, UnPreMultiplyAlreadyUnPreMultiplied) {
+ SkBitmap input;
+ input.allocN32Pixels(2, 2);
+ input.setAlphaType(kUnpremul_SkAlphaType);
+ EXPECT_EQ(input.alphaType(), kUnpremul_SkAlphaType);
+
+ SkBitmap result = SkBitmapOperations::UnPreMultiply(input);
+ EXPECT_EQ(result.alphaType(), kUnpremul_SkAlphaType);
+ EXPECT_EQ(result.getPixels(), input.getPixels());
+}
+
TEST(SkBitmapOperationsTest, CreateTransposedBitmap) {
SkBitmap input;
input.allocN32Pixels(2, 3);
diff --git a/chromium/ui/gfx/skia_vector_animation.cc b/chromium/ui/gfx/skia_vector_animation.cc
index b2e0f7f9bdf..63966d00d65 100644
--- a/chromium/ui/gfx/skia_vector_animation.cc
+++ b/chromium/ui/gfx/skia_vector_animation.cc
@@ -26,7 +26,7 @@ SkiaVectorAnimation::TimerControl::TimerControl(
: start_offset_(offset),
end_offset_((offset + cycle_duration)),
cycle_duration_(end_offset_ - start_offset_),
- progress_per_millisecond_(1.0 / total_duration.InMillisecondsF()),
+ total_duration_(total_duration),
previous_tick_(start_timestamp),
progress_(base::TimeDelta::FromMilliseconds(0)),
current_cycle_progress_(start_offset_),
@@ -58,15 +58,15 @@ void SkiaVectorAnimation::TimerControl::Resume(
double SkiaVectorAnimation::TimerControl::GetNormalizedCurrentCycleProgress()
const {
- return current_cycle_progress_.InMillisecondsF() * progress_per_millisecond_;
+ return current_cycle_progress_ / total_duration_;
}
double SkiaVectorAnimation::TimerControl::GetNormalizedStartOffset() const {
- return start_offset_.InMillisecondsF() * progress_per_millisecond_;
+ return start_offset_ / total_duration_;
}
double SkiaVectorAnimation::TimerControl::GetNormalizedEndOffset() const {
- return end_offset_.InMillisecondsF() * progress_per_millisecond_;
+ return end_offset_ / total_duration_;
}
SkiaVectorAnimation::SkiaVectorAnimation(
@@ -82,8 +82,7 @@ void SkiaVectorAnimation::SetAnimationObserver(
}
base::TimeDelta SkiaVectorAnimation::GetAnimationDuration() const {
- return base::TimeDelta::FromMilliseconds(
- std::floor(SkScalarToFloat(skottie_->duration()) * 1000.f));
+ return base::TimeDelta::FromSecondsD(skottie_->duration());
}
gfx::Size SkiaVectorAnimation::GetOriginalSize() const {
@@ -148,13 +147,11 @@ float SkiaVectorAnimation::GetCurrentProgress() const {
DCHECK(timer_control_);
return timer_control_->GetNormalizedEndOffset();
case PlayState::kPaused:
- if (timer_control_) {
- return timer_control_->GetNormalizedCurrentCycleProgress();
- } else {
- // It may be that the timer hasn't been initialized which may happen if
- // the animation was paused while it was in |kScheculePlay| state.
- return scheduled_start_offset_.InMillisecondsF() / skottie_->duration();
- }
+ // It may be that the timer hasn't been initialized, which may happen if
+ // the animation was paused while it was in the kSchedulePlay state.
+ return timer_control_
+ ? timer_control_->GetNormalizedCurrentCycleProgress()
+ : (scheduled_start_offset_ / GetAnimationDuration());
case PlayState::kSchedulePlay:
case PlayState::kPlaying:
case PlayState::kScheduleResume:
diff --git a/chromium/ui/gfx/skia_vector_animation.h b/chromium/ui/gfx/skia_vector_animation.h
index 9a0f6cc1239..8c5d252620b 100644
--- a/chromium/ui/gfx/skia_vector_animation.h
+++ b/chromium/ui/gfx/skia_vector_animation.h
@@ -185,16 +185,15 @@ class GFX_EXPORT SkiaVectorAnimation {
// Time duration from 0 which marks the beginning of a cycle.
const base::TimeDelta start_offset_;
- // Time duration from 0 which marks the end of a cycle.
+ // Time duration from 0 which marks the end of a cycle.
const base::TimeDelta end_offset_;
// Time duration for one cycle. This is essentially a cache of the
// difference between |end_offset_| - |start_offset_|.
const base::TimeDelta cycle_duration_;
- // Normalized animation progress delta per millisecond, that is, the
- // normalized progress in per millisecond of time duration.
- const double progress_per_millisecond_;
+ // Total duration of all cycles.
+ const base::TimeDelta total_duration_;
// The timetick at which |progress_| was updated last.
base::TimeTicks previous_tick_;
diff --git a/chromium/ui/gfx/skia_vector_animation_unittest.cc b/chromium/ui/gfx/skia_vector_animation_unittest.cc
index 013c06987f9..c82092638c9 100644
--- a/chromium/ui/gfx/skia_vector_animation_unittest.cc
+++ b/chromium/ui/gfx/skia_vector_animation_unittest.cc
@@ -53,7 +53,7 @@ constexpr char kData[] =
"}";
constexpr float kAnimationWidth = 400.f;
constexpr float kAnimationHeight = 200.f;
-constexpr float kAnimationDuration = 5.f;
+constexpr auto kAnimationDuration = base::TimeDelta::FromSeconds(5);
class TestAnimationObserver : public SkiaVectorAnimationObserver {
public:
@@ -145,15 +145,13 @@ class SkiaVectorAnimationTest : public testing::Test {
const base::TickClock* test_clock() const { return &test_clock_; }
- void AdvanceClock(int64_t ms) {
- test_clock_.Advance(base::TimeDelta::FromMilliseconds(ms));
- }
+ void AdvanceClock(base::TimeDelta advance) { test_clock_.Advance(advance); }
base::TimeDelta TimeDeltaSince(const base::TimeTicks& ticks) const {
return test_clock_.NowTicks() - ticks;
}
- const base::TimeTicks NowTicks() const { return test_clock_.NowTicks(); }
+ base::TimeTicks NowTicks() const { return test_clock_.NowTicks(); }
double GetTimerStartOffset() const {
return animation_->timer_control_->GetNormalizedStartOffset();
@@ -167,8 +165,8 @@ class SkiaVectorAnimationTest : public testing::Test {
return animation_->timer_control_->previous_tick_;
}
- double GetTimerProgressPerMs() const {
- return animation_->timer_control_->progress_per_millisecond_;
+ base::TimeDelta GetTimerTotalDuration() const {
+ return animation_->timer_control_->total_duration_;
}
int GetTimerCycles() const {
@@ -205,8 +203,7 @@ TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
- EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
- kAnimationDuration);
+ EXPECT_EQ(animation_->GetAnimationDuration(), kAnimationDuration);
EXPECT_TRUE(IsStopped());
skottie_ = cc::SkottieWrapper::CreateNonSerializable(
@@ -214,8 +211,7 @@ TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
- EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
- kAnimationDuration);
+ EXPECT_EQ(animation_->GetAnimationDuration(), kAnimationDuration);
EXPECT_TRUE(IsStopped());
}
@@ -223,8 +219,7 @@ TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
EXPECT_TRUE(IsStopped());
animation_->Start(SkiaVectorAnimation::Style::kLinear);
@@ -240,18 +235,22 @@ TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(50);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(50);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kAdvance / kAnimationDuration);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), base::TimeDelta());
// Advance the clock to the end of the animation.
- AdvanceClock(4951);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4951);
+ constexpr auto kAdvanceToEnd =
+ kAnimationDuration - kAdvance + base::TimeDelta::FromMilliseconds(1);
+ AdvanceClock(kAdvanceToEnd);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvanceToEnd);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f);
EXPECT_TRUE(HasAnimationEnded());
@@ -262,8 +261,7 @@ TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) {
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
animation_->Start(SkiaVectorAnimation::Style::kLinear);
@@ -271,30 +269,29 @@ TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) {
EXPECT_TRUE(IsPlaying());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
- AdvanceClock(50);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(50);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kAdvance / kAnimationDuration);
animation_->Stop();
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.f);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.0f);
EXPECT_TRUE(IsStopped());
}
TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
EXPECT_FALSE(observer.animation_cycle_ended());
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kLinear);
EXPECT_TRUE(IsScheduledToPlay());
@@ -308,32 +305,36 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) {
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
EXPECT_FLOAT_EQ(GetTimerEndOffset(),
- (start_time_ms + duration_ms) / total_duration_ms);
+ (kStartTime + kDuration) / kAnimationDuration);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(100);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), base::TimeDelta());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
EXPECT_FALSE(observer.animation_cycle_ended());
// Advance clock another 300 ms.
- AdvanceClock(300);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+ constexpr auto kAdvance2 = base::TimeDelta::FromMilliseconds(300);
+ AdvanceClock(kAdvance2);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + 300.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance + kAdvance2) / kAnimationDuration);
EXPECT_FALSE(observer.animation_cycle_ended());
// Reach the end of animation.
- AdvanceClock(601);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 601);
+ constexpr auto kAdvanceToEnd =
+ kDuration - kAdvance - kAdvance2 + base::TimeDelta::FromMilliseconds(1);
+ AdvanceClock(kAdvanceToEnd);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvanceToEnd);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
EXPECT_TRUE(observer.animation_cycle_ended());
@@ -341,33 +342,35 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
+
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- AdvanceClock(200);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(200));
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kLinear);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- AdvanceClock(100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Pause();
EXPECT_TRUE(IsPaused());
// Advancing clock and stepping animation should have no effect when animation
// is paused.
- AdvanceClock(5000);
+ AdvanceClock(kAnimationDuration);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
// Resume playing the animation.
animation_->ResumePlaying();
@@ -376,13 +379,15 @@ TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) {
// There should be no progress, since we haven't advanced the clock yet.
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance * 2) / kAnimationDuration);
- AdvanceClock(801);
+ AdvanceClock(kDuration - kAdvance * 2 + base::TimeDelta::FromMilliseconds(1));
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
}
@@ -390,8 +395,7 @@ TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
EXPECT_TRUE(IsStopped());
animation_->Start(SkiaVectorAnimation::Style::kLoop);
@@ -406,20 +410,23 @@ TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
- EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
+ EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.0f);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(50);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(50);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kAdvance / kAnimationDuration);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), base::TimeDelta());
// Advance the clock to the end of the animation.
- AdvanceClock(4950);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950);
+ AdvanceClock(kAnimationDuration - kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()),
+ kAnimationDuration - kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_EQ(GetTimerCycles(), 1);
EXPECT_TRUE(std::abs(animation_->GetCurrentProgress() - 0.f) < 0.0001f);
@@ -428,19 +435,16 @@ TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
-
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
+
EXPECT_TRUE(IsStopped());
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kLoop);
EXPECT_TRUE(IsScheduledToPlay());
EXPECT_FALSE(observer.animation_will_start_playing());
@@ -452,35 +456,37 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) {
EXPECT_TRUE(observer.animation_will_start_playing());
EXPECT_FALSE(observer.animation_cycle_ended());
- EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(GetTimerStartOffset(), kStartTime / kAnimationDuration);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
EXPECT_FLOAT_EQ(GetTimerEndOffset(),
- (start_time_ms + duration_ms) / total_duration_ms);
+ (kStartTime + kDuration) / kAnimationDuration);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(100);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
EXPECT_FALSE(observer.animation_cycle_ended());
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
- // Advance clock another 300 ms.
- AdvanceClock(300);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+ constexpr auto kAdvance2 = base::TimeDelta::FromMilliseconds(300);
+ AdvanceClock(kAdvance2);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + 300.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance + kAdvance2) / kAnimationDuration);
EXPECT_FALSE(observer.animation_cycle_ended());
// Reach the end of animation.
- AdvanceClock(600);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600);
+ AdvanceClock(kDuration - kAdvance - kAdvance2);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()),
+ kDuration - kAdvance - kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(observer.animation_cycle_ended());
@@ -489,36 +495,38 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- AdvanceClock(200);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(200));
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kLoop);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 400.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kStartTime / kAnimationDuration);
- AdvanceClock(100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Pause();
EXPECT_TRUE(IsPaused());
// Advancing clock and stepping animation should have no effect when animation
// is paused.
- AdvanceClock(5000);
+ AdvanceClock(kAnimationDuration);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
// Resume playing the animation.
animation_->ResumePlaying();
@@ -527,14 +535,16 @@ TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
// There should be no progress, since we haven't advanced the clock yet.
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kAdvance * 2) / kAnimationDuration);
EXPECT_FALSE(observer.animation_cycle_ended());
- AdvanceClock(800);
+ AdvanceClock(kDuration - kAdvance * 2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
EXPECT_TRUE(IsPlaying());
@@ -542,11 +552,10 @@ TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
-
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
animation_->Start(SkiaVectorAnimation::Style::kThrobbing);
EXPECT_TRUE(IsScheduledToPlay());
@@ -560,32 +569,35 @@ TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
- EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
+ EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.0f);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(50);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(50);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kAdvance / kAnimationDuration);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), base::TimeDelta());
// Advance the clock to the end of the animation.
- AdvanceClock(4950);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950);
+ AdvanceClock(kAnimationDuration - kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()),
+ kAnimationDuration - kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.0f);
EXPECT_TRUE(IsPlaying());
EXPECT_FALSE(observer.animation_cycle_ended());
- AdvanceClock(2500);
+ AdvanceClock(kAnimationDuration / 2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.5f);
EXPECT_TRUE(IsPlaying());
EXPECT_FALSE(observer.animation_cycle_ended());
- AdvanceClock(2500);
+ AdvanceClock(kAnimationDuration / 2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
EXPECT_TRUE(IsPlaying());
@@ -593,19 +605,15 @@ TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
-
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kThrobbing);
EXPECT_TRUE(IsScheduledToPlay());
EXPECT_FALSE(observer.animation_will_start_playing());
@@ -616,48 +624,52 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) {
EXPECT_TRUE(IsPlaying());
EXPECT_TRUE(observer.animation_will_start_playing());
- EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(GetTimerStartOffset(), kStartTime / kAnimationDuration);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
EXPECT_FLOAT_EQ(GetTimerEndOffset(),
- (start_time_ms + duration_ms) / total_duration_ms);
+ (kStartTime + kDuration) / kAnimationDuration);
- EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+ EXPECT_EQ(GetTimerTotalDuration(), kAnimationDuration);
- AdvanceClock(100);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FALSE(observer.animation_cycle_ended());
EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
- // Advance clock another 300 ms.
- AdvanceClock(300);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+ constexpr auto kAdvance2 = base::TimeDelta::FromMilliseconds(300);
+ AdvanceClock(kAdvance2);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FALSE(observer.animation_cycle_ended());
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()), base::TimeDelta());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (100.f + 300.f + start_time_ms) / total_duration_ms);
+ (kStartTime + kAdvance + kAdvance2) / kAnimationDuration);
// Reach the end of animation.
- AdvanceClock(600);
- EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600);
+ AdvanceClock(kDuration - kAdvance - kAdvance2);
+ EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()),
+ kDuration - kAdvance - kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
EXPECT_FALSE(observer.animation_cycle_ended());
- AdvanceClock(500);
+ constexpr auto kAdvance3 = base::TimeDelta::FromMilliseconds(500);
+ AdvanceClock(kAdvance3);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 900.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ (kStartTime + kDuration - kAdvance3) / kAnimationDuration);
EXPECT_TRUE(IsPlaying());
EXPECT_FALSE(observer.animation_cycle_ended());
- AdvanceClock(500);
+ AdvanceClock(kDuration - kAdvance3);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
EXPECT_TRUE(IsPlaying());
@@ -665,46 +677,45 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) {
observer.Reset();
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 100.f) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
EXPECT_TRUE(IsPlaying());
}
TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) {
- const int start_time_ms = 400;
- const int duration_ms = 1000;
- const float total_duration_ms = kAnimationDuration * 1000.f;
+ constexpr auto kStartTime = base::TimeDelta::FromMilliseconds(400);
+ constexpr auto kDuration = base::TimeDelta::FromMilliseconds(1000);
- AdvanceClock(200);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(200));
- animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
- base::TimeDelta::FromMilliseconds(duration_ms),
+ animation_->StartSubsection(kStartTime, kDuration,
SkiaVectorAnimation::Style::kThrobbing);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- start_time_ms / total_duration_ms);
+ kStartTime / kAnimationDuration);
- AdvanceClock(100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 100.f) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Pause();
EXPECT_TRUE(IsPaused());
// Advancing clock and stepping animation should have no effect when animation
// is paused.
- AdvanceClock(5000);
+ AdvanceClock(kAnimationDuration);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 100.f) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
// Resume playing the animation.
animation_->ResumePlaying();
@@ -714,31 +725,31 @@ TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) {
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 100.f) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 200.f) / total_duration_ms);
+ (kStartTime + kAdvance * 2) / kAnimationDuration);
- AdvanceClock(800);
+ AdvanceClock(kDuration - kAdvance * 2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
EXPECT_TRUE(IsPlaying());
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 900.f) / total_duration_ms);
+ (kStartTime + kDuration - kAdvance) / kAnimationDuration);
EXPECT_TRUE(IsPlaying());
animation_->Pause();
EXPECT_TRUE(IsPaused());
- AdvanceClock(10000);
+ AdvanceClock(kAnimationDuration * 2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 900.f) / total_duration_ms);
+ (kStartTime + kDuration - kAdvance) / kAnimationDuration);
// Resume playing the animation.
animation_->ResumePlaying();
@@ -747,32 +758,31 @@ TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) {
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
- AdvanceClock(500);
+ constexpr auto kAdvance2 = base::TimeDelta::FromMilliseconds(500);
+ AdvanceClock(kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 400.f) / total_duration_ms);
+ EXPECT_FLOAT_EQ(
+ animation_->GetCurrentProgress(),
+ (kStartTime + kDuration - kAdvance - kAdvance2) / kAnimationDuration);
- AdvanceClock(400);
+ AdvanceClock(kDuration - kAdvance - kAdvance2);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
- AdvanceClock(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
- (start_time_ms + 100.f) / total_duration_ms);
+ (kStartTime + kAdvance) / kAnimationDuration);
EXPECT_TRUE(IsPlaying());
}
+// Test to see if the race condition is handled correctly. It may happen that we
+// pause the video before it even starts playing.
TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) {
- const float total_duration_ms = kAnimationDuration * 1000.f;
-
- // Test to see if the race condition is handled correctly. It may happen that
- // we pause the video before it even starts playing.
-
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
animation_->Start();
EXPECT_TRUE(IsScheduledToPlay());
@@ -780,55 +790,50 @@ TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) {
animation_->Pause();
EXPECT_TRUE(IsPaused());
- AdvanceClock(100);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(100));
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
animation_->ResumePlaying();
EXPECT_TRUE(IsScheduledToResume());
- AdvanceClock(100);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(100));
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
EXPECT_TRUE(IsPlaying());
- AdvanceClock(100);
+ constexpr auto kAdvance = base::TimeDelta::FromMilliseconds(100);
+ AdvanceClock(kAdvance);
animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
- EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 100.f / total_duration_ms);
+ EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+ kAdvance / kAnimationDuration);
}
TEST_F(SkiaVectorAnimationTest, PaintTest) {
- std::unique_ptr<gfx::Canvas> canvas(new gfx::Canvas(
- gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false));
+ gfx::Canvas canvas(gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false);
- // Advance clock by 300 milliseconds.
- AdvanceClock(300);
+ AdvanceClock(base::TimeDelta::FromMilliseconds(300));
animation_->Start(SkiaVectorAnimation::Style::kLinear);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
-
- AdvanceClock(50);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
- SkBitmap bitmap = canvas->GetBitmap();
- IsAllSameColor(SK_ColorGREEN, bitmap);
-
- AdvanceClock(2450);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
- bitmap = canvas->GetBitmap();
- IsAllSameColor(SK_ColorGREEN, bitmap);
-
- AdvanceClock(50);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
- bitmap = canvas->GetBitmap();
- IsAllSameColor(SK_ColorBLUE, bitmap);
-
- AdvanceClock(1000);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
- bitmap = canvas->GetBitmap();
- IsAllSameColor(SK_ColorBLUE, bitmap);
-
- AdvanceClock(1400);
- animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
- bitmap = canvas->GetBitmap();
- IsAllSameColor(SK_ColorBLUE, bitmap);
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(50));
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+ IsAllSameColor(SK_ColorGREEN, canvas.GetBitmap());
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(2450));
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+ IsAllSameColor(SK_ColorGREEN, canvas.GetBitmap());
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(50));
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+ IsAllSameColor(SK_ColorBLUE, canvas.GetBitmap());
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(1000));
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+ IsAllSameColor(SK_ColorBLUE, canvas.GetBitmap());
+
+ AdvanceClock(base::TimeDelta::FromMilliseconds(1400));
+ animation_->Paint(&canvas, NowTicks(), animation_->GetOriginalSize());
+ IsAllSameColor(SK_ColorBLUE, canvas.GetBitmap());
}
} // namespace gfx
diff --git a/chromium/ui/gfx/swap_result.h b/chromium/ui/gfx/swap_result.h
index 1a71e4ec174..6f74bf2c949 100644
--- a/chromium/ui/gfx/swap_result.h
+++ b/chromium/ui/gfx/swap_result.h
@@ -32,6 +32,17 @@ struct SwapTimings {
// dicontinuities in associated UMA data.
base::TimeTicks swap_end;
+ // When Display Compositor thread scheduled work to GPU Thread. For GLRenderer
+ // it's when InProcessCommandBuffer::Flush() happens, for SkiaRenderer it's
+ // PostTask time for FinishPaintRenderPass or SwapBuffers whichever comes
+ // first.
+ base::TimeTicks viz_scheduled_draw;
+
+ // When GPU thread started draw submitted by Display Compositor thread. For
+ // GLRenderer it's InProcessCommandBuffer::FlushOnGpuThread, for SkiaRenderer
+ // it's FinishPaintRenderPass/SwapBuffers.
+ base::TimeTicks gpu_started_draw;
+
bool is_null() const { return swap_start.is_null() && swap_end.is_null(); }
};
diff --git a/chromium/ui/gfx/text_elider_unittest.cc b/chromium/ui/gfx/text_elider_unittest.cc
index d94bb0ec5b4..d7fa4b86b09 100644
--- a/chromium/ui/gfx/text_elider_unittest.cc
+++ b/chromium/ui/gfx/text_elider_unittest.cc
@@ -970,7 +970,7 @@ TEST(TextEliderTest, ElideRectangleTextLongWords) {
// to wrap incorrectly.
TEST(TextEliderTest, ElideRectangleTextCheckLineWidth) {
FontList font_list;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_MAC)
// Use a specific font to expose the line width exceeding problem.
font_list = FontList(Font("LucidaGrande", 12));
#endif
diff --git a/chromium/ui/gfx/transform.cc b/chromium/ui/gfx/transform.cc
index 58b662123d6..0184c6a3e57 100644
--- a/chromium/ui/gfx/transform.cc
+++ b/chromium/ui/gfx/transform.cc
@@ -12,7 +12,6 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/quaternion.h"
#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/rrect_f.h"
#include "ui/gfx/skia_util.h"
@@ -539,8 +538,6 @@ bool Transform::Blend(const Transform& from, double progress) {
}
void Transform::RoundTranslationComponents() {
- // TODO(pkasting): Use SkScalarRound() when
- // https://bugs.chromium.org/p/skia/issues/detail?id=6852 is fixed.
matrix_.set(0, 3, std::round(matrix_.get(0, 3)));
matrix_.set(1, 3, std::round(matrix_.get(1, 3)));
}
diff --git a/chromium/ui/gfx/win/singleton_hwnd.cc b/chromium/ui/gfx/win/singleton_hwnd.cc
index a0244532c42..1476b11bfd3 100644
--- a/chromium/ui/gfx/win/singleton_hwnd.cc
+++ b/chromium/ui/gfx/win/singleton_hwnd.cc
@@ -5,7 +5,7 @@
#include "ui/gfx/win/singleton_hwnd.h"
#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
#include "ui/gfx/win/singleton_hwnd_observer.h"
namespace gfx {
@@ -21,7 +21,7 @@ BOOL SingletonHwnd::ProcessWindowMessage(HWND window,
LPARAM lparam,
LRESULT& result,
DWORD msg_map_id) {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
// If there is no MessageLoop and SingletonHwnd is receiving messages, this
// means it is receiving messages via an external message pump such as COM
// uninitialization.
@@ -37,7 +37,7 @@ BOOL SingletonHwnd::ProcessWindowMessage(HWND window,
}
SingletonHwnd::SingletonHwnd() {
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
// Creating this window in (e.g.) a renderer inhibits shutdown on
// Windows. See http://crbug.com/230122 and http://crbug.com/236039.
return;
diff --git a/chromium/ui/gfx/x/BUILD.gn b/chromium/ui/gfx/x/BUILD.gn
index 24d36ecba8a..293f111afcf 100644
--- a/chromium/ui/gfx/x/BUILD.gn
+++ b/chromium/ui/gfx/x/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
@@ -83,9 +82,12 @@ component("xprotos") {
deps = [
":gen_xprotos",
"//base",
+ "//base:i18n",
+ "//ui/events/platform",
]
sources = get_target_outputs(":gen_xprotos") + [
"xproto_internal.h",
+ "xproto_internal.cc",
"xproto_types.h",
"xproto_types.cc",
"xproto_util.h",
@@ -103,7 +105,7 @@ component("xprotos") {
]
}
-jumbo_component("x") {
+component("x") {
output_name = "gfx_x11"
sources = [
@@ -134,6 +136,7 @@ source_set("unit_test") {
testonly = true
sources = [ "connection_unittest.cc" ]
deps = [
+ "//base",
"//testing/gtest",
"//ui/gfx/x",
]
diff --git a/chromium/ui/gfx/x/DEPS b/chromium/ui/gfx/x/DEPS
new file mode 100644
index 00000000000..a444e969cbd
--- /dev/null
+++ b/chromium/ui/gfx/x/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events/platform/platform_event_source.h",
+]
diff --git a/chromium/ui/gfx/x/connection.cc b/chromium/ui/gfx/x/connection.cc
index af5b6bfb1ae..caf9a2b9e15 100644
--- a/chromium/ui/gfx/x/connection.cc
+++ b/chromium/ui/gfx/x/connection.cc
@@ -6,21 +6,43 @@
#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
+#include <X11/keysym.h>
#include <xcb/xcb.h>
#include <algorithm>
#include "base/command_line.h"
+#include "base/i18n/case_conversion.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_local.h"
#include "ui/gfx/x/bigreq.h"
#include "ui/gfx/x/event.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11_switches.h"
+#include "ui/gfx/x/xkb.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xproto_internal.h"
#include "ui/gfx/x/xproto_types.h"
+extern "C" {
+// This is temporarily required to fix XKB key event processing (bugs 1125886,
+// 1136265, 1136248, 1136206). It should be removed and replaced with an
+// XProto equivalent.
+int XLookupString(XKeyEvent* event_struct,
+ char* buffer_return,
+ int bytes_buffer,
+ ::KeySym* keysym_return,
+ void* status_in_out);
+}
+
namespace x11 {
namespace {
+constexpr KeySym kNoSymbol = static_cast<KeySym>(0);
+
// On the wire, sequence IDs are 16 bits. In xcb, they're usually extended to
// 32 and sometimes 64 bits. In Xlib, they're extended to unsigned long, which
// may be 32 or 64 bits depending on the platform. This function is intended to
@@ -42,28 +64,191 @@ auto CompareSequenceIds(T t, U u) {
return static_cast<SignedType>(t0 - u0);
}
-XDisplay* OpenNewXDisplay() {
+XDisplay* OpenNewXDisplay(const std::string& address) {
if (!XInitThreads())
return nullptr;
std::string display_str =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kX11Display);
+ address.empty()
+ ? base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kX11Display)
+ : address;
return XOpenDisplay(display_str.empty() ? nullptr : display_str.c_str());
}
+// Ported from XConvertCase:
+// https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/2b7598221d87049d03e9a95fcb541c37c8728184/src/KeyBind.c#L645
+void ConvertCaseImpl(uint32_t sym, uint32_t* lower, uint32_t* upper) {
+ // Unicode keysym
+ if ((sym & 0xff000000) == 0x01000000) {
+ base::string16 string({sym & 0x00ffffff});
+ auto lower_string = base::i18n::ToLower(string);
+ auto upper_string = base::i18n::ToUpper(string);
+ *lower = lower_string[0] | 0x01000000;
+ *upper = upper_string[0] | 0x01000000;
+ return;
+ }
+
+ *lower = sym;
+ *upper = sym;
+
+ switch (sym >> 8) {
+ // Latin 1
+ case 0:
+ if ((sym >= XK_A) && (sym <= XK_Z))
+ *lower += (XK_a - XK_A);
+ else if ((sym >= XK_a) && (sym <= XK_z))
+ *upper -= (XK_a - XK_A);
+ else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
+ *lower += (XK_agrave - XK_Agrave);
+ else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
+ *upper -= (XK_agrave - XK_Agrave);
+ else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
+ *lower += (XK_oslash - XK_Ooblique);
+ else if ((sym >= XK_oslash) && (sym <= XK_thorn))
+ *upper -= (XK_oslash - XK_Ooblique);
+ break;
+ // Latin 2
+ case 1:
+ if (sym == XK_Aogonek)
+ *lower = XK_aogonek;
+ else if (sym >= XK_Lstroke && sym <= XK_Sacute)
+ *lower += (XK_lstroke - XK_Lstroke);
+ else if (sym >= XK_Scaron && sym <= XK_Zacute)
+ *lower += (XK_scaron - XK_Scaron);
+ else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
+ *lower += (XK_zcaron - XK_Zcaron);
+ else if (sym == XK_aogonek)
+ *upper = XK_Aogonek;
+ else if (sym >= XK_lstroke && sym <= XK_sacute)
+ *upper -= (XK_lstroke - XK_Lstroke);
+ else if (sym >= XK_scaron && sym <= XK_zacute)
+ *upper -= (XK_scaron - XK_Scaron);
+ else if (sym >= XK_zcaron && sym <= XK_zabovedot)
+ *upper -= (XK_zcaron - XK_Zcaron);
+ else if (sym >= XK_Racute && sym <= XK_Tcedilla)
+ *lower += (XK_racute - XK_Racute);
+ else if (sym >= XK_racute && sym <= XK_tcedilla)
+ *upper -= (XK_racute - XK_Racute);
+ break;
+ // Latin 3
+ case 2:
+ if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
+ *lower += (XK_hstroke - XK_Hstroke);
+ else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
+ *lower += (XK_gbreve - XK_Gbreve);
+ else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
+ *upper -= (XK_hstroke - XK_Hstroke);
+ else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
+ *upper -= (XK_gbreve - XK_Gbreve);
+ else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
+ *lower += (XK_cabovedot - XK_Cabovedot);
+ else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
+ *upper -= (XK_cabovedot - XK_Cabovedot);
+ break;
+ // Latin 4
+ case 3:
+ if (sym >= XK_Rcedilla && sym <= XK_Tslash)
+ *lower += (XK_rcedilla - XK_Rcedilla);
+ else if (sym >= XK_rcedilla && sym <= XK_tslash)
+ *upper -= (XK_rcedilla - XK_Rcedilla);
+ else if (sym == XK_ENG)
+ *lower = XK_eng;
+ else if (sym == XK_eng)
+ *upper = XK_ENG;
+ else if (sym >= XK_Amacron && sym <= XK_Umacron)
+ *lower += (XK_amacron - XK_Amacron);
+ else if (sym >= XK_amacron && sym <= XK_umacron)
+ *upper -= (XK_amacron - XK_Amacron);
+ break;
+ // Cyrillic
+ case 6:
+ if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
+ *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
+ else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
+ *upper += (XK_Serbian_DJE - XK_Serbian_dje);
+ else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
+ *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
+ else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
+ *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
+ break;
+ // Greek
+ case 7:
+ if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
+ *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+ else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
+ sym != XK_Greek_iotaaccentdieresis &&
+ sym != XK_Greek_upsilonaccentdieresis)
+ *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+ else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
+ *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
+ else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
+ sym != XK_Greek_finalsmallsigma)
+ *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
+ break;
+ // Latin 9
+ case 0x13:
+ if (sym == XK_OE)
+ *lower = XK_oe;
+ else if (sym == XK_oe)
+ *upper = XK_OE;
+ else if (sym == XK_Ydiaeresis)
+ *lower = XK_ydiaeresis;
+ break;
+ }
+}
+
+void ConvertCase(KeySym sym, KeySym* lower, KeySym* upper) {
+ uint32_t lower32;
+ uint32_t upper32;
+ ConvertCaseImpl(static_cast<uint32_t>(sym), &lower32, &upper32);
+ *lower = static_cast<KeySym>(lower32);
+ *upper = static_cast<KeySym>(upper32);
+}
+
+bool IsKeypadKey(KeySym keysym) {
+ auto key = static_cast<uint32_t>(keysym);
+ return key >= XK_KP_Space && key <= XK_KP_Equal;
+}
+
+bool IsPrivateKeypadKey(KeySym keysym) {
+ auto key = static_cast<uint32_t>(keysym);
+ return key >= 0x11000000 && key <= 0x1100FFFF;
+}
+
+base::ThreadLocalOwnedPointer<Connection>& GetConnectionTLS() {
+ static base::NoDestructor<base::ThreadLocalOwnedPointer<Connection>> tls;
+ return *tls;
+}
+
} // namespace
+// static
Connection* Connection::Get() {
- static Connection* instance = new Connection;
- return instance;
+ auto& tls = GetConnectionTLS();
+ if (Connection* connection = tls.Get())
+ return connection;
+ auto connection = std::make_unique<Connection>();
+ auto* p_connection = connection.get();
+ tls.Set(std::move(connection));
+ return p_connection;
+}
+
+// static
+void Connection::Set(std::unique_ptr<x11::Connection> connection) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(connection->sequence_checker_);
+ auto& tls = GetConnectionTLS();
+ DCHECK(!tls.Get());
+ tls.Set(std::move(connection));
}
-Connection::Connection() : XProto(this), display_(OpenNewXDisplay()) {
+Connection::Connection(const std::string& address)
+ : XProto(this), display_(OpenNewXDisplay(address)) {
if (display_) {
XSetEventQueueOwner(display_, XCBOwnsEventQueue);
- setup_ = Read<Setup>(
- reinterpret_cast<const uint8_t*>(xcb_get_setup(XcbConnection())));
+ auto buf = ReadBuffer(base::MakeRefCounted<UnretainedRefCountedMemory>(
+ xcb_get_setup(XcbConnection())));
+ setup_ = Read<Setup>(&buf);
default_screen_ = &setup_.roots[DefaultScreenId()];
default_root_depth_ = &*std::find_if(
default_screen_->allowed_depths.begin(),
@@ -88,14 +273,30 @@ Connection::Connection() : XProto(this), display_(OpenNewXDisplay()) {
ExtensionManager::Init(this);
if (auto response = bigreq().Enable({}).Sync())
extended_max_request_length_ = response->maximum_request_length;
+
+ const Format* formats[256];
+ memset(formats, 0, sizeof(formats));
+ for (const auto& format : setup_.pixmap_formats)
+ formats[format.depth] = &format;
+
+ for (const auto& depth : default_screen().allowed_depths) {
+ const Format* format = formats[depth.depth];
+ for (const auto& visual : depth.visuals)
+ default_screen_visuals_[visual.visual_id] = VisualInfo{format, &visual};
+ }
+
+ ResetKeyboardState();
}
Connection::~Connection() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ platform_event_source.reset();
if (display_)
XCloseDisplay(display_);
}
xcb_connection_t* Connection::XcbConnection() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!display())
return nullptr;
return XGetXCBConnection(display());
@@ -117,6 +318,7 @@ bool Connection::HasNextResponse() const {
}
int Connection::DefaultScreenId() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This is not part of the setup data as the server has no concept of a
// default screen. Instead, it's part of the display name. Eg in
// "localhost:0.0", the screen ID is the second "0".
@@ -124,29 +326,85 @@ int Connection::DefaultScreenId() const {
}
bool Connection::Ready() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return display_ && !xcb_connection_has_error(XGetXCBConnection(display_));
}
void Connection::Flush() {
- XFlush(display_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (display_)
+ XFlush(display_);
}
void Connection::Sync() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GetInputFocus({}).Sync();
}
void Connection::ReadResponses() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
while (auto* event = xcb_poll_for_event(XcbConnection())) {
- events_.emplace_back(event, this);
- free(event);
+ events_.emplace_back(base::MakeRefCounted<MallocedRefCountedMemory>(event),
+ this);
}
}
bool Connection::HasPendingResponses() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !events_.empty() || HasNextResponse();
}
+const Connection::VisualInfo* Connection::GetVisualInfoFromId(
+ VisualId id) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto it = default_screen_visuals_.find(id);
+ if (it != default_screen_visuals_.end())
+ return &it->second;
+ return nullptr;
+}
+
+KeyCode Connection::KeysymToKeycode(KeySym keysym) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ uint8_t min_keycode = static_cast<uint8_t>(setup_.min_keycode);
+ uint8_t max_keycode = static_cast<uint8_t>(setup_.max_keycode);
+ uint8_t count = max_keycode - min_keycode + 1;
+ DCHECK_EQ(count * keyboard_mapping_.keysyms_per_keycode,
+ static_cast<int>(keyboard_mapping_.keysyms.size()));
+ for (size_t i = 0; i < keyboard_mapping_.keysyms.size(); i++) {
+ if (keyboard_mapping_.keysyms[i] == keysym) {
+ return static_cast<KeyCode>(min_keycode +
+ i / keyboard_mapping_.keysyms_per_keycode);
+ }
+ }
+ return {};
+}
+
+KeySym Connection::KeycodeToKeysym(uint32_t keycode, unsigned int modifiers) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ XKeyEvent key_event{
+ .type = KeyEvent::Press,
+ .display = display_,
+ .state = modifiers,
+ .keycode = keycode,
+ };
+ ::KeySym keysym;
+ XLookupString(&key_event, nullptr, 0, &keysym, nullptr);
+ return static_cast<x11::KeySym>(keysym);
+}
+
+std::unique_ptr<Connection> Connection::Clone() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return std::make_unique<Connection>(display_ ? XDisplayString(display_) : "");
+}
+
+void Connection::DetachFromSequence() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
void Connection::Dispatch(Delegate* delegate) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(display_);
auto process_next_response = [&] {
@@ -158,9 +416,10 @@ void Connection::Dispatch(Delegate* delegate) {
xcb_generic_error_t* raw_error = nullptr;
xcb_poll_for_reply(connection, request.sequence, &raw_reply, &raw_error);
- std::move(request.callback)
- .Run(FutureBase::RawReply{reinterpret_cast<uint8_t*>(raw_reply)},
- FutureBase::RawError{raw_error});
+ scoped_refptr<MallocedRefCountedMemory> reply;
+ if (raw_reply)
+ reply = base::MakeRefCounted<MallocedRefCountedMemory>(raw_reply);
+ std::move(request.callback).Run(reply, FutureBase::RawError{raw_error});
};
auto process_next_event = [&] {
@@ -205,6 +464,7 @@ void Connection::Dispatch(Delegate* delegate) {
void Connection::AddRequest(unsigned int sequence,
FutureBase::ResponseCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(requests_.empty() ||
CompareSequenceIds(requests_.back().sequence, sequence) < 0);
@@ -212,19 +472,34 @@ void Connection::AddRequest(unsigned int sequence,
}
void Connection::PreDispatchEvent(const Event& event) {
+ if (auto* mapping = event.As<MappingNotifyEvent>()) {
+ if (mapping->request == Mapping::Modifier ||
+ mapping->request == Mapping::Keyboard) {
+ setup_.min_keycode = mapping->first_keycode;
+ setup_.max_keycode = static_cast<x11::KeyCode>(
+ static_cast<int>(mapping->first_keycode) + mapping->count - 1);
+ ResetKeyboardState();
+ }
+ }
+ if (auto* notify = event.As<x11::Xkb::NewKeyboardNotifyEvent>()) {
+ setup_.min_keycode = notify->minKeyCode;
+ setup_.max_keycode = notify->maxKeyCode;
+ ResetKeyboardState();
+ }
+
// This is adapted from XRRUpdateConfiguration.
- if (auto* configure = event.As<x11::ConfigureNotifyEvent>()) {
+ if (auto* configure = event.As<ConfigureNotifyEvent>()) {
int index = ScreenIndexFromRootWindow(configure->window);
if (index != -1) {
setup_.roots[index].width_in_pixels = configure->width;
setup_.roots[index].height_in_pixels = configure->height;
}
- } else if (auto* screen = event.As<x11::RandR::ScreenChangeNotifyEvent>()) {
+ } else if (auto* screen = event.As<RandR::ScreenChangeNotifyEvent>()) {
int index = ScreenIndexFromRootWindow(screen->root);
DCHECK_GE(index, 0);
- bool portrait = static_cast<bool>(
- screen->rotation &
- (x11::RandR::Rotation::Rotate_90 | x11::RandR::Rotation::Rotate_270));
+ bool portrait =
+ static_cast<bool>(screen->rotation & (RandR::Rotation::Rotate_90 |
+ RandR::Rotation::Rotate_270));
if (portrait) {
setup_.roots[index].width_in_pixels = screen->height;
setup_.roots[index].height_in_pixels = screen->width;
@@ -239,7 +514,7 @@ void Connection::PreDispatchEvent(const Event& event) {
}
}
-int Connection::ScreenIndexFromRootWindow(x11::Window root) const {
+int Connection::ScreenIndexFromRootWindow(Window root) const {
for (size_t i = 0; i < setup_.roots.size(); i++) {
if (setup_.roots[i].root == root)
return i;
@@ -247,4 +522,137 @@ int Connection::ScreenIndexFromRootWindow(x11::Window root) const {
return -1;
}
+void Connection::ResetKeyboardState() {
+ uint8_t min_keycode = static_cast<uint8_t>(setup_.min_keycode);
+ uint8_t max_keycode = static_cast<uint8_t>(setup_.max_keycode);
+ uint8_t count = max_keycode - min_keycode + 1;
+ auto keyboard_future = GetKeyboardMapping({setup_.min_keycode, count});
+ auto modifier_future = GetModifierMapping({});
+ Flush();
+ if (auto reply = keyboard_future.Sync())
+ keyboard_mapping_ = std::move(*reply.reply);
+ if (auto reply = modifier_future.Sync())
+ modifier_mapping_ = std::move(*reply.reply);
+
+ for (uint8_t i = 0; i < modifier_mapping_.keycodes_per_modifier; i++) {
+ // Lock modifiers are in the second row of the matrix
+ size_t index = 2 * modifier_mapping_.keycodes_per_modifier + i;
+ for (uint8_t j = 0; j < keyboard_mapping_.keysyms_per_keycode; j++) {
+ auto sym = static_cast<uint32_t>(
+ KeyCodetoKeySym(modifier_mapping_.keycodes[index], j));
+ if (sym == XK_Caps_Lock || sym == XK_ISO_Lock) {
+ lock_meaning_ = XK_Caps_Lock;
+ break;
+ }
+ if (sym == XK_Shift_Lock)
+ lock_meaning_ = XK_Shift_Lock;
+ }
+ }
+
+ // Mod<n> is at row (n + 2) of the matrix. This iterates from Mod1 to Mod5.
+ for (int mod = 3; mod < 8; mod++) {
+ for (size_t i = 0; i < modifier_mapping_.keycodes_per_modifier; i++) {
+ size_t index = mod * modifier_mapping_.keycodes_per_modifier + i;
+ for (uint8_t j = 0; j < keyboard_mapping_.keysyms_per_keycode; j++) {
+ auto sym = static_cast<uint32_t>(
+ KeyCodetoKeySym(modifier_mapping_.keycodes[index], j));
+ if (sym == XK_Mode_switch)
+ mode_switch_ |= 1 << mod;
+ if (sym == XK_Num_Lock)
+ num_lock_ |= 1 << mod;
+ }
+ }
+ }
+}
+
+// Ported from xcb_key_symbols_get_keysym
+// https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms/-/blob/691515491a4a3c119adc6c769c29de264b3f3806/keysyms/keysyms.c#L189
+KeySym Connection::KeyCodetoKeySym(KeyCode keycode, int column) const {
+ uint8_t key = static_cast<uint8_t>(keycode);
+ uint8_t n_keysyms = keyboard_mapping_.keysyms_per_keycode;
+
+ uint8_t min_key = static_cast<uint8_t>(setup_.min_keycode);
+ uint8_t max_key = static_cast<uint8_t>(setup_.max_keycode);
+ if (column < 0 || (column >= n_keysyms && column > 3) || key < min_key ||
+ key > max_key) {
+ return kNoSymbol;
+ }
+
+ const auto* syms = &keyboard_mapping_.keysyms[(key - min_key) * n_keysyms];
+ if (column < 4) {
+ if (column > 1) {
+ while ((n_keysyms > 2) && (syms[n_keysyms - 1] == kNoSymbol))
+ n_keysyms--;
+ if (n_keysyms < 3)
+ column -= 2;
+ }
+ if ((n_keysyms <= (column | 1)) || (syms[column | 1] == kNoSymbol)) {
+ KeySym lsym, usym;
+ ConvertCase(syms[column & ~1], &lsym, &usym);
+ if (!(column & 1))
+ return lsym;
+ if (usym == lsym)
+ return kNoSymbol;
+ return usym;
+ }
+ }
+ return syms[column];
+}
+
+// Ported from _XTranslateKey:
+// https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/2b7598221d87049d03e9a95fcb541c37c8728184/src/KeyBind.c#L761
+KeySym Connection::TranslateKey(uint32_t key, unsigned int modifiers) const {
+ uint8_t min_key = static_cast<uint8_t>(setup_.min_keycode);
+ uint8_t max_key = static_cast<uint8_t>(setup_.max_keycode);
+ if (key < min_key || key > max_key)
+ return kNoSymbol;
+
+ uint8_t n_keysyms = keyboard_mapping_.keysyms_per_keycode;
+ if (!n_keysyms)
+ return {};
+ const auto* syms = &keyboard_mapping_.keysyms[(key - min_key) * n_keysyms];
+ while ((n_keysyms > 2) && (syms[n_keysyms - 1] == kNoSymbol))
+ n_keysyms--;
+ if ((n_keysyms > 2) && (modifiers & mode_switch_)) {
+ syms += 2;
+ n_keysyms -= 2;
+ }
+
+ if ((modifiers & num_lock_) &&
+ (n_keysyms > 1 &&
+ (IsKeypadKey(syms[1]) || IsPrivateKeypadKey(syms[1])))) {
+ if ((modifiers & ShiftMask) ||
+ ((modifiers & LockMask) && (lock_meaning_ == XK_Shift_Lock))) {
+ return syms[0];
+ }
+ return syms[1];
+ }
+
+ KeySym lower;
+ KeySym upper;
+ if (!(modifiers & ShiftMask) &&
+ (!(modifiers & LockMask) || (lock_meaning_ == NoSymbol))) {
+ if ((n_keysyms == 1) || (syms[1] == kNoSymbol)) {
+ ConvertCase(syms[0], &lower, &upper);
+ return lower;
+ }
+ return syms[0];
+ }
+
+ if (!(modifiers & LockMask) || (lock_meaning_ != XK_Caps_Lock)) {
+ if ((n_keysyms == 1) || ((upper = syms[1]) == kNoSymbol))
+ ConvertCase(syms[0], &lower, &upper);
+ return upper;
+ }
+
+ KeySym sym;
+ if ((n_keysyms == 1) || ((sym = syms[1]) == kNoSymbol))
+ sym = syms[0];
+ ConvertCase(sym, &lower, &upper);
+ if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
+ ((sym != upper) || (lower == upper)))
+ ConvertCase(syms[0], &lower, &upper);
+ return upper;
+}
+
} // namespace x11
diff --git a/chromium/ui/gfx/x/connection.h b/chromium/ui/gfx/x/connection.h
index a103b431d71..d764be97e49 100644
--- a/chromium/ui/gfx/x/connection.h
+++ b/chromium/ui/gfx/x/connection.h
@@ -9,6 +9,8 @@
#include <queue>
#include "base/component_export.h"
+#include "base/sequence_checker.h"
+#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/x/event.h"
#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/xproto.h"
@@ -28,27 +30,52 @@ class COMPONENT_EXPORT(X11) Connection : public XProto,
virtual ~Delegate() = default;
};
- // Gets or creates the singleton connection.
+ struct VisualInfo {
+ const Format* format;
+ const VisualType* visual_type;
+ };
+
+ // Gets or creates the thread local connection instance.
static Connection* Get();
- explicit Connection();
+ // Sets the thread local connection instance.
+ static void Set(std::unique_ptr<x11::Connection> connection);
+
+ explicit Connection(const std::string& address = "");
~Connection();
Connection(const Connection&) = delete;
Connection(Connection&&) = delete;
- XDisplay* display() const { return display_; }
+ XDisplay* display() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return display_;
+ }
xcb_connection_t* XcbConnection();
uint32_t extended_max_request_length() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return extended_max_request_length_;
}
- const Setup& setup() const { return setup_; }
- const Screen& default_screen() const { return *default_screen_; }
- x11::Window default_root() const { return default_screen().root; }
- const Depth& default_root_depth() const { return *default_root_depth_; }
+ const Setup& setup() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return setup_;
+ }
+ const Screen& default_screen() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return *default_screen_;
+ }
+ x11::Window default_root() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return default_screen().root;
+ }
+ const Depth& default_root_depth() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return *default_root_depth_;
+ }
const VisualType& default_root_visual() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return *default_root_visual_;
}
@@ -56,6 +83,7 @@ class COMPONENT_EXPORT(X11) Connection : public XProto,
template <typename T>
T GenerateId() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return static_cast<T>(xcb_generate_id(XcbConnection()));
}
@@ -77,8 +105,30 @@ class COMPONENT_EXPORT(X11) Connection : public XProto,
// Dispatch any buffered events, errors, or replies.
void Dispatch(Delegate* delegate);
+ // Returns the visual data for |id|, or nullptr if the visual with that ID
+ // doesn't exist or only exists on a non-default screen.
+ const VisualInfo* GetVisualInfoFromId(VisualId id) const;
+
+ KeyCode KeysymToKeycode(KeySym keysym);
+
+ KeySym KeycodeToKeysym(uint32_t keycode, unsigned int modifiers);
+
// Access the event buffer. Clients can add, delete, or modify events.
- std::list<Event>& events() { return events_; }
+ std::list<Event>& events() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return events_;
+ }
+
+ std::unique_ptr<Connection> Clone() const;
+
+ // Releases ownership of this connection to a different thread.
+ void DetachFromSequence();
+
+ // The viz compositor thread hangs a PlatformEventSource off the connection so
+ // that it gets destroyed at the appropriate time.
+ // TODO(thomasanderson): This is a layering violation and this should be moved
+ // somewhere else.
+ std::unique_ptr<ui::PlatformEventSource> platform_event_source;
private:
friend class FutureBase;
@@ -100,6 +150,12 @@ class COMPONENT_EXPORT(X11) Connection : public XProto,
int ScreenIndexFromRootWindow(x11::Window root) const;
+ void ResetKeyboardState();
+
+ KeySym KeyCodetoKeySym(KeyCode keycode, int column) const;
+
+ KeySym TranslateKey(uint32_t keycode, unsigned int modifiers) const;
+
XDisplay* const display_;
uint32_t extended_max_request_length_ = 0;
@@ -109,9 +165,20 @@ class COMPONENT_EXPORT(X11) Connection : public XProto,
Depth* default_root_depth_ = nullptr;
VisualType* default_root_visual_ = nullptr;
+ std::unordered_map<VisualId, VisualInfo> default_screen_visuals_;
+
+ // Keyboard state.
+ GetKeyboardMappingReply keyboard_mapping_;
+ GetModifierMappingReply modifier_mapping_;
+ uint16_t lock_meaning_ = 0;
+ uint8_t mode_switch_ = 0;
+ uint8_t num_lock_ = 0;
+
std::list<Event> events_;
std::queue<Request> requests_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace x11
diff --git a/chromium/ui/gfx/x/connection_unittest.cc b/chromium/ui/gfx/x/connection_unittest.cc
index de2285911a4..42e581c69cc 100644
--- a/chromium/ui/gfx/x/connection_unittest.cc
+++ b/chromium/ui/gfx/x/connection_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "ui/gfx/x/connection.h"
+#include "base/memory/ref_counted_memory.h"
#include "ui/gfx/x/xproto.h"
#undef Bool
@@ -72,23 +73,23 @@ TEST(X11ConnectionTest, Event) {
});
EXPECT_FALSE(cwa_future.Sync().error);
+ std::vector<uint8_t> data{0};
auto prop_future = connection.ChangeProperty({
.window = static_cast<x11::Window>(window),
.property = x11::Atom::WM_NAME,
.type = x11::Atom::STRING,
.format = CHAR_BIT,
.data_len = 1,
- .data = std::vector<uint8_t>{0},
+ .data = base::RefCountedBytes::TakeVector(&data),
});
EXPECT_FALSE(prop_future.Sync().error);
connection.ReadResponses();
ASSERT_EQ(connection.events().size(), 1u);
- XEvent& event = connection.events().front().xlib_event();
- auto property_notify_opcode = PropertyNotifyEvent::opcode;
- EXPECT_EQ(event.type, property_notify_opcode);
- EXPECT_EQ(event.xproperty.atom, static_cast<uint32_t>(x11::Atom::WM_NAME));
- EXPECT_EQ(event.xproperty.state, static_cast<int>(Property::NewValue));
+ auto* prop = connection.events().front().As<x11::PropertyNotifyEvent>();
+ ASSERT_TRUE(prop);
+ EXPECT_EQ(prop->atom, x11::Atom::WM_NAME);
+ EXPECT_EQ(prop->state, Property::NewValue);
}
TEST(X11ConnectionTest, Error) {
diff --git a/chromium/ui/gfx/x/event.cc b/chromium/ui/gfx/x/event.cc
index 238e06bb656..4ce77f09676 100644
--- a/chromium/ui/gfx/x/event.cc
+++ b/chromium/ui/gfx/x/event.cc
@@ -4,17 +4,14 @@
#include "ui/gfx/x/event.h"
-#include <X11/Xlibint.h>
-#include <X11/extensions/XInput2.h>
-
-// Xlibint.h defines those as macros, which breaks the C++ versions in
-// the std namespace.
-#undef max
-#undef min
-
#include <cstring>
+#include "base/check_op.h"
+#include "base/memory/scoped_refptr.h"
#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xproto_internal.h"
+#include "ui/gfx/x/xproto_types.h"
namespace x11 {
@@ -22,7 +19,22 @@ Event::Event() = default;
Event::Event(xcb_generic_event_t* xcb_event,
x11::Connection* connection,
+ bool sequence_valid)
+ : Event(base::MakeRefCounted<x11::UnretainedRefCountedMemory>(xcb_event),
+ connection,
+ sequence_valid) {
+ // Make sure the event is a fixed-size (32 bytes) event, otherwise
+ // UnretainedRefCountedMemory may be unsafe to use if the event contains
+ // variable-sized data.
+ DCHECK_NE(xcb_event->response_type & ~x11::kSendEventMask,
+ x11::GeGenericEvent::opcode);
+}
+
+Event::Event(scoped_refptr<base::RefCountedMemory> event_bytes,
+ x11::Connection* connection,
bool sequence_valid) {
+ auto* xcb_event = reinterpret_cast<xcb_generic_event_t*>(
+ const_cast<uint8_t*>(event_bytes->data()));
XDisplay* display = connection->display();
sequence_valid_ = sequence_valid;
@@ -42,28 +54,28 @@ Event::Event(xcb_generic_event_t* xcb_event,
if ((xcb_event->response_type & ~kSendEventMask) ==
x11::GeGenericEvent::opcode) {
auto* ge = reinterpret_cast<xcb_ge_event_t*>(xcb_event);
- memmove(&ge->full_sequence, &ge[1], ge->length * 4);
+ constexpr size_t ge_length = sizeof(xcb_raw_generic_event_t);
+ constexpr size_t offset = sizeof(ge->full_sequence);
+ size_t extended_length = ge->length * 4;
+ if (extended_length < ge_length) {
+ // If the additional data is smaller than the fixed size event, shift
+ // the additional data to the left.
+ memmove(&ge->full_sequence, &ge[1], extended_length);
+ } else {
+ // Otherwise shift the fixed size event to the right.
+ char* addr = reinterpret_cast<char*>(xcb_event);
+ memmove(addr + offset, addr, ge_length);
+ event_bytes = base::MakeRefCounted<OffsetRefCountedMemory>(
+ event_bytes, offset, ge_length + extended_length);
+ xcb_event = reinterpret_cast<xcb_generic_event_t*>(addr + offset);
+ }
}
}
// Xlib sometimes modifies |xcb_event|, so let it handle the event after
// we parse it with ReadEvent().
- ReadEvent(this, connection, reinterpret_cast<uint8_t*>(xcb_event));
-
- _XEnq(display, reinterpret_cast<xEvent*>(xcb_event));
- if (!XEventsQueued(display, QueuedAlready)) {
- // If Xlib gets an event it doesn't recognize (eg. from an
- // extension it doesn't know about), it won't add the event to the
- // queue. In this case, zero-out the event data. This will set
- // the event type to 0, which does not correspond to any event.
- // This is safe because event handlers should always check the
- // event type before downcasting to a concrete event.
- memset(&xlib_event_, 0, sizeof(xlib_event_));
- return;
- }
- XNextEvent(display, &xlib_event_);
- if (xlib_event_.type == x11::GeGenericEvent::opcode)
- XGetEventData(display, &xlib_event_.xcookie);
+ ReadBuffer buf(event_bytes);
+ ReadEvent(this, connection, &buf);
}
Event::Event(Event&& event) {
@@ -83,19 +95,6 @@ Event::~Event() {
}
void Event::Dealloc() {
- if (xlib_event_.type == x11::GeGenericEvent::opcode &&
- xlib_event_.xcookie.data) {
- if (custom_allocated_xlib_event_) {
- XIDeviceEvent* xiev =
- static_cast<XIDeviceEvent*>(xlib_event_.xcookie.data);
- delete[] xiev->valuators.mask;
- delete[] xiev->valuators.values;
- delete[] xiev->buttons.mask;
- delete xiev;
- } else {
- XFreeEventData(xlib_event_.xcookie.display, &xlib_event_.xcookie);
- }
- }
if (deleter_)
deleter_(event_);
}
diff --git a/chromium/ui/gfx/x/event.h b/chromium/ui/gfx/x/event.h
index 37073dbd584..5ad11ca9ee3 100644
--- a/chromium/ui/gfx/x/event.h
+++ b/chromium/ui/gfx/x/event.h
@@ -12,33 +12,41 @@
#include <utility>
#include "base/component_export.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "ui/gfx/x/xproto.h"
namespace x11 {
class Connection;
class Event;
+struct ReadBuffer;
COMPONENT_EXPORT(X11)
-void ReadEvent(Event* event, Connection* connection, const uint8_t* buffer);
+void ReadEvent(Event* event, Connection* connection, ReadBuffer* buffer);
class COMPONENT_EXPORT(X11) Event {
public:
- // Used to create events for testing.
template <typename T>
- Event(XEvent* xlib_event, T&& xproto_event) {
+ explicit Event(T&& xproto_event) {
sequence_valid_ = true;
- sequence_ = xlib_event_.xany.serial;
- custom_allocated_xlib_event_ = true;
- xlib_event_ = *xlib_event;
+ sequence_ = xproto_event.sequence;
type_id_ = T::type_id;
deleter_ = [](void* event) { delete reinterpret_cast<T*>(event); };
- event_ = new T(std::forward<T>(xproto_event));
+ T* event = new T(std::forward<T>(xproto_event));
+ event_ = event;
+ window_ = event->GetWindow();
}
Event();
Event(xcb_generic_event_t* xcb_event,
Connection* connection,
bool sequence_valid = true);
+ // |event_bytes| is modified and will not be valid after this call.
+ // A copy is necessary if the original data is still needed.
+ Event(scoped_refptr<base::RefCountedMemory> event_bytes,
+ Connection* connection,
+ bool sequence_valid = true);
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
@@ -63,28 +71,26 @@ class COMPONENT_EXPORT(X11) Event {
bool sequence_valid() const { return sequence_valid_; }
uint32_t sequence() const { return sequence_; }
- const XEvent& xlib_event() const { return xlib_event_; }
- XEvent& xlib_event() { return xlib_event_; }
+ x11::Window window() const { return window_ ? *window_ : x11::Window::None; }
private:
friend void ReadEvent(Event* event,
Connection* connection,
- const uint8_t* buffer);
+ ReadBuffer* buffer);
void Dealloc();
bool sequence_valid_ = false;
- uint32_t sequence_ = 0;
-
- // Indicates if |xlib_event_| was allocated manually and therefore
- // needs to be freed manually.
- bool custom_allocated_xlib_event_ = false;
- XEvent xlib_event_{};
+ uint16_t sequence_ = 0;
// XProto event state.
int type_id_ = 0;
void (*deleter_)(void*) = nullptr;
void* event_ = nullptr;
+
+ // This member points to a field in |event_|, or may be nullptr if there's no
+ // associated window for the event. It's owned by |event_|, not us.
+ x11::Window* window_ = nullptr;
};
} // namespace x11
diff --git a/chromium/ui/gfx/x/gen_xproto.py b/chromium/ui/gfx/x/gen_xproto.py
index 24a2efcc245..f5df00a7cbc 100644
--- a/chromium/ui/gfx/x/gen_xproto.py
+++ b/chromium/ui/gfx/x/gen_xproto.py
@@ -189,6 +189,7 @@ RENAME = {
'COLORMAP': 'ColorMap',
'Connection': 'RandRConnection',
'CP': 'CreatePictureAttribute',
+ 'CS': 'ClientSpec',
'CW': 'CreateWindowAttribute',
'DAMAGE': 'DamageId',
'DIRECTFORMAT': 'DirectFormat',
@@ -232,6 +233,7 @@ READ_SPECIAL = set([
WRITE_SPECIAL = set([
('xcb', 'ClientMessage'),
+ ('xcb', 'Expose'),
('xcb', 'UnmapNotify'),
('xcb', 'SelectionNotify'),
])
@@ -283,6 +285,11 @@ def event_base_name(names):
return name
+def list_size(name, list_type):
+ separator = '->' if list_type.is_ref_counted_memory else '.'
+ return '%s%ssize()' % (name, separator)
+
+
# Left-pad with 2 spaces while this class is alive.
class Indent:
def __init__(self, xproto, opening_line, closing_line):
@@ -454,6 +461,8 @@ class GenXproto(FileWriter):
return '::'.join(name[chop:])
def fieldtype(self, field):
+ if field.isfd:
+ return 'base::ScopedFD'
return self.qualtype(field.type,
field.enum if field.enum else field.field_type)
@@ -486,7 +495,8 @@ class GenXproto(FileWriter):
if field.type.is_list:
len_name = field_name + '_len'
if not self.field_from_scope(len_name):
- self.write('size_t %s = %s.size();' % (len_name, field_name))
+ self.write('size_t %s = %s;' %
+ (len_name, list_size(field_name, field.type)))
return 1
@@ -575,14 +585,6 @@ class GenXproto(FileWriter):
if (name[-1] in ('FLOAT32', 'FLOAT64')
or renamed in self.replace_with_enum):
return
- elif name[-1] == 'FP1616':
- # Xcbproto defines FP1616 as uint32_t instead of a struct of
- # two 16-bit ints, which is how it's intended to be used.
- with Indent(self, 'struct Fp1616 {', '};'):
- self.write('int16_t integral;')
- self.write('uint16_t frac;')
- self.write()
- return
xidunion = self.get_xidunion_element(name)
if xidunion:
@@ -593,8 +595,19 @@ class GenXproto(FileWriter):
self.write()
def copy_primitive(self, name):
- self.write('%s(&%s, &buf);' %
- ('Read' if self.is_read else 'Write', name))
+ if self.is_read:
+ self.write('Read(&%s, &buf);' % name)
+ else:
+ self.write('buf.Write(&%s);' % name)
+
+ def copy_fd(self, field, name):
+ if self.is_read:
+ self.write('%s = base::ScopedFD(buf.TakeFd());' % name)
+ else:
+ # We take the request struct as const&, so dup() the fd to preserve
+ # const-correctness because XCB close()s it after writing it.
+ self.write('buf.fds().push_back(HANDLE_EINTR(dup(%s.get())));' %
+ name)
def copy_special_field(self, field):
type_name = self.fieldtype(field)
@@ -647,10 +660,11 @@ class GenXproto(FileWriter):
if not case.field_name:
return fields
name = safe_name(case.field_name)
- with Indent(self, 'struct %s_t {' % name, '};'):
+ typename = adjust_type_name(name)
+ with Indent(self, 'struct %s {' % typename, '};'):
for field in fields:
self.write('%s %s{};' % field)
- return [(name + '_t', name)]
+ return [(typename, name)]
def copy_case(self, case, switch_name):
op = 'CaseEq' if case.type.is_case else 'CaseAnd'
@@ -693,26 +707,29 @@ class GenXproto(FileWriter):
name = safe_name(field.field_name)
assert (t.nmemb not in (0, 1))
- if t.nmemb:
+ if t.is_ref_counted_memory:
+ type_name = 'scoped_refptr<base::RefCountedMemory>'
+ elif t.nmemb:
type_name = 'std::array<%s, %d>' % (type_name, t.nmemb)
+ elif type_name == 'char':
+ type_name = 'std::string'
else:
- if type_name == 'void':
- # xcb uses void* in some places, but we prefer to use
- # std::vector<T> when possible. Use T=uint8_t instead of
- # T=void for containers.
- type_name = 'std::vector<uint8_t>'
- elif type_name == 'char':
- type_name = 'std::string'
- else:
- type_name = 'std::vector<%s>' % type_name
+ type_name = 'std::vector<%s>' % type_name
return [(type_name, name)]
def copy_list(self, field):
t = field.type
name = safe_name(field.field_name)
+ size = self.expr(t.expr)
+
+ if t.is_ref_counted_memory:
+ if self.is_read:
+ self.write('%s = buffer->ReadAndAdvance(%s);' % (name, size))
+ else:
+ self.write('buf.AppendBuffer(%s, %s);' % (name, size))
+ return
if not t.nmemb:
- size = self.expr(t.expr)
if self.is_read:
self.write('%s.resize(%s);' % (name, size))
else:
@@ -770,8 +787,9 @@ class GenXproto(FileWriter):
# variable from the given context.
if not self.is_read:
if field.for_list:
- self.write('%s = %s.size();' %
- (name, safe_name(field.for_list.field_name)))
+ size = list_size(safe_name(field.for_list.field_name),
+ field.for_list.type)
+ self.write('%s = %s;' % (name, size))
if field.for_switch:
self.generate_switch_var(field)
@@ -793,12 +811,11 @@ class GenXproto(FileWriter):
elif t.is_container:
with Indent(self, '{', '}'):
self.copy_container(t, name)
- elif t.is_fd:
- # TODO(https://crbug.com/1066670): Copy FDs out of band.
- self.write('NOTIMPLEMENTED();')
else:
assert t.is_simple
- if field.enum:
+ if field.isfd:
+ self.copy_fd(field, name)
+ elif field.enum:
self.copy_enum(field)
else:
self.copy_primitive(name)
@@ -846,6 +863,40 @@ class GenXproto(FileWriter):
for field_type_name in self.declare_field(field):
self.write('%s %s{};' % field_type_name)
+ # This tries to match XEvent.xany.window, except the window will be
+ # x11::Window::None for events that don't have a window, unlike the XEvent
+ # union which will get whatever data happened to be at the offset of
+ # xany.window.
+ def get_window_field(self, event):
+ # The window field is not stored at any particular offset in the event,
+ # so get a list of all the window fields.
+ WINDOW_TYPES = set([
+ ('xcb', 'WINDOW'),
+ ('xcb', 'DRAWABLE'),
+ ('xcb', 'Glx', 'DRAWABLE'),
+ ])
+ # The window we want may not be the first in the list if there are
+ # multiple windows. This is a list of all possible window names,
+ # ordered from highest to lowest priority.
+ WINDOW_NAMES = [
+ 'event',
+ 'window',
+ 'request_window',
+ 'owner',
+ ]
+ windows = set([
+ field.field_name for field in event.fields
+ if field.field_type in WINDOW_TYPES
+ ])
+ if len(windows) == 0:
+ return ''
+ if len(windows) == 1:
+ return list(windows)[0]
+ for name in WINDOW_NAMES:
+ if name in windows:
+ return name
+ assert False
+
def declare_event(self, event, name):
event_name = name[-1] + 'Event'
self.undef(event_name)
@@ -863,6 +914,11 @@ class GenXproto(FileWriter):
self.write('%s = %s,' % (opname, opcode))
self.write('bool send_event{};')
self.declare_fields(event.fields)
+ self.write()
+ window_field = self.get_window_field(event)
+ ret = ('reinterpret_cast<x11::Window*>(&%s)' %
+ window_field if window_field else 'nullptr')
+ self.write('x11::Window* GetWindow() { return %s; }' % ret)
self.write()
def declare_container(self, struct, struct_name):
@@ -883,8 +939,8 @@ class GenXproto(FileWriter):
name = self.qualtype(struct, name)
self.write('template <> COMPONENT_EXPORT(X11)')
self.write('%s Read<%s>(' % (name, name))
- with Indent(self, ' const uint8_t* buffer) {', '}'):
- self.write('ReadBuffer buf{buffer, 0UL};')
+ with Indent(self, ' ReadBuffer* buffer) {', '}'):
+ self.write('auto& buf = *buffer;')
self.write('%s obj;' % name)
self.write()
self.is_read = True
@@ -896,7 +952,7 @@ class GenXproto(FileWriter):
self.namespace = ['x11']
name = self.qualtype(struct, name)
self.write('template <> COMPONENT_EXPORT(X11)')
- self.write('std::vector<uint8_t> Write<%s>(' % name)
+ self.write('WriteBuffer Write<%s>(' % name)
with Indent(self, ' const %s& obj) {' % name, '}'):
self.write('WriteBuffer buf;')
self.write()
@@ -971,8 +1027,9 @@ class GenXproto(FileWriter):
self.copy_container(request, 'request')
self.write('Align(&buf, 4);')
self.write()
- self.write('return x11::SendRequest<%s>(connection_, &buf);' %
- reply_name)
+ reply_has_fds = reply and any(field.isfd for field in reply.fields)
+ self.write('return x11::SendRequest<%s>(connection_, &buf, %s);' %
+ (reply_name, 'true' if reply_has_fds else 'false'))
self.write()
if not reply:
@@ -980,10 +1037,10 @@ class GenXproto(FileWriter):
self.write('template<> COMPONENT_EXPORT(X11)')
self.write('std::unique_ptr<%s>' % reply_name)
- sig = 'detail::ReadReply<%s>(const uint8_t* buffer) {' % reply_name
+ sig = 'detail::ReadReply<%s>(ReadBuffer* buffer) {' % reply_name
with Indent(self, sig, '}'):
self.namespace = ['x11']
- self.write('ReadBuffer buf{buffer, 0UL};')
+ self.write('auto& buf = *buffer;')
self.write('auto reply = std::make_unique<%s>();' % reply_name)
self.write()
self.is_read = True
@@ -1000,12 +1057,16 @@ class GenXproto(FileWriter):
name = self.qualtype(event, name)
self.write('template <> COMPONENT_EXPORT(X11)')
self.write('void ReadEvent<%s>(' % name)
- with Indent(self, ' %s* event_, const uint8_t* buffer) {' % name,
- '}'):
- self.write('ReadBuffer buf{buffer, 0UL};')
+ with Indent(self, ' %s* event_, ReadBuffer* buffer) {' % name, '}'):
+ self.write('auto& buf = *buffer;')
self.write()
self.is_read = True
self.copy_container(event, '(*event_)')
+ if event.is_ge_event:
+ self.write('Align(&buf, 4);')
+ self.write('DCHECK_EQ(buf.offset, 32 + 4 * length);')
+ else:
+ self.write('DCHECK_LE(buf.offset, 32ul);')
self.write()
def define_type(self, item, name):
@@ -1082,6 +1143,12 @@ class GenXproto(FileWriter):
if field.field_name == 'sequence':
field.visible = True
field.parent = (t, name)
+
+ if field.type.is_list:
+ # xcb uses void* in some places to represent arbitrary data.
+ field.type.is_ref_counted_memory = (
+ not field.type.nmemb and field.field_type[0] == 'void')
+
# |for_list| and |for_switch| may have already been set when
# processing other fields in this structure.
field.for_list = getattr(field, 'for_list', None)
@@ -1246,7 +1313,10 @@ class GenXproto(FileWriter):
self.write('#include <vector>')
self.write()
self.write('#include "base/component_export.h"')
+ self.write('#include "base/memory/ref_counted_memory.h"')
+ self.write('#include "base/memory/scoped_refptr.h"')
self.write('#include "base/optional.h"')
+ self.write('#include "base/files/scoped_file.h"')
self.write('#include "ui/gfx/x/xproto_types.h"')
imports = set(self.module.direct_imports)
if self.module.namespace.is_ext:
@@ -1332,6 +1402,7 @@ class GenXproto(FileWriter):
self.write('#include <xcb/xcbext.h>')
self.write()
self.write('#include "base/logging.h"')
+ self.write('#include "base/posix/eintr_wrapper.h"')
self.write('#include "ui/gfx/x/xproto_internal.h"')
self.write()
self.write('namespace x11 {')
@@ -1501,12 +1572,13 @@ class GenReadEvent(FileWriter):
with Indent(self, 'event->deleter_ = [](void* event) {', '};'):
self.write('delete reinterpret_cast<%s*>(event);' % typename)
self.write('auto* event_ = new %s;' % typename)
- self.write('ReadEvent(event_, buf);')
+ self.write('ReadEvent(event_, buffer);')
if len(event.opcodes) > 1:
self.write('{0} = static_cast<decltype({0})>({1});'.format(
'event_->opcode', opcode))
self.write('event_->send_event = send_event;')
self.write('event->event_ = event_;')
+ self.write('event->window_ = event_->GetWindow();')
self.write('return;')
self.write()
@@ -1521,8 +1593,9 @@ class GenReadEvent(FileWriter):
self.write('namespace x11 {')
self.write()
self.write('void ReadEvent(')
- args = 'Event* event, Connection* conn, const uint8_t* buf'
+ args = 'Event* event, Connection* conn, ReadBuffer* buffer'
with Indent(self, ' %s) {' % args, '}'):
+ self.write('auto* buf = buffer->data->data();')
cast = 'auto* %s = reinterpret_cast<const %s*>(buf);'
self.write(cast % ('ev', 'xcb_generic_event_t'))
self.write(cast % ('ge', 'xcb_ge_generic_event_t'))
diff --git a/chromium/ui/gfx/x/x11.h b/chromium/ui/gfx/x/x11.h
index 2cae7c5b3ee..c1ee10ab986 100644
--- a/chromium/ui/gfx/x/x11.h
+++ b/chromium/ui/gfx/x/x11.h
@@ -19,20 +19,13 @@ extern "C" {
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xatom.h>
-#include <X11/Xcursor/Xcursor.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xregion.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
-#include <X11/extensions/XI2.h>
-#include <X11/extensions/XInput.h>
-#include <X11/extensions/XInput2.h>
-#include <X11/extensions/XIproto.h>
#include <X11/extensions/XShm.h>
-#include <X11/extensions/XTest.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/Xrender.h>
-#include <X11/extensions/record.h>
#include <X11/extensions/sync.h>
// Define XK_xxx before the #include of <X11/keysym.h> so that <X11/keysym.h>
@@ -91,12 +84,6 @@ extern "C" {
#undef RootWindow // Defined by X11/Xlib.h
#undef DestroyAll // Defined by X11/X.h to 0
#undef Always // Defined by X11/X.h to 2
-#undef AddToList // Defined by X11/extensions/XI.h to 0
-#undef COUNT // Defined by X11/extensions/XI.h to 0
-#undef CREATE // Defined by X11/extensions/XI.h to 1
-#undef DeviceAdded // Defined by X11/extensions/XI.h to 0
-#undef DeviceMode // Defined by X11/extensions/XI.h to 1
-#undef DeviceRemoved // Defined by X11/extensions/XI.h to 1
#undef FocusIn // Defined by X.h to 9
#undef FocusOut // Defined by X.h to 10
#undef None // Defined by X11/X.h to 0L
@@ -113,8 +100,6 @@ static constexpr long CurrentTime = 0L;
static constexpr int False = 0;
static constexpr int True = 1;
static constexpr int Success = 0;
-static constexpr int FocusIn = 9;
-static constexpr int FocusOut = 10;
typedef int Bool;
typedef int Status;
} // namespace x11
diff --git a/chromium/ui/gfx/x/x11_types.cc b/chromium/ui/gfx/x/x11_types.cc
index ad41a13dff3..756a1f59dc0 100644
--- a/chromium/ui/gfx/x/x11_types.cc
+++ b/chromium/ui/gfx/x/x11_types.cc
@@ -23,147 +23,5 @@ XDisplay* CloneXDisplay(XDisplay* display) {
return XOpenDisplay(DisplayString(display));
}
-void PutARGBImage(XDisplay* display,
- void* visual,
- int depth,
- XID pixmap,
- void* pixmap_gc,
- const uint8_t* data,
- int width,
- int height) {
- PutARGBImage(display,
- visual, depth,
- pixmap, pixmap_gc,
- data, width, height,
- 0, 0, // src_x, src_y
- 0, 0, // dst_x, dst_y
- width, height);
-}
-
-int BitsPerPixelForPixmapDepth(XDisplay* dpy, int depth) {
- int count;
- XScopedPtr<XPixmapFormatValues[]> formats(XListPixmapFormats(dpy, &count));
- if (!formats)
- return -1;
-
- for (int i = 0; i < count; ++i) {
- if (formats[i].depth == depth)
- return formats[i].bits_per_pixel;
- }
-
- return -1;
-}
-
-void PutARGBImage(XDisplay* display,
- void* visual,
- int depth,
- XID pixmap,
- void* pixmap_gc,
- const uint8_t* data,
- int data_width,
- int data_height,
- int src_x,
- int src_y,
- int dst_x,
- int dst_y,
- int copy_width,
- int copy_height) {
- // TODO(scherkus): potential performance impact... consider passing in as a
- // parameter.
- int pixmap_bpp = BitsPerPixelForPixmapDepth(display, depth);
-
- XImage image;
- memset(&image, 0, sizeof(image));
-
- image.width = data_width;
- image.height = data_height;
- image.format = static_cast<int>(x11::ImageFormat::ZPixmap);
- image.byte_order = static_cast<int>(x11::ImageOrder::LSBFirst);
- image.bitmap_unit = 8;
- image.bitmap_bit_order = static_cast<int>(x11::ImageOrder::LSBFirst);
- image.depth = depth;
- image.bits_per_pixel = pixmap_bpp;
- image.bytes_per_line = data_width * pixmap_bpp / 8;
-
- if (pixmap_bpp == 32) {
- image.red_mask = 0xff0000;
- image.green_mask = 0xff00;
- image.blue_mask = 0xff;
-
- // If the X server depth is already 32-bits and the color masks match,
- // then our job is easy.
- Visual* vis = static_cast<Visual*>(visual);
- if (image.red_mask == vis->red_mask &&
- image.green_mask == vis->green_mask &&
- image.blue_mask == vis->blue_mask) {
- image.data = const_cast<char*>(reinterpret_cast<const char*>(data));
- XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
- src_x, src_y, dst_x, dst_y,
- copy_width, copy_height);
- } else {
- // Otherwise, we need to shuffle the colors around. Assume red and blue
- // need to be swapped.
- //
- // It's possible to use some fancy SSE tricks here, but since this is the
- // slow path anyway, we do it slowly.
-
- uint8_t* bitmap32 =
- static_cast<uint8_t*>(malloc(4 * data_width * data_height));
- if (!bitmap32)
- return;
- uint8_t* const orig_bitmap32 = bitmap32;
- const uint32_t* bitmap_in = reinterpret_cast<const uint32_t*>(data);
- for (int y = 0; y < data_height; ++y) {
- for (int x = 0; x < data_width; ++x) {
- const uint32_t pixel = *(bitmap_in++);
- bitmap32[0] = (pixel >> 16) & 0xff; // Red
- bitmap32[1] = (pixel >> 8) & 0xff; // Green
- bitmap32[2] = pixel & 0xff; // Blue
- bitmap32[3] = (pixel >> 24) & 0xff; // Alpha
- bitmap32 += 4;
- }
- }
- image.data = reinterpret_cast<char*>(orig_bitmap32);
- XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
- src_x, src_y, dst_x, dst_y,
- copy_width, copy_height);
- free(orig_bitmap32);
- }
- } else if (pixmap_bpp == 16) {
- // Some folks have VNC setups which still use 16-bit visuals and VNC
- // doesn't include Xrender.
-
- uint16_t* bitmap16 =
- static_cast<uint16_t*>(malloc(2 * data_width * data_height));
- if (!bitmap16)
- return;
- uint16_t* const orig_bitmap16 = bitmap16;
- const uint32_t* bitmap_in = reinterpret_cast<const uint32_t*>(data);
- for (int y = 0; y < data_height; ++y) {
- for (int x = 0; x < data_width; ++x) {
- const uint32_t pixel = *(bitmap_in++);
- uint16_t out_pixel = ((pixel >> 8) & 0b1111100000000000) |
- ((pixel >> 5) & 0b0000011111100000) |
- ((pixel >> 3) & 0b0000000000011111);
- *(bitmap16++) = out_pixel;
- }
- }
-
- image.data = reinterpret_cast<char*>(orig_bitmap16);
- image.red_mask = 0b1111100000000000;
- image.green_mask = 0b0000011111100000;
- image.blue_mask = 0b0000000000011111;
-
- XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
- src_x, src_y, dst_x, dst_y,
- copy_width, copy_height);
- free(orig_bitmap16);
- } else {
- LOG(FATAL) << "Sorry, we don't support your visual depth without "
- "Xrender support (depth:" << depth
- << " bpp:" << pixmap_bpp << ")";
- }
-}
-
} // namespace gfx
diff --git a/chromium/ui/gfx/x/x11_types.h b/chromium/ui/gfx/x/x11_types.h
index a1886764342..9bede0e7cdf 100644
--- a/chromium/ui/gfx/x/x11_types.h
+++ b/chromium/ui/gfx/x/x11_types.h
@@ -13,7 +13,6 @@
#include "ui/gfx/x/connection.h"
typedef unsigned long VisualID;
-typedef struct _XcursorImage XcursorImage;
typedef union _XEvent XEvent;
typedef struct _XImage XImage;
typedef struct _XGC* GC;
@@ -44,41 +43,6 @@ GFX_EXPORT XDisplay* GetXDisplay();
// same X server. It's the caller's responsibility to call XCloseDisplay().
GFX_EXPORT XDisplay* CloneXDisplay(XDisplay* display);
-// Return the number of bits-per-pixel for a pixmap of the given depth
-GFX_EXPORT int BitsPerPixelForPixmapDepth(XDisplay* display, int depth);
-
-// Draws ARGB data on the given pixmap using the given GC, converting to the
-// server side visual depth as needed. Destination is assumed to be the same
-// dimensions as |data| or larger. |data| is also assumed to be in row order
-// with each line being exactly |width| * 4 bytes long.
-GFX_EXPORT void PutARGBImage(XDisplay* display,
- void* visual,
- int depth,
- XID pixmap,
- void* pixmap_gc,
- const uint8_t* data,
- int width,
- int height);
-
-// Same as above only more general:
-// - |data_width| and |data_height| refer to the data image
-// - |src_x|, |src_y|, |copy_width| and |copy_height| define source region
-// - |dst_x|, |dst_y|, |copy_width| and |copy_height| define destination region
-GFX_EXPORT void PutARGBImage(XDisplay* display,
- void* visual,
- int depth,
- XID pixmap,
- void* pixmap_gc,
- const uint8_t* data,
- int data_width,
- int data_height,
- int src_x,
- int src_y,
- int dst_x,
- int dst_y,
- int copy_width,
- int copy_height);
-
} // namespace gfx
#endif // UI_GFX_X_X11_UTIL_H_
diff --git a/chromium/ui/gfx/x/xproto_internal.cc b/chromium/ui/gfx/x/xproto_internal.cc
new file mode 100644
index 00000000000..2dbdd73e81b
--- /dev/null
+++ b/chromium/ui/gfx/x/xproto_internal.cc
@@ -0,0 +1,158 @@
+// 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 "ui/gfx/x/xproto_internal.h"
+
+// XCB used to send requests with FDs by sending each FD individually with
+// xcb_send_fd(), then the request with xcb_send_request(). However, there's a
+// race condition -- FDs can get mixed up if multiple threads are sending them
+// at the same time. xcb_send_request_with_fds() was introduced to atomically
+// handle this case, however it's only available on newer systems. In
+// particular it's not available on Ubuntu Xenial (which has LTS until April
+// 2024). We want to use the bug-free version when it's available, and fallback
+// to the buggy version otherwise.
+
+// Declare the function in case this is a packager build on an older distro with
+// use_sysroot=false.
+unsigned int xcb_send_request_with_fds(xcb_connection_t* c,
+ int flags,
+ struct iovec* vector,
+ const xcb_protocol_request_t* request,
+ unsigned int num_fds,
+ int* fds);
+
+// Add the weak attribute to the symbol. This prevents the dynamic linker from
+// erroring out. Instead, if the function is not found, then it's address is
+// nullptr, so we can do a runtime check to test availability.
+extern "C" __attribute__((weak)) decltype(
+ xcb_send_request_with_fds) xcb_send_request_with_fds;
+
+namespace x11 {
+
+MallocedRefCountedMemory::MallocedRefCountedMemory(void* data)
+ : data_(reinterpret_cast<uint8_t*>(data)) {}
+
+const uint8_t* MallocedRefCountedMemory::front() const {
+ return data_;
+}
+
+size_t MallocedRefCountedMemory::size() const {
+ // There's no easy way to tell how large malloc'ed data is.
+ NOTREACHED();
+ return 0;
+}
+
+MallocedRefCountedMemory::~MallocedRefCountedMemory() {
+ free(data_);
+}
+
+OffsetRefCountedMemory::OffsetRefCountedMemory(
+ scoped_refptr<base::RefCountedMemory> memory,
+ size_t offset,
+ size_t size)
+ : memory_(memory), offset_(offset), size_(size) {}
+
+const uint8_t* OffsetRefCountedMemory::front() const {
+ return memory_->front() + offset_;
+}
+
+size_t OffsetRefCountedMemory::size() const {
+ return size_;
+}
+
+OffsetRefCountedMemory::~OffsetRefCountedMemory() = default;
+
+UnretainedRefCountedMemory::UnretainedRefCountedMemory(const void* data)
+ : data_(reinterpret_cast<const uint8_t*>(data)) {}
+
+const uint8_t* UnretainedRefCountedMemory::front() const {
+ return data_;
+}
+
+size_t UnretainedRefCountedMemory::size() const {
+ // There's no easy way to tell how large malloc'ed data is.
+ NOTREACHED();
+ return 0;
+}
+
+UnretainedRefCountedMemory::~UnretainedRefCountedMemory() = default;
+
+base::Optional<unsigned int> SendRequestImpl(x11::Connection* connection,
+ WriteBuffer* buf,
+ bool is_void,
+ bool reply_has_fds) {
+ xcb_protocol_request_t xpr{
+ .ext = nullptr,
+ .isvoid = is_void,
+ };
+
+ struct RequestHeader {
+ uint8_t major_opcode;
+ uint8_t minor_opcode;
+ uint16_t length;
+ };
+
+ struct ExtendedRequestHeader {
+ RequestHeader header;
+ uint32_t long_length;
+ };
+ static_assert(sizeof(ExtendedRequestHeader) == 8, "");
+
+ auto& first_buffer = buf->GetBuffers()[0];
+ DCHECK_GE(first_buffer->size(), sizeof(RequestHeader));
+ auto* old_header = reinterpret_cast<RequestHeader*>(
+ const_cast<uint8_t*>(first_buffer->data()));
+ ExtendedRequestHeader new_header{*old_header, 0};
+
+ // Requests are always a multiple of 4 bytes on the wire. Because of this,
+ // the length field represents the size in chunks of 4 bytes.
+ DCHECK_EQ(buf->offset() % 4, 0UL);
+ size_t size32 = buf->offset() / 4;
+
+ // XCB requires 2 iovecs for its own internal usage.
+ std::vector<struct iovec> io{{nullptr, 0}, {nullptr, 0}};
+ if (size32 < connection->setup().maximum_request_length) {
+ // Regular request
+ old_header->length = size32;
+ } else if (size32 < connection->extended_max_request_length()) {
+ // BigRequests extension request
+ DCHECK_EQ(new_header.header.length, 0U);
+ new_header.long_length = size32 + 1;
+
+ io.push_back({&new_header, sizeof(ExtendedRequestHeader)});
+ first_buffer = base::MakeRefCounted<OffsetRefCountedMemory>(
+ first_buffer, sizeof(RequestHeader),
+ first_buffer->size() - sizeof(RequestHeader));
+ } else {
+ LOG(ERROR) << "Cannot send request of length " << buf->offset();
+ return base::nullopt;
+ }
+
+ for (auto& buffer : buf->GetBuffers())
+ io.push_back({const_cast<uint8_t*>(buffer->data()), buffer->size()});
+ xpr.count = io.size() - 2;
+
+ xcb_connection_t* conn = connection->XcbConnection();
+ auto flags = XCB_REQUEST_CHECKED | XCB_REQUEST_RAW;
+ if (reply_has_fds)
+ flags |= XCB_REQUEST_REPLY_FDS;
+ base::Optional<unsigned int> sequence;
+ if (xcb_send_request_with_fds) {
+ // Atomically send the request with its FDs if we can.
+ sequence = xcb_send_request_with_fds(conn, flags, &io[2], &xpr,
+ buf->fds().size(), buf->fds().data());
+ } else {
+ // Otherwise manually lock and send the fds, then the request.
+ XLockDisplay(connection->display());
+ for (int fd : buf->fds())
+ xcb_send_fd(conn, fd);
+ sequence = xcb_send_request(conn, flags, &io[2], &xpr);
+ XUnlockDisplay(connection->display());
+ }
+ if (xcb_connection_has_error(conn))
+ return base::nullopt;
+ return sequence;
+}
+
+} // namespace x11
diff --git a/chromium/ui/gfx/x/xproto_internal.h b/chromium/ui/gfx/x/xproto_internal.h
index 32c68b4f801..f278ea8d9a5 100644
--- a/chromium/ui/gfx/x/xproto_internal.h
+++ b/chromium/ui/gfx/x/xproto_internal.h
@@ -6,7 +6,7 @@
#define UI_GFX_X_XPROTO_INTERNAL_H_
#ifndef IS_X11_IMPL
-#error "This file should only be included by generated xprotos"
+#error "This file should only be included by //ui/gfx/x:xprotos"
#endif
#include <X11/Xlib-xcb.h>
@@ -21,6 +21,7 @@
#include "base/component_export.h"
#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/optional.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/xproto_types.h"
@@ -30,8 +31,6 @@ namespace x11 {
template <class Reply>
class Future;
-using WriteBuffer = std::vector<uint8_t>;
-
template <typename T, typename Enable = void>
struct EnumBase {
using type = T;
@@ -45,37 +44,84 @@ struct EnumBase<T, typename std::enable_if_t<std::is_enum<T>::value>> {
template <typename T>
using EnumBaseType = typename EnumBase<T>::type;
-struct ReadBuffer {
- const uint8_t* data = nullptr;
- size_t offset = 0;
+// Calls free() on the underlying data when the count drops to 0.
+class COMPONENT_EXPORT(X11) MallocedRefCountedMemory
+ : public base::RefCountedMemory {
+ public:
+ explicit MallocedRefCountedMemory(void* data);
+
+ MallocedRefCountedMemory(const MallocedRefCountedMemory&) = delete;
+ MallocedRefCountedMemory& operator=(const MallocedRefCountedMemory&) = delete;
+
+ const uint8_t* front() const override;
+
+ size_t size() const override;
+
+ private:
+ ~MallocedRefCountedMemory() override;
+
+ uint8_t* const data_;
};
-template <typename T>
-void VerifyAlignment(T* t, size_t offset) {
- // On the wire, X11 types are always aligned to their size. This is a sanity
- // check to ensure padding etc are working properly.
- if (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)
- DCHECK_EQ(offset % sizeof(*t), 0UL);
-}
+// Wraps another RefCountedMemory, giving a view into it. Similar to
+// base::StringPiece, the data is some contiguous subarray, but unlike
+// StringPiece, a counted reference is kept on the underlying memory.
+class COMPONENT_EXPORT(X11) OffsetRefCountedMemory
+ : public base::RefCountedMemory {
+ public:
+ OffsetRefCountedMemory(scoped_refptr<base::RefCountedMemory> memory,
+ size_t offset,
+ size_t size);
-template <typename T>
-void Write(const T* t, WriteBuffer* buf) {
- static_assert(std::is_trivially_copyable<T>::value, "");
- VerifyAlignment(t, buf->size());
- const uint8_t* start = reinterpret_cast<const uint8_t*>(t);
- std::copy(start, start + sizeof(*t), std::back_inserter(*buf));
-}
+ OffsetRefCountedMemory(const OffsetRefCountedMemory&) = delete;
+ OffsetRefCountedMemory& operator=(const OffsetRefCountedMemory&) = delete;
+
+ const uint8_t* front() const override;
+
+ size_t size() const override;
+
+ private:
+ ~OffsetRefCountedMemory() override;
+
+ scoped_refptr<base::RefCountedMemory> memory_;
+ size_t offset_;
+ size_t size_;
+};
+
+// Wraps a bare pointer and does not take any action when the reference count
+// reaches 0. This is used to wrap stack-alloctaed or persistent data so we can
+// pass those to Read/ReadEvent/ReadReply which expect RefCountedMemory.
+class COMPONENT_EXPORT(X11) UnretainedRefCountedMemory
+ : public base::RefCountedMemory {
+ public:
+ explicit UnretainedRefCountedMemory(const void* data);
+
+ UnretainedRefCountedMemory(const UnretainedRefCountedMemory&) = delete;
+ UnretainedRefCountedMemory& operator=(const UnretainedRefCountedMemory&) =
+ delete;
+
+ const uint8_t* front() const override;
+
+ size_t size() const override;
+
+ private:
+ ~UnretainedRefCountedMemory() override;
+
+ const uint8_t* const data_;
+};
template <typename T>
void Read(T* t, ReadBuffer* buf) {
static_assert(std::is_trivially_copyable<T>::value, "");
- VerifyAlignment(t, buf->offset);
- memcpy(t, buf->data + buf->offset, sizeof(*t));
+ detail::VerifyAlignment(t, buf->offset);
+ memcpy(t, buf->data->data() + buf->offset, sizeof(*t));
buf->offset += sizeof(*t);
}
inline void Pad(WriteBuffer* buf, size_t amount) {
- buf->resize(buf->size() + amount, '\0');
+ uint8_t zero = 0;
+ for (size_t i = 0; i < amount; i++)
+ buf->Write(&zero);
}
inline void Pad(ReadBuffer* buf, size_t amount) {
@@ -83,69 +129,25 @@ inline void Pad(ReadBuffer* buf, size_t amount) {
}
inline void Align(WriteBuffer* buf, size_t align) {
- Pad(buf, (align - (buf->size() % align)) % align);
+ Pad(buf, (align - (buf->offset() % align)) % align);
}
inline void Align(ReadBuffer* buf, size_t align) {
Pad(buf, (align - (buf->offset % align)) % align);
}
-template <typename Reply>
-Future<Reply> SendRequest(x11::Connection* connection, WriteBuffer* buf) {
- // Clang crashes when the value of |is_void| is inlined below,
- // so keep this variable outside of |xpr|.
- constexpr bool is_void = std::is_void<Reply>::value;
- xcb_protocol_request_t xpr{
- .ext = nullptr,
- .isvoid = is_void,
- };
-
- struct RequestHeader {
- uint8_t major_opcode;
- uint8_t minor_opcode;
- uint16_t length;
- };
-
- struct ExtendedRequestHeader {
- RequestHeader header;
- uint32_t long_length;
- };
- static_assert(sizeof(ExtendedRequestHeader) == 8, "");
-
- auto* old_header = reinterpret_cast<RequestHeader*>(buf->data());
- ExtendedRequestHeader new_header{*old_header, 0};
-
- // Requests are always a multiple of 4 bytes on the wire. Because of this,
- // the length field represents the size in chunks of 4 bytes.
- DCHECK_EQ(buf->size() % 4, 0UL);
- size_t size32 = buf->size() / 4;
-
- struct iovec io[4];
- memset(&io, 0, sizeof(io));
- if (size32 < connection->setup().maximum_request_length) {
- xpr.count = 1;
- old_header->length = size32;
- io[2].iov_base = buf->data();
- io[2].iov_len = buf->size();
- } else if (size32 < connection->extended_max_request_length()) {
- xpr.count = 2;
- DCHECK_EQ(new_header.header.length, 0U);
- new_header.long_length = size32 + 1;
- io[2].iov_base = &new_header;
- io[2].iov_len = sizeof(ExtendedRequestHeader);
- io[3].iov_base = buf->data() + sizeof(RequestHeader);
- io[3].iov_len = buf->size() - sizeof(RequestHeader);
- } else {
- LOG(ERROR) << "Cannot send request of length " << buf->size();
- return {nullptr, base::nullopt};
- }
+base::Optional<unsigned int> SendRequestImpl(x11::Connection* connection,
+ WriteBuffer* buf,
+ bool is_void,
+ bool reply_has_fds);
- xcb_connection_t* conn = connection->XcbConnection();
- auto flags = XCB_REQUEST_CHECKED | XCB_REQUEST_RAW;
- auto sequence = xcb_send_request(conn, flags, &io[2], &xpr);
- if (xcb_connection_has_error(conn))
- return {nullptr, base::nullopt};
- return {connection, sequence};
+template <typename Reply>
+Future<Reply> SendRequest(x11::Connection* connection,
+ WriteBuffer* buf,
+ bool reply_has_fds) {
+ auto sequence = SendRequestImpl(connection, buf, std::is_void<Reply>::value,
+ reply_has_fds);
+ return {sequence ? connection : nullptr, sequence};
}
// Helper function for xcbproto popcount. Given an integral type, returns the
diff --git a/chromium/ui/gfx/x/xproto_types.cc b/chromium/ui/gfx/x/xproto_types.cc
index 0fc634cecf3..296862e8965 100644
--- a/chromium/ui/gfx/x/xproto_types.cc
+++ b/chromium/ui/gfx/x/xproto_types.cc
@@ -4,10 +4,78 @@
#include "ui/gfx/x/xproto_types.h"
+#include "base/memory/scoped_refptr.h"
#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto_internal.h"
namespace x11 {
+namespace {
+
+constexpr uint8_t kResponseTypeReply = 1;
+
+struct ReplyHeader {
+ uint8_t response_type;
+ uint8_t pad;
+ uint16_t sequence;
+ uint32_t length;
+};
+
+} // namespace
+
+ReadBuffer::ReadBuffer(scoped_refptr<base::RefCountedMemory> data)
+ : data(data) {
+ const auto* reply_header = reinterpret_cast<const ReplyHeader*>(data->data());
+
+ // Only replies can have FDs, not events or errors.
+ if (reply_header->response_type == kResponseTypeReply) {
+ // All replies are at least 32 bytes. The length field specifies the
+ // amount of extra data in 4-byte multiples after the fixed 32 bytes.
+ size_t reply_length = 32 + 4 * reply_header->length;
+
+ // libxcb stores the fds after the reply data.
+ fds = reinterpret_cast<const int*>(data->data() + reply_length);
+ }
+}
+
+ReadBuffer::ReadBuffer(ReadBuffer&&) = default;
+
+ReadBuffer::~ReadBuffer() = default;
+
+scoped_refptr<base::RefCountedMemory> ReadBuffer::ReadAndAdvance(
+ size_t length) {
+ auto buf = base::MakeRefCounted<OffsetRefCountedMemory>(data, offset, length);
+ offset += length;
+ return buf;
+}
+
+int ReadBuffer::TakeFd() {
+ return *fds++;
+}
+
+WriteBuffer::WriteBuffer() = default;
+
+WriteBuffer::WriteBuffer(WriteBuffer&&) = default;
+
+WriteBuffer::~WriteBuffer() = default;
+
+void WriteBuffer::AppendBuffer(scoped_refptr<base::RefCountedMemory> buffer,
+ size_t size) {
+ AppendCurrentBuffer();
+ buffers_.push_back(buffer);
+ offset_ += size;
+}
+
+std::vector<scoped_refptr<base::RefCountedMemory>>& WriteBuffer::GetBuffers() {
+ if (!current_buffer_.empty())
+ AppendCurrentBuffer();
+ return buffers_;
+}
+
+void WriteBuffer::AppendCurrentBuffer() {
+ buffers_.push_back(base::RefCountedBytes::TakeVector(&current_buffer_));
+}
+
FutureBase::FutureBase(Connection* connection,
base::Optional<unsigned int> sequence)
: connection_(connection), sequence_(sequence) {}
@@ -51,11 +119,14 @@ FutureBase& FutureBase::operator=(FutureBase&& future) {
return *this;
}
-void FutureBase::SyncImpl(Error** raw_error, uint8_t** raw_reply) {
+void FutureBase::SyncImpl(Error** raw_error,
+ scoped_refptr<base::RefCountedMemory>* raw_reply) {
if (!sequence_)
return;
- *raw_reply = reinterpret_cast<uint8_t*>(
+ auto* reply = reinterpret_cast<uint8_t*>(
xcb_wait_for_reply(connection_->XcbConnection(), *sequence_, raw_error));
+ if (reply)
+ *raw_reply = base::MakeRefCounted<MallocedRefCountedMemory>(reply);
sequence_ = base::nullopt;
}
diff --git a/chromium/ui/gfx/x/xproto_types.h b/chromium/ui/gfx/x/xproto_types.h
index 415c2146481..9e8e04d3429 100644
--- a/chromium/ui/gfx/x/xproto_types.h
+++ b/chromium/ui/gfx/x/xproto_types.h
@@ -5,7 +5,6 @@
#ifndef UI_GFX_X_XPROTO_TYPES_H_
#define UI_GFX_X_XPROTO_TYPES_H_
-#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include <xcb/xcbext.h>
@@ -14,7 +13,10 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/files/scoped_file.h"
#include "base/memory/free_deleter.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "ui/gfx/x/xproto_util.h"
@@ -28,8 +30,74 @@ constexpr uint8_t kSendEventMask = 0x80;
namespace detail {
+template <typename T>
+void VerifyAlignment(T* t, size_t offset) {
+ // On the wire, X11 types are always aligned to their size. This is a sanity
+ // check to ensure padding etc are working properly.
+ if (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)
+ DCHECK_EQ(offset % sizeof(*t), 0UL);
+}
+
+} // namespace detail
+
+// Wraps data read from the connection.
+struct COMPONENT_EXPORT(X11) ReadBuffer {
+ explicit ReadBuffer(scoped_refptr<base::RefCountedMemory> data);
+
+ ReadBuffer(const ReadBuffer&) = delete;
+ ReadBuffer(ReadBuffer&&);
+
+ ~ReadBuffer();
+
+ scoped_refptr<base::RefCountedMemory> ReadAndAdvance(size_t length);
+
+ int TakeFd();
+
+ scoped_refptr<base::RefCountedMemory> data;
+ size_t offset = 0;
+ const int* fds = nullptr;
+};
+
+// Wraps data to write to the connection.
+class COMPONENT_EXPORT(X11) WriteBuffer {
+ public:
+ WriteBuffer();
+
+ WriteBuffer(const WriteBuffer&) = delete;
+ WriteBuffer(WriteBuffer&&);
+
+ ~WriteBuffer();
+
+ void AppendBuffer(scoped_refptr<base::RefCountedMemory> buffer, size_t size);
+
+ std::vector<scoped_refptr<base::RefCountedMemory>>& GetBuffers();
+
+ size_t offset() const { return offset_; }
+
+ std::vector<int>& fds() { return fds_; }
+
+ template <typename T>
+ void Write(const T* t) {
+ static_assert(std::is_trivially_copyable<T>::value, "");
+ detail::VerifyAlignment(t, offset_);
+ const uint8_t* start = reinterpret_cast<const uint8_t*>(t);
+ std::copy(start, start + sizeof(*t), std::back_inserter(current_buffer_));
+ offset_ += sizeof(*t);
+ }
+
+ private:
+ void AppendCurrentBuffer();
+
+ std::vector<scoped_refptr<base::RefCountedMemory>> buffers_;
+ std::vector<uint8_t> current_buffer_;
+ size_t offset_ = 0;
+ std::vector<int> fds_;
+};
+
+namespace detail {
+
template <typename Reply>
-std::unique_ptr<Reply> ReadReply(const uint8_t* buffer);
+std::unique_ptr<Reply> ReadReply(ReadBuffer* buffer);
} // namespace detail
@@ -39,18 +107,19 @@ template <class Reply>
class Future;
template <typename T>
-T Read(const uint8_t* buf);
+T Read(ReadBuffer* buf);
template <typename T>
-std::vector<uint8_t> Write(const T& t);
+WriteBuffer Write(const T& t);
template <typename T>
-void ReadEvent(T* event, const uint8_t* buf);
+void ReadEvent(T* event, ReadBuffer* buf);
template <typename Reply>
struct Response {
operator bool() const { return reply.get(); }
const Reply* operator->() const { return reply.get(); }
+ Reply* operator->() { return reply.get(); }
std::unique_ptr<Reply> reply;
std::unique_ptr<Error, base::FreeDeleter> error;
@@ -76,7 +145,7 @@ struct Response<void> {
class COMPONENT_EXPORT(X11) FutureBase {
public:
- using RawReply = std::unique_ptr<uint8_t, base::FreeDeleter>;
+ using RawReply = scoped_refptr<base::RefCountedMemory>;
using RawError = std::unique_ptr<xcb_generic_error_t, base::FreeDeleter>;
using ResponseCallback =
base::OnceCallback<void(RawReply reply, RawError error)>;
@@ -91,7 +160,8 @@ class COMPONENT_EXPORT(X11) FutureBase {
FutureBase(FutureBase&& future);
FutureBase& operator=(FutureBase&& future);
- void SyncImpl(Error** raw_error, uint8_t** raw_reply);
+ void SyncImpl(Error** raw_error,
+ scoped_refptr<base::RefCountedMemory>* raw_reply);
void SyncImpl(Error** raw_error);
void OnResponseImpl(ResponseCallback callback);
@@ -114,13 +184,13 @@ class Future : public FutureBase {
// Blocks until we receive the response from the server. Returns the response.
Response<Reply> Sync() {
Error* raw_error = nullptr;
- uint8_t* raw_reply = nullptr;
+ scoped_refptr<base::RefCountedMemory> raw_reply;
SyncImpl(&raw_error, &raw_reply);
std::unique_ptr<Reply> reply;
if (raw_reply) {
- reply = detail::ReadReply<Reply>(raw_reply);
- free(raw_reply);
+ auto buf = ReadBuffer(raw_reply);
+ reply = detail::ReadReply<Reply>(&buf);
}
std::unique_ptr<Error, base::FreeDeleter> error;
@@ -137,8 +207,9 @@ class Future : public FutureBase {
// |callback| must be bound as the first argument of the intermediate
// function.
auto wrapper = [](Callback callback, RawReply raw_reply, RawError error) {
+ ReadBuffer buf(raw_reply);
std::unique_ptr<Reply> reply =
- raw_reply ? detail::ReadReply<Reply>(raw_reply.get()) : nullptr;
+ raw_reply ? detail::ReadReply<Reply>(&buf) : nullptr;
std::move(callback).Run({std::move(reply), std::move(error)});
};
OnResponseImpl(base::BindOnce(wrapper, std::move(callback)));
@@ -150,7 +221,7 @@ class Future : public FutureBase {
private:
template <typename R>
- friend Future<R> SendRequest(Connection*, std::vector<uint8_t>*);
+ friend Future<R> SendRequest(Connection*, WriteBuffer*, bool);
Future(Connection* connection, base::Optional<unsigned int> sequence)
: FutureBase(connection, sequence) {}
diff --git a/chromium/ui/gl/BUILD.gn b/chromium/ui/gl/BUILD.gn
index fb084016885..bc37b3abdb9 100644
--- a/chromium/ui/gl/BUILD.gn
+++ b/chromium/ui/gl/BUILD.gn
@@ -5,7 +5,6 @@
import("//build/buildflag_header.gni")
import("//build/config/chrome_build.gni")
import("//build/config/chromecast_build.gni")
-import("//build/config/jumbo.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -53,7 +52,7 @@ config("gl_config") {
}
}
-jumbo_component("gl") {
+component("gl") {
output_name = "gl_wrapper" # Avoid colliding with OS X"s libGL.dylib.
sources = [
@@ -214,7 +213,7 @@ jumbo_component("gl") {
"shared_gl_fence_egl.h",
]
- if (is_linux || use_ozone) {
+ if (is_linux || is_chromeos || use_ozone) {
sources += [
"gl_image_native_pixmap.cc",
"gl_image_native_pixmap.h",
@@ -253,6 +252,8 @@ jumbo_component("gl") {
if (use_x11 || ozone_platform_x11) {
sources += [
+ "gl_image_egl_pixmap.cc",
+ "gl_image_egl_pixmap.h",
"gl_surface_egl_x11.cc",
"gl_surface_egl_x11.h",
"gl_surface_egl_x11_gles2.cc",
@@ -358,7 +359,7 @@ jumbo_component("gl") {
"scoped_cgl.h",
]
- libs = [
+ frameworks = [
"CoreFoundation.framework",
"IOSurface.framework",
"OpenGL.framework",
@@ -446,7 +447,7 @@ if (is_mac && use_egl) {
}
}
-jumbo_static_library("gl_unittest_utils") {
+static_library("gl_unittest_utils") {
testonly = true
sources = [
"egl_bindings_autogen_mock.cc",
@@ -472,7 +473,7 @@ jumbo_static_library("gl_unittest_utils") {
]
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"test/gl_image_bind_test_template.h",
@@ -499,7 +500,7 @@ jumbo_static_library("test_support") {
"//ui/base",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
deps += [ "//ui/platform_window/common" ]
}
@@ -520,7 +521,10 @@ source_set("run_all_unittests") {
sources = [ "test/run_all_unittests.cc" ]
- deps = [ "//base" ]
+ deps = [
+ "//base",
+ "//ui/base:features",
+ ]
public_deps = [ "//base/test:test_support" ]
@@ -560,7 +564,7 @@ test("gl_unittests") {
if (is_mac) {
sources += [ "gl_image_io_surface_unittest.cc" ]
- libs = [ "IOSurface.framework" ]
+ frameworks = [ "IOSurface.framework" ]
}
if (is_win) {
diff --git a/chromium/ui/gl/OWNERS b/chromium/ui/gl/OWNERS
index 7b3ab11b08d..cb9ad84e7fd 100644
--- a/chromium/ui/gl/OWNERS
+++ b/chromium/ui/gl/OWNERS
@@ -1,5 +1,4 @@
backer@chromium.org
-ericrk@chromium.org
kbr@chromium.org
sunnyps@chromium.org
zmo@chromium.org
diff --git a/chromium/ui/gl/angle_platform_impl.cc b/chromium/ui/gl/angle_platform_impl.cc
index ff4b4a73c12..980a7ba99e9 100644
--- a/chromium/ui/gl/angle_platform_impl.cc
+++ b/chromium/ui/gl/angle_platform_impl.cc
@@ -123,17 +123,18 @@ void ANGLEPlatformImpl_histogramBoolean(PlatformMethods* platform,
ANGLEPlatformImpl_histogramEnumeration(platform, name, sample ? 1 : 0, 2);
}
+NO_SANITIZE("cfi-icall")
+void AnglePlatformImpl_runWorkerTask(PostWorkerTaskCallback callback, void* user_data) {
+ TRACE_EVENT0("toplevel", "ANGLEPlatformImpl::RunWorkerTask");
+ callback(user_data);
+}
+
void ANGLEPlatformImpl_postWorkerTask(PlatformMethods* platform,
PostWorkerTaskCallback callback,
void* user_data) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::USER_VISIBLE},
- base::BindOnce(
- [](PostWorkerTaskCallback callback, void* user_data) {
- TRACE_EVENT0("toplevel", "ANGLEPlatformImpl::RunWorkerTask");
- callback(user_data);
- },
- callback, user_data));
+ base::BindOnce(&AnglePlatformImpl_runWorkerTask, callback, user_data));
}
} // anonymous namespace
diff --git a/chromium/ui/gl/dc_layer_tree.cc b/chromium/ui/gl/dc_layer_tree.cc
index f3548fbca02..40ce168aef1 100644
--- a/chromium/ui/gl/dc_layer_tree.cc
+++ b/chromium/ui/gl/dc_layer_tree.cc
@@ -189,6 +189,8 @@ bool DCLayerTree::CommitAndClearPendingOverlays(
} else {
new_video_swap_chains.emplace_back(std::make_unique<SwapChainPresenter>(
this, d3d11_device_, dcomp_device_));
+ if (frame_rate_ > 0)
+ new_video_swap_chains.back()->SetFrameRate(frame_rate_);
}
}
video_swap_chains_.swap(new_video_swap_chains);
@@ -255,4 +257,10 @@ bool DCLayerTree::ScheduleDCLayer(const ui::DCRendererLayerParams& params) {
return true;
}
+void DCLayerTree::SetFrameRate(float frame_rate) {
+ frame_rate_ = frame_rate;
+ for (size_t ii = 0; ii < video_swap_chains_.size(); ++ii)
+ video_swap_chains_[ii]->SetFrameRate(frame_rate);
+}
+
} // namespace gl
diff --git a/chromium/ui/gl/dc_layer_tree.h b/chromium/ui/gl/dc_layer_tree.h
index 54b6a5724a8..99aa29f0477 100644
--- a/chromium/ui/gl/dc_layer_tree.h
+++ b/chromium/ui/gl/dc_layer_tree.h
@@ -83,6 +83,8 @@ class DCLayerTree {
Microsoft::WRL::ComPtr<IDXGISwapChain1> GetLayerSwapChainForTesting(
size_t index) const;
+ void SetFrameRate(float frame_rate);
+
private:
const bool disable_nv12_dynamic_textures_;
const bool disable_larger_than_screen_overlays_;
@@ -126,6 +128,9 @@ class DCLayerTree {
// List of swap chain presenters for previous frame.
std::vector<std::unique_ptr<SwapChainPresenter>> video_swap_chains_;
+ // Number of frames per second.
+ float frame_rate_ = 0.f;
+
DISALLOW_COPY_AND_ASSIGN(DCLayerTree);
};
diff --git a/chromium/ui/gl/direct_composition_child_surface_win.cc b/chromium/ui/gl/direct_composition_child_surface_win.cc
index 4c0c3441eb8..e1db24c417f 100644
--- a/chromium/ui/gl/direct_composition_child_surface_win.cc
+++ b/chromium/ui/gl/direct_composition_child_surface_win.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/process/process.h"
#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "base/win/windows_version.h"
@@ -23,7 +24,9 @@
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_switches.h"
+#include "ui/gl/gl_utils.h"
#include "ui/gl/scoped_make_current.h"
+#include "ui/gl/vsync_thread_win.h"
#ifndef EGL_ANGLE_flexible_surface_compatibility
#define EGL_ANGLE_flexible_surface_compatibility 1
@@ -47,11 +50,32 @@ IDCompositionSurface* g_current_surface = nullptr;
bool g_direct_composition_swap_chain_failed = false;
+bool SupportsLowLatencyPresentation() {
+ return base::FeatureList::IsEnabled(
+ features::kDirectCompositionLowLatencyPresentation);
+}
} // namespace
+DirectCompositionChildSurfaceWin::PendingFrame::PendingFrame(
+ Microsoft::WRL::ComPtr<ID3D11Query> query,
+ PresentationCallback callback)
+ : query(std::move(query)), callback(std::move(callback)) {}
+DirectCompositionChildSurfaceWin::PendingFrame::PendingFrame(
+ PendingFrame&& other) = default;
+DirectCompositionChildSurfaceWin::PendingFrame::~PendingFrame() = default;
+DirectCompositionChildSurfaceWin::PendingFrame&
+DirectCompositionChildSurfaceWin::PendingFrame::operator=(
+ PendingFrame&& other) = default;
+
DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin(
- bool use_angle_texture_offset)
- : use_angle_texture_offset_(use_angle_texture_offset) {}
+ VSyncCallback vsync_callback,
+ bool use_angle_texture_offset,
+ size_t max_pending_frames)
+ : vsync_callback_(std::move(vsync_callback)),
+ use_angle_texture_offset_(use_angle_texture_offset),
+ max_pending_frames_(max_pending_frames),
+ vsync_thread_(VSyncThreadWin::GetInstance()),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() {
Destroy();
@@ -123,20 +147,47 @@ bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
UINT interval =
first_swap_ || !vsync_enabled_ || use_swap_chain_tearing ? 0 : 1;
UINT flags = use_swap_chain_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0;
- DXGI_PRESENT_PARAMETERS params = {};
- RECT dirty_rect = swap_rect_.ToRECT();
- params.DirtyRectsCount = 1;
- params.pDirtyRects = &dirty_rect;
+ flags |= DXGI_PRESENT_USE_DURATION;
+ bool force_full_damage =
+ ShouldForceDirectCompositionRootSurfaceFullDamage();
TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::PresentSwapChain",
- "interval", interval, "dirty_rect", swap_rect_.ToString());
- HRESULT hr = swap_chain_->Present1(interval, flags, &params);
+ "interval", interval, "dirty_rect",
+ force_full_damage ? "full_damage" : swap_rect_.ToString());
+ HRESULT hr;
+ if (force_full_damage) {
+ hr = swap_chain_->Present(interval, flags);
+ } else {
+ DXGI_PRESENT_PARAMETERS params = {};
+ RECT dirty_rect = swap_rect_.ToRECT();
+ params.DirtyRectsCount = 1;
+ params.pDirtyRects = &dirty_rect;
+ hr = swap_chain_->Present1(interval, flags, &params);
+ }
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
// indicates that the window is occluded and we can stop rendering.
if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
return false;
}
+
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
+ if (SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
+ DXGI_FRAME_STATISTICS_MEDIA stats = {};
+ // GetFrameStatisticsMedia fails with
+ // DXGI_ERROR_FRAME_STATISTICS_DISJOINT sometimes, which means an
+ // event (such as power cycle) interrupted the gathering of
+ // presentation statistics. In this situation, calling the function
+ // again succeeds but returns with CompositionMode = NONE.
+ // Waiting for the DXGI adapter to finish presenting before calling
+ // the function doesn't get rid of the failure.
+ if (SUCCEEDED(swap_chain_media->GetFrameStatisticsMedia(&stats))) {
+ base::UmaHistogramSparse(
+ "GPU.DirectComposition.CompositionMode.MainBuffer",
+ stats.CompositionMode);
+ }
+ }
+
if (first_swap_) {
// Wait for the GPU to finish executing its commands before
// committing the DirectComposition tree, or else the swapchain
@@ -158,6 +209,13 @@ bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
}
void DirectCompositionChildSurfaceWin::Destroy() {
+ for (auto& frame : pending_frames_)
+ std::move(frame.callback).Run(gfx::PresentationFeedback::Failure());
+ pending_frames_.clear();
+
+ if (vsync_thread_started_)
+ vsync_thread_->RemoveObserver(this);
+
if (default_surface_) {
if (!eglDestroySurface(GetDisplay(), default_surface_)) {
DLOG(ERROR) << "eglDestroySurface failed with error "
@@ -198,13 +256,12 @@ gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers(
PresentationCallback callback) {
TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::SwapBuffers", "size",
size_.ToString());
- // PresentationCallback is handled by DirectCompositionSurfaceWin. The child
- // surface doesn't need to provide presentation feedback.
- DCHECK(!callback);
- return ReleaseDrawTexture(false /* will_discard */)
- ? gfx::SwapResult::SWAP_ACK
- : gfx::SwapResult::SWAP_FAILED;
+ gfx::SwapResult swap_result = ReleaseDrawTexture(false /* will_discard */)
+ ? gfx::SwapResult::SWAP_ACK
+ : gfx::SwapResult::SWAP_FAILED;
+ EnqueuePendingFrame(std::move(callback));
+ return swap_result;
}
gfx::SurfaceOrigin DirectCompositionChildSurfaceWin::GetOrigin() const {
@@ -349,6 +406,8 @@ bool DirectCompositionChildSurfaceWin::SetDrawRectangle(
DCHECK(SUCCEEDED(hr))
<< "SetColorSpace1 failed with error " << std::hex << hr;
}
+
+ SetSwapChainPresentDuration();
}
swap_rect_ = rectangle;
@@ -473,6 +532,145 @@ bool DirectCompositionChildSurfaceWin::SetEnableDCLayers(bool enable) {
return true;
}
+void DirectCompositionChildSurfaceWin::SetFrameRate(float frame_rate) {
+ frame_rate_ = frame_rate;
+ SetSwapChainPresentDuration();
+}
+
+void DirectCompositionChildSurfaceWin::SetSwapChainPresentDuration() {
+ if (!swap_chain_)
+ return;
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
+ if (SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
+ UINT duration_100ns = FrameRateToPresentDuration(frame_rate_);
+ HRESULT hr = swap_chain_media->SetPresentDuration(duration_100ns);
+ if (FAILED(hr))
+ DLOG(ERROR) << "SetPresentDuration failed with error " << std::hex << hr;
+ }
+}
+
+gfx::VSyncProvider* DirectCompositionChildSurfaceWin::GetVSyncProvider() {
+ return vsync_thread_->vsync_provider();
+}
+
+bool DirectCompositionChildSurfaceWin::SupportsGpuVSync() const {
+ return true;
+}
+
+void DirectCompositionChildSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
+ {
+ base::AutoLock auto_lock(vsync_callback_enabled_lock_);
+ vsync_callback_enabled_ = enabled;
+ }
+ StartOrStopVSyncThread();
+}
+
+void DirectCompositionChildSurfaceWin::OnVSync(base::TimeTicks vsync_time,
+ base::TimeDelta interval) {
+ // Main thread will run vsync callback in low latency presentation mode.
+ if (VSyncCallbackEnabled() && !SupportsLowLatencyPresentation()) {
+ DCHECK(vsync_callback_);
+ vsync_callback_.Run(vsync_time, interval);
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&DirectCompositionChildSurfaceWin::HandleVSyncOnMainThread,
+ weak_factory_.GetWeakPtr(), vsync_time, interval));
+}
+
+void DirectCompositionChildSurfaceWin::HandleVSyncOnMainThread(
+ base::TimeTicks vsync_time,
+ base::TimeDelta interval) {
+ last_vsync_time_ = vsync_time;
+ last_vsync_interval_ = interval;
+
+ CheckPendingFrames();
+
+ UMA_HISTOGRAM_COUNTS_100("GPU.DirectComposition.NumPendingFrames",
+ pending_frames_.size());
+
+ if (SupportsLowLatencyPresentation() && VSyncCallbackEnabled() &&
+ pending_frames_.size() < max_pending_frames_) {
+ DCHECK(vsync_callback_);
+ vsync_callback_.Run(vsync_time, interval);
+ }
+}
+
+void DirectCompositionChildSurfaceWin::StartOrStopVSyncThread() {
+ bool start_vsync_thread = VSyncCallbackEnabled() || !pending_frames_.empty();
+ if (vsync_thread_started_ == start_vsync_thread)
+ return;
+ vsync_thread_started_ = start_vsync_thread;
+ if (start_vsync_thread) {
+ vsync_thread_->AddObserver(this);
+ } else {
+ vsync_thread_->RemoveObserver(this);
+ }
+}
+
+bool DirectCompositionChildSurfaceWin::VSyncCallbackEnabled() const {
+ base::AutoLock auto_lock(vsync_callback_enabled_lock_);
+ return vsync_callback_enabled_;
+}
+
+void DirectCompositionChildSurfaceWin::CheckPendingFrames() {
+ TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::CheckPendingFrames",
+ "num_pending_frames", pending_frames_.size());
+
+ if (pending_frames_.empty())
+ return;
+
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
+ d3d11_device_->GetImmediateContext(&context);
+ while (!pending_frames_.empty()) {
+ auto& frame = pending_frames_.front();
+ // Query isn't created if there was no damage for previous frame.
+ if (frame.query) {
+ HRESULT hr = context->GetData(frame.query.Get(), nullptr, 0,
+ D3D11_ASYNC_GETDATA_DONOTFLUSH);
+ // When the GPU completes execution past the event query, GetData() will
+ // return S_OK, and S_FALSE otherwise. Do not use SUCCEEDED() because
+ // S_FALSE is also a success code.
+ if (hr != S_OK)
+ break;
+ }
+ std::move(frame.callback)
+ .Run(
+ gfx::PresentationFeedback(last_vsync_time_, last_vsync_interval_,
+ gfx::PresentationFeedback::kVSync |
+ gfx::PresentationFeedback::kHWClock));
+ pending_frames_.pop_front();
+ }
+
+ StartOrStopVSyncThread();
+}
+
+void DirectCompositionChildSurfaceWin::EnqueuePendingFrame(
+ PresentationCallback callback) {
+ Microsoft::WRL::ComPtr<ID3D11Query> query;
+
+ // Do not create query for empty damage so that 3D engine is not used when
+ // only presenting video in overlay. Callback will be dequeued on next vsync.
+ if (!swap_rect_.IsEmpty()) {
+ D3D11_QUERY_DESC desc = {};
+ desc.Query = D3D11_QUERY_EVENT;
+ HRESULT hr = d3d11_device_->CreateQuery(&desc, &query);
+ if (SUCCEEDED(hr)) {
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
+ d3d11_device_->GetImmediateContext(&context);
+ context->End(query.Get());
+ context->Flush();
+ } else {
+ DLOG(ERROR) << "CreateQuery failed with error 0x" << std::hex << hr;
+ }
+ }
+
+ pending_frames_.emplace_back(std::move(query), std::move(callback));
+
+ StartOrStopVSyncThread();
+}
+
// static
bool DirectCompositionChildSurfaceWin::IsDirectCompositionSwapChainFailed() {
return g_direct_composition_swap_chain_failed;
diff --git a/chromium/ui/gl/direct_composition_child_surface_win.h b/chromium/ui/gl/direct_composition_child_surface_win.h
index 98a38f54be9..4690bb63e5d 100644
--- a/chromium/ui/gl/direct_composition_child_surface_win.h
+++ b/chromium/ui/gl/direct_composition_child_surface_win.h
@@ -10,14 +10,30 @@
#include <dcomp.h>
#include <wrl/client.h>
+#include "base/callback.h"
+#include "base/containers/circular_deque.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
#include "ui/gl/gl_export.h"
#include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/vsync_observer.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
namespace gl {
+class VSyncThreadWin;
-class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
+class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL,
+ public VSyncObserver {
public:
- explicit DirectCompositionChildSurfaceWin(bool use_angle_texture_offset);
+ using VSyncCallback =
+ base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
+ DirectCompositionChildSurfaceWin(VSyncCallback vsync_callback,
+ bool use_angle_texture_offset,
+ size_t max_pending_frames);
// GLSurfaceEGL implementation.
bool Initialize(GLSurfaceFormat format) override;
@@ -38,6 +54,14 @@ class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
const gfx::ColorSpace& color_space,
bool has_alpha) override;
bool SetEnableDCLayers(bool enable) override;
+ void SetFrameRate(float frame_rate) override;
+ gfx::VSyncProvider* GetVSyncProvider() override;
+ bool SupportsGpuVSync() const override;
+ void SetGpuVSyncEnabled(bool enabled) override;
+
+ // VSyncObserver implementation.
+ void OnVSync(base::TimeTicks vsync_time, base::TimeDelta interval) override;
+
static bool IsDirectCompositionSwapChainFailed();
const Microsoft::WRL::ComPtr<IDCompositionSurface>& dcomp_surface() const {
@@ -57,12 +81,38 @@ class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
~DirectCompositionChildSurfaceWin() override;
private:
+ struct PendingFrame {
+ PendingFrame(Microsoft::WRL::ComPtr<ID3D11Query> query,
+ PresentationCallback callback);
+ PendingFrame(PendingFrame&& other);
+ ~PendingFrame();
+ PendingFrame& operator=(PendingFrame&& other);
+
+ // Event query issued after frame is presented.
+ Microsoft::WRL::ComPtr<ID3D11Query> query;
+
+ // Presentation callback enqueued in SwapBuffers().
+ PresentationCallback callback;
+ };
+
+ void EnqueuePendingFrame(PresentationCallback callback);
+ void CheckPendingFrames();
+
+ void StartOrStopVSyncThread();
+
+ bool VSyncCallbackEnabled() const;
+
+ void HandleVSyncOnMainThread(base::TimeTicks vsync_time,
+ base::TimeDelta interval);
+
// Release the texture that's currently being drawn to. If will_discard is
// true then the surface should be discarded without swapping any contents
// to it. Returns false if this fails.
bool ReleaseDrawTexture(bool will_discard);
- const bool use_angle_texture_offset_;
+ // This is called when a new swap chain is created, or when a new frame rate
+ // is received.
+ void SetSwapChainPresentDuration();
gfx::Size size_ = gfx::Size(1, 1);
bool enable_dc_layers_ = false;
@@ -92,6 +142,28 @@ class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> draw_texture_;
+ // Number of frames per second.
+ float frame_rate_ = 0.f;
+
+ const VSyncCallback vsync_callback_;
+ const bool use_angle_texture_offset_;
+ const size_t max_pending_frames_;
+
+ VSyncThreadWin* const vsync_thread_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ bool vsync_thread_started_ = false;
+ bool vsync_callback_enabled_ GUARDED_BY(vsync_callback_enabled_lock_) = false;
+ mutable base::Lock vsync_callback_enabled_lock_;
+
+ // Queue of pending presentation callbacks.
+ base::circular_deque<PendingFrame> pending_frames_;
+
+ base::TimeTicks last_vsync_time_;
+ base::TimeDelta last_vsync_interval_;
+
+ base::WeakPtrFactory<DirectCompositionChildSurfaceWin> weak_factory_{this};
+
DISALLOW_COPY_AND_ASSIGN(DirectCompositionChildSurfaceWin);
};
diff --git a/chromium/ui/gl/direct_composition_surface_win.cc b/chromium/ui/gl/direct_composition_surface_win.cc
index 7ace65466d0..6406d7d8ee5 100644
--- a/chromium/ui/gl/direct_composition_surface_win.cc
+++ b/chromium/ui/gl/direct_composition_surface_win.cc
@@ -14,7 +14,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "base/win/windows_version.h"
@@ -22,10 +21,8 @@
#include "ui/gl/direct_composition_child_surface_win.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_implementation.h"
-#include "ui/gl/gl_surface_presentation_helper.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_switching_manager.h"
-#include "ui/gl/vsync_thread_win.h"
#ifndef EGL_ANGLE_flexible_surface_compatibility
#define EGL_ANGLE_flexible_surface_compatibility 1
@@ -39,6 +36,8 @@ bool g_overlay_caps_valid = false;
// Indicates support for either NV12 or YUY2 overlays. GUARDED_BY
// GetOverlayLock().
bool g_supports_overlays = false;
+// Whether the DecodeSwapChain is disabled or not.
+bool g_decode_swap_chain_disabled = false;
// The lock to guard g_overlay_caps_valid and g_supports_overlays.
base::Lock& GetOverlayLock() {
@@ -50,6 +49,7 @@ bool SupportsOverlays() {
base::AutoLock auto_lock(GetOverlayLock());
return g_supports_overlays;
}
+
void SetSupportsOverlays(bool support) {
base::AutoLock auto_lock(GetOverlayLock());
g_supports_overlays = support;
@@ -93,12 +93,27 @@ DXGI_FORMAT g_overlay_format_used = DXGI_FORMAT_NV12;
DXGI_FORMAT g_overlay_format_used_hdr = DXGI_FORMAT_UNKNOWN;
// These are the raw support info, which shouldn't depend on field trial state,
-// or command line flags.
+// or command line flags. GUARDED_BY GetOverlayLock().
UINT g_nv12_overlay_support_flags = 0;
UINT g_yuy2_overlay_support_flags = 0;
UINT g_bgra8_overlay_support_flags = 0;
UINT g_rgb10a2_overlay_support_flags = 0;
+// When this is set, if NV12 or YUY2 overlays are supported, set BGRA8 overlays
+// as supported as well.
+bool g_enable_bgra8_overlays_with_yuv_overlay_support = false;
+
+void SetOverlaySupportFlagsForFormats(UINT nv12_flags,
+ UINT yuy2_flags,
+ UINT bgra8_flags,
+ UINT rgb10a2_flags) {
+ base::AutoLock auto_lock(GetOverlayLock());
+ g_nv12_overlay_support_flags = nv12_flags;
+ g_yuy2_overlay_support_flags = yuy2_flags;
+ g_bgra8_overlay_support_flags = bgra8_flags;
+ g_rgb10a2_overlay_support_flags = rgb10a2_flags;
+}
+
bool FlagsSupportsOverlays(UINT flags) {
return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
@@ -205,6 +220,13 @@ void GetGpuDriverOverlayInfo(bool* supports_overlays,
*overlay_format_used = DXGI_FORMAT_YUY2;
*supports_overlays = true;
}
+ if (g_enable_bgra8_overlays_with_yuv_overlay_support) {
+ if (FlagsSupportsOverlays(*nv12_overlay_support_flags))
+ *bgra8_overlay_support_flags = *nv12_overlay_support_flags;
+ else if (FlagsSupportsOverlays(*yuy2_overlay_support_flags))
+ *bgra8_overlay_support_flags = *yuy2_overlay_support_flags;
+ }
+
if (*supports_overlays) {
DXGI_OUTPUT_DESC monitor_desc = {};
if (SUCCEEDED(output3->GetDesc(&monitor_desc))) {
@@ -286,66 +308,34 @@ void UpdateOverlaySupport() {
// Update global caps
SetSupportsOverlays(supports_overlays);
+ SetOverlaySupportFlagsForFormats(
+ nv12_overlay_support_flags, yuy2_overlay_support_flags,
+ bgra8_overlay_support_flags, rgb10a2_overlay_support_flags);
g_overlay_format_used = overlay_format_used;
g_overlay_format_used_hdr = overlay_format_used_hdr;
- g_nv12_overlay_support_flags = nv12_overlay_support_flags;
- g_yuy2_overlay_support_flags = yuy2_overlay_support_flags;
- g_bgra8_overlay_support_flags = bgra8_overlay_support_flags;
- g_rgb10a2_overlay_support_flags = rgb10a2_overlay_support_flags;
g_overlay_monitor_size = overlay_monitor_size;
}
-bool SupportsPresentationFeedback() {
- return base::FeatureList::IsEnabled(
- features::kDirectCompositionPresentationFeedback) &&
- base::FeatureList::IsEnabled(features::kDirectCompositionGpuVSync);
-}
-
-bool SupportsLowLatencyPresentation() {
- return base::FeatureList::IsEnabled(
- features::kDirectCompositionLowLatencyPresentation) &&
- SupportsPresentationFeedback();
-}
-
void RunOverlayHdrGpuInfoUpdateCallback() {
if (g_overlay_hdr_gpu_info_callback)
g_overlay_hdr_gpu_info_callback.Run();
}
} // namespace
-DirectCompositionSurfaceWin::PendingFrame::PendingFrame(
- Microsoft::WRL::ComPtr<ID3D11Query> query,
- PresentationCallback callback)
- : query(std::move(query)), callback(std::move(callback)) {}
-DirectCompositionSurfaceWin::PendingFrame::PendingFrame(PendingFrame&& other) =
- default;
-DirectCompositionSurfaceWin::PendingFrame::~PendingFrame() = default;
-DirectCompositionSurfaceWin::PendingFrame&
-DirectCompositionSurfaceWin::PendingFrame::operator=(PendingFrame&& other) =
- default;
-
DirectCompositionSurfaceWin::DirectCompositionSurfaceWin(
- std::unique_ptr<gfx::VSyncProvider> vsync_provider,
- VSyncCallback vsync_callback,
HWND parent_window,
+ VSyncCallback vsync_callback,
const Settings& settings)
: GLSurfaceEGL(),
child_window_(parent_window),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
root_surface_(new DirectCompositionChildSurfaceWin(
- settings.use_angle_texture_offset)),
+ std::move(vsync_callback),
+ settings.use_angle_texture_offset,
+ settings.max_pending_frames)),
layer_tree_(std::make_unique<DCLayerTree>(
settings.disable_nv12_dynamic_textures,
settings.disable_larger_than_screen_overlays,
- settings.disable_vp_scaling)),
- presentation_helper_(
- std::make_unique<GLSurfacePresentationHelper>(vsync_provider.get())),
- vsync_provider_(std::move(vsync_provider)),
- vsync_callback_(std::move(vsync_callback)),
- max_pending_frames_(settings.max_pending_frames) {
- // Call GetWeakPtr() on main thread before calling on vsync thread so that the
- // internal weak reference is initialized in a thread-safe way.
- weak_ptr_ = weak_factory_.GetWeakPtr();
+ settings.disable_vp_scaling)) {
ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
}
@@ -428,15 +418,21 @@ bool DirectCompositionSurfaceWin::AreOverlaysSupported() {
// static
bool DirectCompositionSurfaceWin::IsDecodeSwapChainSupported() {
- if (base::FeatureList::IsEnabled(
+ if (!g_decode_swap_chain_disabled &&
+ base::FeatureList::IsEnabled(
features::kDirectCompositionUseNV12DecodeSwapChain)) {
UpdateOverlaySupport();
- return GetOverlayFormatUsed() == DXGI_FORMAT_NV12;
+ return GetOverlayFormatUsedForSDR() == DXGI_FORMAT_NV12;
}
return false;
}
// static
+void DirectCompositionSurfaceWin::DisableDecodeSwapChain() {
+ g_decode_swap_chain_disabled = true;
+}
+
+// static
void DirectCompositionSurfaceWin::DisableOverlays() {
SetSupportsOverlays(false);
RunOverlayHdrGpuInfoUpdateCallback();
@@ -463,6 +459,7 @@ bool DirectCompositionSurfaceWin::AreScaledOverlaysSupported() {
// static
UINT DirectCompositionSurfaceWin::GetOverlaySupportFlags(DXGI_FORMAT format) {
UpdateOverlaySupport();
+ base::AutoLock auto_lock(GetOverlayLock());
UINT support_flag = 0;
switch (format) {
case DXGI_FORMAT_NV12:
@@ -490,7 +487,7 @@ gfx::Size DirectCompositionSurfaceWin::GetOverlayMonitorSize() {
}
// static
-DXGI_FORMAT DirectCompositionSurfaceWin::GetOverlayFormatUsed() {
+DXGI_FORMAT DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR() {
return g_overlay_format_used;
}
@@ -516,7 +513,7 @@ void DirectCompositionSurfaceWin::SetOverlayFormatUsedForTesting(
DCHECK(format == DXGI_FORMAT_NV12 || format == DXGI_FORMAT_YUY2);
UpdateOverlaySupport();
g_overlay_format_used = format;
- DCHECK_EQ(format, GetOverlayFormatUsed());
+ DCHECK_EQ(format, GetOverlayFormatUsedForSDR());
}
// static
@@ -572,7 +569,6 @@ bool DirectCompositionSurfaceWin::IsHDRSupported() {
}
base::UmaHistogramSparse("GPU.Output.ColorSpace", desc.ColorSpace);
- base::UmaHistogramSparse("GPU.Output.MaxLuminance", desc.MaxLuminance);
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
hdr_monitor_found = true;
@@ -635,6 +631,13 @@ void DirectCompositionSurfaceWin::SetOverlayHDRGpuInfoUpdateCallback(
g_overlay_hdr_gpu_info_callback = std::move(callback);
}
+// static
+void DirectCompositionSurfaceWin::EnableBGRA8OverlaysWithYUVOverlaySupport() {
+ // This has to be set before initializing overlay caps.
+ DCHECK(!OverlayCapsValid());
+ g_enable_bgra8_overlays_with_yuv_overlay_support = true;
+}
+
bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
d3d11_device_ = QueryD3D11DeviceObjectFromANGLE();
if (!d3d11_device_) {
@@ -659,25 +662,11 @@ bool DirectCompositionSurfaceWin::Initialize(GLSurfaceFormat format) {
if (!root_surface_->Initialize(GLSurfaceFormat()))
return false;
- if ((SupportsGpuVSync() && vsync_callback_) || SupportsPresentationFeedback())
- vsync_thread_ = VSyncThreadWin::GetInstance();
-
return true;
}
void DirectCompositionSurfaceWin::Destroy() {
- for (auto& frame : pending_frames_)
- std::move(frame.callback).Run(gfx::PresentationFeedback::Failure());
- pending_frames_.clear();
-
- if (vsync_thread_) {
- vsync_thread_->RemoveObserver(this);
- vsync_thread_ = nullptr;
- }
- // Destroy presentation helper first because its dtor calls GetHandle.
- presentation_helper_ = nullptr;
root_surface_->Destroy();
-
// Freeing DComp resources such as visuals and surfaces causes the
// device to become 'dirty'. We must commit the changes to the device
// in order for the objects to actually be destroyed.
@@ -718,29 +707,14 @@ gfx::SwapResult DirectCompositionSurfaceWin::SwapBuffers(
PresentationCallback callback) {
TRACE_EVENT0("gpu", "DirectCompositionSurfaceWin::SwapBuffers");
- base::Optional<GLSurfacePresentationHelper::ScopedSwapBuffers>
- scoped_swap_buffers;
- if (!SupportsPresentationFeedback()) {
- scoped_swap_buffers.emplace(presentation_helper_.get(),
- std::move(callback));
- }
+ if (root_surface_->SwapBuffers(std::move(callback)) !=
+ gfx::SwapResult::SWAP_ACK)
+ return gfx::SwapResult::SWAP_FAILED;
- gfx::SwapResult swap_result;
- if (root_surface_->SwapBuffers(PresentationCallback()) ==
- gfx::SwapResult::SWAP_ACK &&
- layer_tree_->CommitAndClearPendingOverlays(root_surface_.get())) {
- swap_result = gfx::SwapResult::SWAP_ACK;
- } else {
- swap_result = gfx::SwapResult::SWAP_FAILED;
- }
-
- if (scoped_swap_buffers) {
- scoped_swap_buffers->set_result(swap_result);
- } else {
- EnqueuePendingFrame(std::move(callback));
- }
+ if (!layer_tree_->CommitAndClearPendingOverlays(root_surface_.get()))
+ return gfx::SwapResult::SWAP_FAILED;
- return swap_result;
+ return gfx::SwapResult::SWAP_ACK;
}
gfx::SwapResult DirectCompositionSurfaceWin::PostSubBuffer(
@@ -755,7 +729,7 @@ gfx::SwapResult DirectCompositionSurfaceWin::PostSubBuffer(
}
gfx::VSyncProvider* DirectCompositionSurfaceWin::GetVSyncProvider() {
- return vsync_provider_.get();
+ return root_surface_->GetVSyncProvider();
}
void DirectCompositionSurfaceWin::SetVSyncEnabled(bool enabled) {
@@ -767,6 +741,11 @@ bool DirectCompositionSurfaceWin::ScheduleDCLayer(
return layer_tree_->ScheduleDCLayer(params);
}
+void DirectCompositionSurfaceWin::SetFrameRate(float frame_rate) {
+ layer_tree_->SetFrameRate(frame_rate);
+ root_surface_->SetFrameRate(frame_rate);
+}
+
bool DirectCompositionSurfaceWin::SetEnableDCLayers(bool enable) {
return root_surface_->SetEnableDCLayers(enable);
}
@@ -780,8 +759,6 @@ bool DirectCompositionSurfaceWin::SupportsPostSubBuffer() {
}
bool DirectCompositionSurfaceWin::OnMakeCurrent(GLContext* context) {
- if (presentation_helper_)
- presentation_helper_->OnMakeCurrent(context, this);
return root_surface_->OnMakeCurrent(context);
}
@@ -810,118 +787,11 @@ gfx::Vector2d DirectCompositionSurfaceWin::GetDrawOffset() const {
}
bool DirectCompositionSurfaceWin::SupportsGpuVSync() const {
- return base::FeatureList::IsEnabled(features::kDirectCompositionGpuVSync);
+ return true;
}
void DirectCompositionSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
- DCHECK(vsync_thread_);
- {
- base::AutoLock lock(vsync_callback_lock_);
- vsync_callback_enabled_ = enabled;
- }
- StartOrStopVSyncThread();
-}
-
-void DirectCompositionSurfaceWin::CheckPendingFrames() {
- DCHECK(SupportsPresentationFeedback());
-
- TRACE_EVENT1("gpu", "DirectCompositionSurfaceWin::CheckPendingFrames",
- "num_pending_frames", pending_frames_.size());
-
- if (pending_frames_.empty())
- return;
-
- Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
- d3d11_device_->GetImmediateContext(&context);
- while (!pending_frames_.empty()) {
- auto& frame = pending_frames_.front();
- if (frame.query) {
- HRESULT hr = context->GetData(frame.query.Get(), nullptr, 0,
- D3D11_ASYNC_GETDATA_DONOTFLUSH);
- // When the GPU completes execution past the event query, GetData() will
- // return S_OK, and S_FALSE otherwise. Do not use SUCCEEDED() because
- // S_FALSE is also a success code.
- if (hr != S_OK)
- break;
- }
- std::move(frame.callback)
- .Run(
- gfx::PresentationFeedback(last_vsync_time_, last_vsync_interval_,
- gfx::PresentationFeedback::kVSync |
- gfx::PresentationFeedback::kHWClock));
- pending_frames_.pop_front();
- }
-
- StartOrStopVSyncThread();
-}
-
-void DirectCompositionSurfaceWin::EnqueuePendingFrame(
- PresentationCallback callback) {
- DCHECK(SupportsPresentationFeedback());
-
- Microsoft::WRL::ComPtr<ID3D11Query> query;
- D3D11_QUERY_DESC desc = {};
- desc.Query = D3D11_QUERY_EVENT;
-
- HRESULT hr = d3d11_device_->CreateQuery(&desc, &query);
- if (SUCCEEDED(hr)) {
- Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
- d3d11_device_->GetImmediateContext(&context);
- context->End(query.Get());
- context->Flush();
- } else {
- DLOG(ERROR) << "CreateQuery failed with error 0x" << std::hex << hr;
- }
-
- pending_frames_.emplace_back(std::move(query), std::move(callback));
-
- StartOrStopVSyncThread();
-}
-
-bool DirectCompositionSurfaceWin::VSyncCallbackEnabled() const {
- base::AutoLock lock(vsync_callback_lock_);
- return vsync_callback_enabled_;
-}
-
-void DirectCompositionSurfaceWin::StartOrStopVSyncThread() {
- if (VSyncCallbackEnabled() || !pending_frames_.empty()) {
- vsync_thread_->AddObserver(this);
- } else {
- vsync_thread_->RemoveObserver(this);
- }
-}
-
-void DirectCompositionSurfaceWin::OnVSync(base::TimeTicks vsync_time,
- base::TimeDelta interval) {
- if (!SupportsLowLatencyPresentation() && VSyncCallbackEnabled()) {
- DCHECK(vsync_callback_);
- vsync_callback_.Run(vsync_time, interval);
- }
-
- if (SupportsPresentationFeedback()) {
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&DirectCompositionSurfaceWin::HandleVSyncOnMainThread,
- weak_ptr_, vsync_time, interval));
- }
-}
-
-void DirectCompositionSurfaceWin::HandleVSyncOnMainThread(
- base::TimeTicks vsync_time,
- base::TimeDelta interval) {
- last_vsync_time_ = vsync_time;
- last_vsync_interval_ = interval;
-
- CheckPendingFrames();
-
- UMA_HISTOGRAM_COUNTS_100("GPU.DirectComposition.NumPendingFrames",
- pending_frames_.size());
-
- if (SupportsLowLatencyPresentation() && VSyncCallbackEnabled() &&
- pending_frames_.size() < max_pending_frames_) {
- DCHECK(vsync_callback_);
- vsync_callback_.Run(vsync_time, interval);
- }
+ root_surface_->SetGpuVSyncEnabled(enabled);
}
void DirectCompositionSurfaceWin::OnGpuSwitched(
diff --git a/chromium/ui/gl/direct_composition_surface_win.h b/chromium/ui/gl/direct_composition_surface_win.h
index 82d17af5964..12c9d1db996 100644
--- a/chromium/ui/gl/direct_composition_surface_win.h
+++ b/chromium/ui/gl/direct_composition_surface_win.h
@@ -21,16 +21,12 @@
namespace gl {
class DCLayerTree;
class DirectCompositionChildSurfaceWin;
-class GLSurfacePresentationHelper;
-class VSyncThreadWin;
class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
- public VSyncObserver,
public ui::GpuSwitchingObserver {
public:
using VSyncCallback =
base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
-
using OverlayHDRInfoUpdateCallback = base::RepeatingClosure;
struct Settings {
@@ -42,9 +38,8 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
};
DirectCompositionSurfaceWin(
- std::unique_ptr<gfx::VSyncProvider> vsync_provider,
- VSyncCallback vsync_callback,
HWND parent_window,
+ VSyncCallback vsync_callback,
const DirectCompositionSurfaceWin::Settings& settings);
// Returns true if direct composition is supported. We prefer to use direct
@@ -60,6 +55,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
// Returns true if zero copy decode swap chain is supported.
static bool IsDecodeSwapChainSupported();
+ static void DisableDecodeSwapChain();
// After this is called, overlay support is disabled during the
// current GPU process' lifetime.
@@ -72,7 +68,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
static bool AreScaledOverlaysSupported();
// Returns preferred overlay format set when detecting overlay support.
- static DXGI_FORMAT GetOverlayFormatUsed();
+ static DXGI_FORMAT GetOverlayFormatUsedForSDR();
// Returns monitor size.
static gfx::Size GetOverlayMonitorSize();
@@ -80,6 +76,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
// Returns overlay support flags for the given format.
// Caller should check for DXGI_OVERLAY_SUPPORT_FLAG_DIRECT and
// DXGI_OVERLAY_SUPPORT_FLAG_SCALING bits.
+ // This function is thread safe.
static UINT GetOverlaySupportFlags(DXGI_FORMAT format);
// Returns true if there is an HDR capable display connected.
@@ -97,6 +94,11 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
static void SetOverlayHDRGpuInfoUpdateCallback(
OverlayHDRInfoUpdateCallback callback);
+ // On Intel GPUs where YUV overlays are supported, BGRA8 overlays are
+ // supported as well but IDXGIOutput3::CheckOverlaySupport() returns
+ // unsupported. So allow manually enabling BGRA8 overlay support.
+ static void EnableBGRA8OverlaysWithYUVOverlaySupport();
+
// GLSurfaceEGL implementation.
bool Initialize(GLSurfaceFormat format) override;
void Destroy() override;
@@ -131,9 +133,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
// scheduled with ScheduleDCLayer, as it's automatically placed in the layer
// tree at z-order 0.
bool ScheduleDCLayer(const ui::DCRendererLayerParams& params) override;
-
- // VSyncObserver implementation.
- void OnVSync(base::TimeTicks vsync_time, base::TimeDelta interval) override;
+ void SetFrameRate(float frame_rate) override;
// Implements GpuSwitchingObserver.
void OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) override;
@@ -157,57 +157,15 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
~DirectCompositionSurfaceWin() override;
private:
- struct PendingFrame {
- PendingFrame(Microsoft::WRL::ComPtr<ID3D11Query> query,
- PresentationCallback callback);
- PendingFrame(PendingFrame&& other);
- ~PendingFrame();
- PendingFrame& operator=(PendingFrame&& other);
-
- // Event query issued after frame is presented.
- Microsoft::WRL::ComPtr<ID3D11Query> query;
-
- // Presentation callback enqueued in SwapBuffers().
- PresentationCallback callback;
- };
-
- void EnqueuePendingFrame(PresentationCallback callback);
- void CheckPendingFrames();
-
- bool VSyncCallbackEnabled() const;
-
- void StartOrStopVSyncThread();
- void HandleVSyncOnMainThread(base::TimeTicks vsync_time,
- base::TimeDelta interval);
-
HWND window_ = nullptr;
ChildWindowWin child_window_;
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_refptr<DirectCompositionChildSurfaceWin> root_surface_;
std::unique_ptr<DCLayerTree> layer_tree_;
- std::unique_ptr<GLSurfacePresentationHelper> presentation_helper_;
-
- std::unique_ptr<gfx::VSyncProvider> vsync_provider_;
-
- const VSyncCallback vsync_callback_;
- mutable base::Lock vsync_callback_lock_;
- bool GUARDED_BY(vsync_callback_lock_) vsync_callback_enabled_ = false;
- VSyncThreadWin* vsync_thread_ = nullptr;
-
- base::TimeTicks last_vsync_time_;
- base::TimeDelta last_vsync_interval_;
-
- // Queue of pending presentation callbacks.
- base::circular_deque<PendingFrame> pending_frames_;
- const size_t max_pending_frames_;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice2> dcomp_device_;
- base::WeakPtr<DirectCompositionSurfaceWin> weak_ptr_;
- base::WeakPtrFactory<DirectCompositionSurfaceWin> weak_factory_{this};
-
DISALLOW_COPY_AND_ASSIGN(DirectCompositionSurfaceWin);
};
diff --git a/chromium/ui/gl/direct_composition_surface_win_unittest.cc b/chromium/ui/gl/direct_composition_surface_win_unittest.cc
index 9c03f576b29..01e8103b5df 100644
--- a/chromium/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/chromium/ui/gl/direct_composition_surface_win_unittest.cc
@@ -46,6 +46,7 @@ class TestPlatformDelegate : public ui::PlatformWindowDelegate {
void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
void OnLostCapture() override {}
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {}
+ void OnWillDestroyAcceleratedWidget() override {}
void OnAcceleratedWidgetDestroyed() override {}
void OnActivationChanged(bool active) override {}
void OnMouseEnter() override {}
@@ -140,8 +141,7 @@ class DirectCompositionSurfaceTest : public testing::Test {
DirectCompositionSurfaceWin::Settings settings;
scoped_refptr<DirectCompositionSurfaceWin> surface =
base::MakeRefCounted<DirectCompositionSurfaceWin>(
- /*vsync_provider=*/nullptr,
- DirectCompositionSurfaceWin::VSyncCallback(), parent_window_,
+ parent_window_, DirectCompositionSurfaceWin::VSyncCallback(),
settings);
EXPECT_TRUE(surface->Initialize(GLSurfaceFormat()));
diff --git a/chromium/ui/gl/features.gni b/chromium/ui/gl/features.gni
index cb2c986495d..8dbbe74fe58 100644
--- a/chromium/ui/gl/features.gni
+++ b/chromium/ui/gl/features.gni
@@ -9,7 +9,7 @@ declare_args() {
# Should EGL support be compiled?
# Can be overriden to test during bring up of EGL support on other platforms.
- use_egl = is_win || is_android || is_linux || is_fuchsia || is_mac
+ use_egl = is_win || is_android || is_linux || is_chromeos || is_fuchsia || is_mac
# Should Dawn support be compiled to back the WebGPU implementation?
# Also controls linking Dawn depedencies in such as SPIRV-Tools/SPIRV-Cross.
@@ -17,11 +17,11 @@ declare_args() {
# Should Dawn test binaries (unittests, end2end_tests, perf_tests) be built?
# Independent of use_dawn, which controls whether Dawn is used in Chromium.
- build_dawn_tests = is_mac || is_win || is_linux
+ build_dawn_tests = is_mac || is_win || is_linux || is_chromeos
}
declare_args() {
- enable_swiftshader = (is_win || is_linux || (is_mac && use_egl) ||
+ enable_swiftshader = (is_win || is_linux || is_chromeos || (is_mac && use_egl) ||
is_chromeos || is_fuchsia) &&
(target_cpu == "x86" || target_cpu == "x64" ||
target_cpu == "arm" || target_cpu == "arm64" ||
diff --git a/chromium/ui/gl/generate_bindings.py b/chromium/ui/gl/generate_bindings.py
index 4b02fa80d3d..9d0ee030843 100755
--- a/chromium/ui/gl/generate_bindings.py
+++ b/chromium/ui/gl/generate_bindings.py
@@ -1564,7 +1564,10 @@ GL_FUNCTIONS = [
'extensions': ['GL_KHR_debug'] }],
'arguments': 'void* ptr, GLsizei length, const char* label', },
{ 'return_type': 'void',
- 'names': ['glPatchParameteri'],
+ 'versions': [{ 'name': 'glPatchParameteri',
+ 'extensions': ['GL_ARB_tessellation_shader'] },
+ { 'name': 'glPatchParameteriOES',
+ 'extensions': ['GL_OES_tessellation_shader'] }],
'arguments': 'GLenum pname, GLint value', },
{ 'return_type': 'void',
'names': ['glPathCommandsNV'],
@@ -2116,7 +2119,14 @@ GL_FUNCTIONS = [
{ 'return_type': 'void',
'names': [ 'glTexStorageMem2DEXT'] ,
'arguments': 'GLenum target, GLsizei levels, GLenum internalFormat, '
- 'GLsizei width, GLsizei height, GLuint memory, GLuint64 offset'},
+ 'GLsizei width, GLsizei height, GLuint memory, GLuint64 offset', },
+{ 'return_type': 'void',
+ 'names': [ 'glTexStorageMemFlags2DANGLE'] ,
+ 'versions': [{ 'name': 'glTexStorageMemFlags2DANGLE',
+ 'extensions': ['GL_ANGLE_memory_object_flags'] }],
+ 'arguments': 'GLenum target, GLsizei levels, GLenum internalFormat, '
+ 'GLsizei width, GLsizei height, GLuint memory, GLuint64 offset, '
+ 'GLbitfield createFlags, GLbitfield usageFlags', },
{ 'return_type': 'void',
'names': ['glTexSubImage2D'],
'arguments':
diff --git a/chromium/ui/gl/gl_angle_util_win.cc b/chromium/ui/gl/gl_angle_util_win.cc
index 374c279dc8b..ef04c3977bd 100644
--- a/chromium/ui/gl/gl_angle_util_win.cc
+++ b/chromium/ui/gl/gl_angle_util_win.cc
@@ -100,8 +100,9 @@ Microsoft::WRL::ComPtr<IDCompositionDevice2> QueryDirectCompositionDevice(
return dcomp_device;
UINT data_size = sizeof(dcomp_device.Get());
- HRESULT hr = d3d11_device->GetPrivateData(kDirectCompositionGUID, &data_size,
- dcomp_device.GetAddressOf());
+ HRESULT hr =
+ d3d11_device->GetPrivateData(kDirectCompositionGUID, &data_size,
+ dcomp_device.ReleaseAndGetAddressOf());
if (SUCCEEDED(hr) && dcomp_device)
return dcomp_device;
@@ -119,14 +120,13 @@ Microsoft::WRL::ComPtr<IDCompositionDevice2> QueryDirectCompositionDevice(
return dcomp_device;
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
- d3d11_device.CopyTo(dxgi_device.GetAddressOf());
+ d3d11_device.As(&dxgi_device);
Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
- hr = create_device_function(dxgi_device.Get(),
- IID_PPV_ARGS(desktop_device.GetAddressOf()));
+ hr = create_device_function(dxgi_device.Get(), IID_PPV_ARGS(&desktop_device));
if (FAILED(hr))
return dcomp_device;
- hr = desktop_device.CopyTo(dcomp_device.GetAddressOf());
+ hr = desktop_device.As(&dcomp_device);
CHECK(SUCCEEDED(hr));
d3d11_device->SetPrivateDataInterface(kDirectCompositionGUID,
dcomp_device.Get());
diff --git a/chromium/ui/gl/gl_bindings.h b/chromium/ui/gl/gl_bindings.h
index 85e92fc5865..d6a02030a33 100644
--- a/chromium/ui/gl/gl_bindings.h
+++ b/chromium/ui/gl/gl_bindings.h
@@ -45,7 +45,7 @@
// The standard OpenGL native extension headers are also included.
#if defined(OS_WIN)
#include <GL/wglext.h>
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
#include <OpenGL/OpenGL.h>
#elif defined(USE_GLX)
#include <GL/glx.h>
@@ -469,6 +469,11 @@
#define GL_CONFORMANT_NV 0x9374
#endif /* GL_NV_internalformat_sample_query */
+#ifndef GL_EXT_YUV_target
+#define GL_EXT_YUV_target 1
+#define GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT 0x8BE7
+#endif /* GL_EXT_YUV_target */
+
#define GL_GLEXT_PROTOTYPES 1
#if defined(OS_WIN)
diff --git a/chromium/ui/gl/gl_bindings_api_autogen_gl.h b/chromium/ui/gl/gl_bindings_api_autogen_gl.h
index 102e3f99a87..0ec7b3e5f68 100644
--- a/chromium/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/chromium/ui/gl/gl_bindings_api_autogen_gl.h
@@ -1447,6 +1447,15 @@ void glTexStorageMem2DEXTFn(GLenum target,
GLsizei height,
GLuint memory,
GLuint64 offset) override;
+void glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) override;
void glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.cc b/chromium/ui/gl/gl_bindings_autogen_gl.cc
index 96a8926f7cb..a9c9d7a3e84 100644
--- a/chromium/ui/gl/gl_bindings_autogen_gl.cc
+++ b/chromium/ui/gl/gl_bindings_autogen_gl.cc
@@ -289,6 +289,8 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver,
gfx::HasExtension(extensions, "GL_ANGLE_framebuffer_multisample");
ext.b_GL_ANGLE_instanced_arrays =
gfx::HasExtension(extensions, "GL_ANGLE_instanced_arrays");
+ ext.b_GL_ANGLE_memory_object_flags =
+ gfx::HasExtension(extensions, "GL_ANGLE_memory_object_flags");
ext.b_GL_ANGLE_memory_object_fuchsia =
gfx::HasExtension(extensions, "GL_ANGLE_memory_object_fuchsia");
ext.b_GL_ANGLE_multi_draw =
@@ -339,6 +341,8 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver,
ext.b_GL_ARB_shader_image_load_store =
gfx::HasExtension(extensions, "GL_ARB_shader_image_load_store");
ext.b_GL_ARB_sync = gfx::HasExtension(extensions, "GL_ARB_sync");
+ ext.b_GL_ARB_tessellation_shader =
+ gfx::HasExtension(extensions, "GL_ARB_tessellation_shader");
ext.b_GL_ARB_texture_multisample =
gfx::HasExtension(extensions, "GL_ARB_texture_multisample");
ext.b_GL_ARB_texture_storage =
@@ -452,6 +456,8 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver,
ext.b_GL_OES_get_program_binary =
gfx::HasExtension(extensions, "GL_OES_get_program_binary");
ext.b_GL_OES_mapbuffer = gfx::HasExtension(extensions, "GL_OES_mapbuffer");
+ ext.b_GL_OES_tessellation_shader =
+ gfx::HasExtension(extensions, "GL_OES_tessellation_shader");
ext.b_GL_OES_texture_buffer =
gfx::HasExtension(extensions, "GL_OES_texture_buffer");
ext.b_GL_OES_vertex_array_object =
@@ -2173,9 +2179,13 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver,
GetGLProcAddress("glObjectPtrLabelKHR"));
}
- if (ver->IsAtLeastGLES(3u, 2u) || ver->IsAtLeastGL(4u, 0u)) {
+ if (ver->IsAtLeastGLES(3u, 2u) || ver->IsAtLeastGL(4u, 0u) ||
+ ext.b_GL_ARB_tessellation_shader) {
fn.glPatchParameteriFn = reinterpret_cast<glPatchParameteriProc>(
GetGLProcAddress("glPatchParameteri"));
+ } else if (ext.b_GL_OES_tessellation_shader) {
+ fn.glPatchParameteriFn = reinterpret_cast<glPatchParameteriProc>(
+ GetGLProcAddress("glPatchParameteriOES"));
}
if (ext.b_GL_NV_path_rendering) {
@@ -2803,6 +2813,12 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver,
GetGLProcAddress("glTexStorageMem2DEXT"));
}
+ if (ext.b_GL_ANGLE_memory_object_flags) {
+ fn.glTexStorageMemFlags2DANGLEFn =
+ reinterpret_cast<glTexStorageMemFlags2DANGLEProc>(
+ GetGLProcAddress("glTexStorageMemFlags2DANGLE"));
+ }
+
if (ext.b_GL_ANGLE_robust_client_memory) {
fn.glTexSubImage2DRobustANGLEFn =
reinterpret_cast<glTexSubImage2DRobustANGLEProc>(
@@ -5932,6 +5948,20 @@ void GLApiBase::glTexStorageMem2DEXTFn(GLenum target,
height, memory, offset);
}
+void GLApiBase::glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ driver_->fn.glTexStorageMemFlags2DANGLEFn(target, levels, internalFormat,
+ width, height, memory, offset,
+ createFlags, usageFlags);
+}
+
void GLApiBase::glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
@@ -9799,6 +9829,22 @@ void TraceGLApi::glTexStorageMem2DEXTFn(GLenum target,
memory, offset);
}
+void TraceGLApi::glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ TRACE_EVENT_BINARY_EFFICIENT0("gpu",
+ "TraceGLAPI::glTexStorageMemFlags2DANGLE")
+ gl_api_->glTexStorageMemFlags2DANGLEFn(target, levels, internalFormat, width,
+ height, memory, offset, createFlags,
+ usageFlags);
+}
+
void TraceGLApi::glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
@@ -14815,6 +14861,25 @@ void LogGLApi::glTexStorageMem2DEXTFn(GLenum target,
memory, offset);
}
+void LogGLApi::glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ GL_SERVICE_LOG("glTexStorageMemFlags2DANGLE"
+ << "(" << GLEnums::GetStringEnum(target) << ", " << levels
+ << ", " << GLEnums::GetStringEnum(internalFormat) << ", "
+ << width << ", " << height << ", " << memory << ", " << offset
+ << ", " << createFlags << ", " << usageFlags << ")");
+ gl_api_->glTexStorageMemFlags2DANGLEFn(target, levels, internalFormat, width,
+ height, memory, offset, createFlags,
+ usageFlags);
+}
+
void LogGLApi::glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
@@ -18321,6 +18386,18 @@ void NoContextGLApi::glTexStorageMem2DEXTFn(GLenum target,
NoContextHelper("glTexStorageMem2DEXT");
}
+void NoContextGLApi::glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ NoContextHelper("glTexStorageMemFlags2DANGLE");
+}
+
void NoContextGLApi::glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.h b/chromium/ui/gl/gl_bindings_autogen_gl.h
index 76825eda5f4..e28fa48646d 100644
--- a/chromium/ui/gl/gl_bindings_autogen_gl.h
+++ b/chromium/ui/gl/gl_bindings_autogen_gl.h
@@ -1688,6 +1688,16 @@ typedef void(GL_BINDING_CALL* glTexStorageMem2DEXTProc)(GLenum target,
GLsizei height,
GLuint memory,
GLuint64 offset);
+typedef void(GL_BINDING_CALL* glTexStorageMemFlags2DANGLEProc)(
+ GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags);
typedef void(GL_BINDING_CALL* glTexSubImage2DProc)(GLenum target,
GLint level,
GLint xoffset,
@@ -1946,6 +1956,7 @@ struct ExtensionsGL {
bool b_GL_ANGLE_framebuffer_blit;
bool b_GL_ANGLE_framebuffer_multisample;
bool b_GL_ANGLE_instanced_arrays;
+ bool b_GL_ANGLE_memory_object_flags;
bool b_GL_ANGLE_memory_object_fuchsia;
bool b_GL_ANGLE_multi_draw;
bool b_GL_ANGLE_request_extension;
@@ -1973,6 +1984,7 @@ struct ExtensionsGL {
bool b_GL_ARB_sampler_objects;
bool b_GL_ARB_shader_image_load_store;
bool b_GL_ARB_sync;
+ bool b_GL_ARB_tessellation_shader;
bool b_GL_ARB_texture_multisample;
bool b_GL_ARB_texture_storage;
bool b_GL_ARB_texture_swizzle;
@@ -2033,6 +2045,7 @@ struct ExtensionsGL {
bool b_GL_OES_draw_buffers_indexed;
bool b_GL_OES_get_program_binary;
bool b_GL_OES_mapbuffer;
+ bool b_GL_OES_tessellation_shader;
bool b_GL_OES_texture_buffer;
bool b_GL_OES_vertex_array_object;
bool b_GL_OVR_multiview;
@@ -2493,6 +2506,7 @@ struct ProcsGL {
glTexStorage2DMultisampleProc glTexStorage2DMultisampleFn;
glTexStorage3DProc glTexStorage3DFn;
glTexStorageMem2DEXTProc glTexStorageMem2DEXTFn;
+ glTexStorageMemFlags2DANGLEProc glTexStorageMemFlags2DANGLEFn;
glTexSubImage2DProc glTexSubImage2DFn;
glTexSubImage2DRobustANGLEProc glTexSubImage2DRobustANGLEFn;
glTexSubImage3DProc glTexSubImage3DFn;
@@ -4059,6 +4073,15 @@ class GL_EXPORT GLApi {
GLsizei height,
GLuint memory,
GLuint64 offset) = 0;
+ virtual void glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) = 0;
virtual void glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
@@ -4926,6 +4949,8 @@ class GL_EXPORT GLApi {
::gl::g_current_gl_context->glTexStorage2DMultisampleFn
#define glTexStorage3D ::gl::g_current_gl_context->glTexStorage3DFn
#define glTexStorageMem2DEXT ::gl::g_current_gl_context->glTexStorageMem2DEXTFn
+#define glTexStorageMemFlags2DANGLE \
+ ::gl::g_current_gl_context->glTexStorageMemFlags2DANGLEFn
#define glTexSubImage2D ::gl::g_current_gl_context->glTexSubImage2DFn
#define glTexSubImage2DRobustANGLE \
::gl::g_current_gl_context->glTexSubImage2DRobustANGLEFn
diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.cc b/chromium/ui/gl/gl_bindings_autogen_mock.cc
index afd8a2a6809..27ce4e6cc9c 100644
--- a/chromium/ui/gl/gl_bindings_autogen_mock.cc
+++ b/chromium/ui/gl/gl_bindings_autogen_mock.cc
@@ -3460,6 +3460,12 @@ void GL_BINDING_CALL MockGLInterface::Mock_glPatchParameteri(GLenum pname,
interface_->PatchParameteri(pname, value);
}
+void GL_BINDING_CALL MockGLInterface::Mock_glPatchParameteriOES(GLenum pname,
+ GLint value) {
+ MakeGlMockFunctionUnique("glPatchParameteriOES");
+ interface_->PatchParameteri(pname, value);
+}
+
void GL_BINDING_CALL
MockGLInterface::Mock_glPathCommandsCHROMIUM(GLuint path,
GLsizei numCommands,
@@ -4746,6 +4752,22 @@ MockGLInterface::Mock_glTexStorageMem2DEXT(GLenum target,
memory, offset);
}
+void GL_BINDING_CALL
+MockGLInterface::Mock_glTexStorageMemFlags2DANGLE(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ MakeGlMockFunctionUnique("glTexStorageMemFlags2DANGLE");
+ interface_->TexStorageMemFlags2DANGLE(target, levels, internalFormat, width,
+ height, memory, offset, createFlags,
+ usageFlags);
+}
+
void GL_BINDING_CALL MockGLInterface::Mock_glTexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
@@ -6342,6 +6364,8 @@ MockGLInterface::GetGLProcAddress(const char* name) {
return reinterpret_cast<GLFunctionPointerType>(Mock_glObjectPtrLabelKHR);
if (strcmp(name, "glPatchParameteri") == 0)
return reinterpret_cast<GLFunctionPointerType>(Mock_glPatchParameteri);
+ if (strcmp(name, "glPatchParameteriOES") == 0)
+ return reinterpret_cast<GLFunctionPointerType>(Mock_glPatchParameteriOES);
if (strcmp(name, "glPathCommandsCHROMIUM") == 0)
return reinterpret_cast<GLFunctionPointerType>(Mock_glPathCommandsCHROMIUM);
if (strcmp(name, "glPathCommandsNV") == 0)
@@ -6673,6 +6697,9 @@ MockGLInterface::GetGLProcAddress(const char* name) {
return reinterpret_cast<GLFunctionPointerType>(Mock_glTexStorage3D);
if (strcmp(name, "glTexStorageMem2DEXT") == 0)
return reinterpret_cast<GLFunctionPointerType>(Mock_glTexStorageMem2DEXT);
+ if (strcmp(name, "glTexStorageMemFlags2DANGLE") == 0)
+ return reinterpret_cast<GLFunctionPointerType>(
+ Mock_glTexStorageMemFlags2DANGLE);
if (strcmp(name, "glTexSubImage2D") == 0)
return reinterpret_cast<GLFunctionPointerType>(Mock_glTexSubImage2D);
if (strcmp(name, "glTexSubImage2DRobustANGLE") == 0)
diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.h b/chromium/ui/gl/gl_bindings_autogen_mock.h
index 304adb20e58..39f9c8eb61e 100644
--- a/chromium/ui/gl/gl_bindings_autogen_mock.h
+++ b/chromium/ui/gl/gl_bindings_autogen_mock.h
@@ -1462,6 +1462,8 @@ static void GL_BINDING_CALL Mock_glObjectPtrLabelKHR(void* ptr,
GLsizei length,
const char* label);
static void GL_BINDING_CALL Mock_glPatchParameteri(GLenum pname, GLint value);
+static void GL_BINDING_CALL Mock_glPatchParameteriOES(GLenum pname,
+ GLint value);
static void GL_BINDING_CALL Mock_glPathCommandsCHROMIUM(GLuint path,
GLsizei numCommands,
const GLubyte* commands,
@@ -2086,6 +2088,16 @@ static void GL_BINDING_CALL Mock_glTexStorageMem2DEXT(GLenum target,
GLsizei height,
GLuint memory,
GLuint64 offset);
+static void GL_BINDING_CALL
+Mock_glTexStorageMemFlags2DANGLE(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags);
static void GL_BINDING_CALL Mock_glTexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
diff --git a/chromium/ui/gl/gl_context.cc b/chromium/ui/gl/gl_context.cc
index 60c4665776f..428b5ad08d8 100644
--- a/chromium/ui/gl/gl_context.cc
+++ b/chromium/ui/gl/gl_context.cc
@@ -72,7 +72,7 @@ GLContext::GLContext(GLShareGroup* share_group) : share_group_(share_group) {
}
GLContext::~GLContext() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
DCHECK(!HasBackpressureFences());
#endif
share_group_->RemoveContext(this);
@@ -122,6 +122,12 @@ GpuPreference GLContext::AdjustGpuPreference(GpuPreference gpu_preference) {
}
}
+bool GLContext::MakeCurrent(GLSurface* surface) {
+ if (context_lost_)
+ return false;
+ return MakeCurrentImpl(surface);
+}
+
GLApi* GLContext::CreateGLApi(DriverGL* driver) {
real_gl_api_ = new RealGLApi;
real_gl_api_->set_gl_workarounds(gl_workarounds_);
@@ -197,7 +203,7 @@ void GLContext::DirtyVirtualContextState() {
current_virtual_context_ = nullptr;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
constexpr uint64_t kInvalidFenceId = 0;
uint64_t GLContext::BackpressureFenceCreate() {
@@ -365,6 +371,13 @@ void GLContext::SetGLStateRestorer(GLStateRestorer* state_restorer) {
}
GLenum GLContext::CheckStickyGraphicsResetStatus() {
+ GLenum status = CheckStickyGraphicsResetStatusImpl();
+ if (status != GL_NO_ERROR)
+ context_lost_ = true;
+ return status;
+}
+
+GLenum GLContext::CheckStickyGraphicsResetStatusImpl() {
DCHECK(IsCurrent(nullptr));
return GL_NO_ERROR;
}
@@ -392,7 +405,7 @@ bool GLContext::MakeVirtuallyCurrent(
GLContext* virtual_context, GLSurface* surface) {
if (!ForceGpuSwitchIfNeeded())
return false;
- if (virtual_context_lost_)
+ if (context_lost_)
return false;
bool switched_real_contexts = GLContext::GetRealCurrent() != this;
@@ -404,7 +417,7 @@ bool GLContext::MakeVirtuallyCurrent(
if (switched_real_contexts || !current_surface ||
!virtual_context->IsCurrent(surface)) {
if (!MakeCurrent(surface)) {
- virtual_context_lost_ = true;
+ context_lost_ = true;
return false;
}
}
@@ -445,7 +458,7 @@ bool GLContext::MakeVirtuallyCurrent(
virtual_context->SetCurrent(surface);
if (!surface->OnMakeCurrent(virtual_context)) {
LOG(ERROR) << "Could not make GLSurface current.";
- virtual_context_lost_ = true;
+ context_lost_ = true;
return false;
}
return true;
diff --git a/chromium/ui/gl/gl_context.h b/chromium/ui/gl/gl_context.h
index a7dd9c470b1..47c9403aec3 100644
--- a/chromium/ui/gl/gl_context.h
+++ b/chromium/ui/gl/gl_context.h
@@ -82,11 +82,19 @@ struct GL_EXPORT GLContextAttribs {
bool bind_generates_resource = true;
bool webgl_compatibility_context = false;
bool global_texture_share_group = false;
+ bool global_semaphore_share_group = false;
bool robust_resource_initialization = false;
bool robust_buffer_access = false;
int client_major_es_version = 3;
int client_minor_es_version = 0;
bool can_skip_validation = false;
+
+ // If true, and if supported (for EGL, this requires the robustness
+ // extension), set the reset notification strategy to lose context on reset.
+ // This setting can be changed independently of robust_buffer_access.
+ // (True by default to match previous behavior.)
+ bool lose_context_on_reset = true;
+
ContextPriority context_priority = ContextPriorityMedium;
};
@@ -117,7 +125,7 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext>,
const GLContextAttribs& attribs) = 0;
// Makes the GL context and a surface current on the current thread.
- virtual bool MakeCurrent(GLSurface* surface) = 0;
+ bool MakeCurrent(GLSurface* surface);
// Releases this GL context and surface as current on the current thread.
virtual void ReleaseCurrent(GLSurface* surface) = 0;
@@ -182,7 +190,7 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext>,
// other than GL_NO_ERROR, that value is returned until the context is
// destroyed.
// The context must be current.
- virtual unsigned int CheckStickyGraphicsResetStatus();
+ unsigned int CheckStickyGraphicsResetStatus();
// Make this context current when used for context virtualization.
bool MakeVirtuallyCurrent(GLContext* virtual_context, GLSurface* surface);
@@ -222,7 +230,7 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext>,
// context is made current.
void DirtyVirtualContextState();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Create a fence for all work submitted to this context so far, and return a
// monotonically increasing handle to it. This returned handle never needs to
// be freed. This method is used to create backpressure to throttle GL work
@@ -266,19 +274,19 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext>,
// Returns the last real (non-virtual) GLContext made current.
static GLContext* GetRealCurrent();
+ virtual bool MakeCurrentImpl(GLSurface* surface) = 0;
+ virtual unsigned int CheckStickyGraphicsResetStatusImpl();
virtual void ResetExtensions() = 0;
GLApi* gl_api() { return gl_api_wrapper_->api(); }
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Child classes are responsible for calling DestroyBackpressureFences during
// their destruction while a context is current.
bool HasBackpressureFences() const;
void DestroyBackpressureFences();
#endif
- void MarkVirtualContextLost() { virtual_context_lost_ = true; }
-
private:
friend class base::RefCounted<GLContext>;
@@ -313,9 +321,9 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext>,
std::unique_ptr<GLVersionInfo> version_info_;
// This bit allows us to avoid virtual context state restoration in the case
// where this underlying context becomes lost. https://crbug.com/1061442
- bool virtual_context_lost_ = false;
+ bool context_lost_ = false;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
std::map<uint64_t, std::unique_ptr<GLFence>> backpressure_fences_;
uint64_t next_backpressure_fence_ = 0;
#endif
diff --git a/chromium/ui/gl/gl_context_cgl.cc b/chromium/ui/gl/gl_context_cgl.cc
index 28b926453c9..917e0c13ce0 100644
--- a/chromium/ui/gl/gl_context_cgl.cc
+++ b/chromium/ui/gl/gl_context_cgl.cc
@@ -210,7 +210,7 @@ YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter(
return yuv_to_rgb_converter.get();
}
-bool GLContextCGL::MakeCurrent(GLSurface* surface) {
+bool GLContextCGL::MakeCurrentImpl(GLSurface* surface) {
DCHECK(context_);
if (!ForceGpuSwitchIfNeeded())
diff --git a/chromium/ui/gl/gl_context_cgl.h b/chromium/ui/gl/gl_context_cgl.h
index 27386324728..de70cd90a48 100644
--- a/chromium/ui/gl/gl_context_cgl.h
+++ b/chromium/ui/gl/gl_context_cgl.h
@@ -26,7 +26,7 @@ class GL_EXPORT GLContextCGL : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override;
bool IsCurrent(GLSurface* surface) override;
void* GetHandle() override;
diff --git a/chromium/ui/gl/gl_context_egl.cc b/chromium/ui/gl/gl_context_egl.cc
index 329fbfbf938..52407ec9cb5 100644
--- a/chromium/ui/gl/gl_context_egl.cc
+++ b/chromium/ui/gl/gl_context_egl.cc
@@ -35,6 +35,11 @@
#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x33AF
#endif /* EGL_ANGLE_display_texture_share_group */
+#ifndef EGL_ANGLE_display_semaphore_share_group
+#define EGL_ANGLE_display_semaphore_share_group 1
+#define EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE 0x348D
+#endif /* EGL_ANGLE_display_semaphore_share_group */
+
#ifndef EGL_ANGLE_create_context_client_arrays
#define EGL_ANGLE_create_context_client_arrays 1
#define EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE 0x3452
@@ -128,9 +133,11 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface,
context_attributes.push_back(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
context_attributes.push_back(attribs.robust_buffer_access ? EGL_TRUE
: EGL_FALSE);
- context_attributes.push_back(
- EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
- context_attributes.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT);
+ if (attribs.lose_context_on_reset) {
+ context_attributes.push_back(
+ EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
+ context_attributes.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT);
+ }
} else {
// At some point we should require the presence of the robustness
// extension and remove this code path.
@@ -181,6 +188,14 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface,
DCHECK(!attribs.global_texture_share_group);
}
+ if (GLSurfaceEGL::IsDisplaySemaphoreShareGroupSupported()) {
+ context_attributes.push_back(EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE);
+ context_attributes.push_back(
+ attribs.global_semaphore_share_group ? EGL_TRUE : EGL_FALSE);
+ } else {
+ DCHECK(!attribs.global_semaphore_share_group);
+ }
+
if (GLSurfaceEGL::IsCreateContextClientArraysSupported()) {
// Disable client arrays if the context supports it
context_attributes.push_back(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE);
@@ -282,7 +297,7 @@ void GLContextEGL::SetVisibility(bool visibility) {
}
void GLContextEGL::ReleaseYUVToRGBConvertersAndBackpressureFences() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
bool has_backpressure_fences = HasBackpressureFences();
#else
bool has_backpressure_fences = false;
@@ -312,7 +327,7 @@ void GLContextEGL::ReleaseYUVToRGBConvertersAndBackpressureFences() {
}
yuv_to_rgb_converters_.clear();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
DestroyBackpressureFences();
#endif
@@ -331,7 +346,7 @@ void GLContextEGL::ReleaseYUVToRGBConvertersAndBackpressureFences() {
}
}
-bool GLContextEGL::MakeCurrent(GLSurface* surface) {
+bool GLContextEGL::MakeCurrentImpl(GLSurface* surface) {
DCHECK(context_);
if (lost_)
return false;
@@ -420,7 +435,7 @@ void* GLContextEGL::GetHandle() {
return context_;
}
-unsigned int GLContextEGL::CheckStickyGraphicsResetStatus() {
+unsigned int GLContextEGL::CheckStickyGraphicsResetStatusImpl() {
DCHECK(IsCurrent(nullptr));
DCHECK(g_current_gl_driver);
const ExtensionsGL& ext = g_current_gl_driver->ext;
diff --git a/chromium/ui/gl/gl_context_egl.h b/chromium/ui/gl/gl_context_egl.h
index ce18596b67d..0a95c783e10 100644
--- a/chromium/ui/gl/gl_context_egl.h
+++ b/chromium/ui/gl/gl_context_egl.h
@@ -29,11 +29,11 @@ class GL_EXPORT GLContextEGL : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override;
bool IsCurrent(GLSurface* surface) override;
void* GetHandle() override;
- unsigned int CheckStickyGraphicsResetStatus() override;
+ unsigned int CheckStickyGraphicsResetStatusImpl() override;
void SetUnbindFboOnMakeCurrent() override;
YUVToRGBConverter* GetYUVToRGBConverter(
const gfx::ColorSpace& color_space) override;
diff --git a/chromium/ui/gl/gl_context_glx.cc b/chromium/ui/gl/gl_context_glx.cc
index 59b3c0580b7..8042e11094f 100644
--- a/chromium/ui/gl/gl_context_glx.cc
+++ b/chromium/ui/gl/gl_context_glx.cc
@@ -212,7 +212,7 @@ void GLContextGLX::Destroy() {
}
}
-bool GLContextGLX::MakeCurrent(GLSurface* surface) {
+bool GLContextGLX::MakeCurrentImpl(GLSurface* surface) {
DCHECK(context_);
if (IsCurrent(surface))
return true;
@@ -278,7 +278,7 @@ void* GLContextGLX::GetHandle() {
return context_;
}
-unsigned int GLContextGLX::CheckStickyGraphicsResetStatus() {
+unsigned int GLContextGLX::CheckStickyGraphicsResetStatusImpl() {
DCHECK(IsCurrent(nullptr));
DCHECK(g_current_gl_driver);
const ExtensionsGL& ext = g_current_gl_driver->ext;
diff --git a/chromium/ui/gl/gl_context_glx.h b/chromium/ui/gl/gl_context_glx.h
index 47beb46b14c..b6e69d27901 100644
--- a/chromium/ui/gl/gl_context_glx.h
+++ b/chromium/ui/gl/gl_context_glx.h
@@ -28,11 +28,11 @@ class GL_EXPORT GLContextGLX : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override;
bool IsCurrent(GLSurface* surface) override;
void* GetHandle() override;
- unsigned int CheckStickyGraphicsResetStatus() override;
+ unsigned int CheckStickyGraphicsResetStatusImpl() override;
protected:
~GLContextGLX() override;
diff --git a/chromium/ui/gl/gl_context_glx_unittest.cc b/chromium/ui/gl/gl_context_glx_unittest.cc
index 5fc733ba5f0..32081e5107d 100644
--- a/chromium/ui/gl/gl_context_glx_unittest.cc
+++ b/chromium/ui/gl/gl_context_glx_unittest.cc
@@ -50,8 +50,8 @@ TEST(GLContextGLXTest, MAYBE_DoNotDestroyOnFailedMakeCurrent) {
XSync(xdisplay, x11::False);
GLImageTestSupport::InitializeGL(base::nullopt);
- auto surface =
- gl::InitializeGLSurface(base::MakeRefCounted<GLSurfaceGLXX11>(xwindow));
+ auto surface = gl::InitializeGLSurface(base::MakeRefCounted<GLSurfaceGLXX11>(
+ static_cast<gfx::AcceleratedWidget>(xwindow)));
scoped_refptr<GLContext> context =
gl::init::CreateGLContext(nullptr, surface.get(), GLContextAttribs());
diff --git a/chromium/ui/gl/gl_context_stub.cc b/chromium/ui/gl/gl_context_stub.cc
index 8c43dccde5f..56eca511b10 100644
--- a/chromium/ui/gl/gl_context_stub.cc
+++ b/chromium/ui/gl/gl_context_stub.cc
@@ -22,7 +22,7 @@ bool GLContextStub::Initialize(GLSurface* compatible_surface,
return true;
}
-bool GLContextStub::MakeCurrent(GLSurface* surface) {
+bool GLContextStub::MakeCurrentImpl(GLSurface* surface) {
DCHECK(surface);
BindGLApi();
SetCurrent(surface);
@@ -50,7 +50,7 @@ std::string GLContextStub::GetGLRenderer() {
return std::string("CHROMIUM");
}
-unsigned int GLContextStub::CheckStickyGraphicsResetStatus() {
+unsigned int GLContextStub::CheckStickyGraphicsResetStatusImpl() {
DCHECK(IsCurrent(nullptr));
if ((graphics_reset_status_ == GL_NO_ERROR) && HasRobustness()) {
graphics_reset_status_ = glGetGraphicsResetStatusARB();
@@ -75,7 +75,7 @@ bool GLContextStub::HasRobustness() {
HasExtension("GL_KHR_robustness") || HasExtension("GL_EXT_robustness");
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void GLContextStub::FlushForDriverCrashWorkaround() {}
#endif
diff --git a/chromium/ui/gl/gl_context_stub.h b/chromium/ui/gl/gl_context_stub.h
index 0a24ab04404..f3cfa8f704d 100644
--- a/chromium/ui/gl/gl_context_stub.h
+++ b/chromium/ui/gl/gl_context_stub.h
@@ -23,20 +23,20 @@ class GL_EXPORT GLContextStub : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override;
bool IsCurrent(GLSurface* surface) override;
void* GetHandle() override;
std::string GetGLVersion() override;
std::string GetGLRenderer() override;
- unsigned int CheckStickyGraphicsResetStatus() override;
+ unsigned int CheckStickyGraphicsResetStatusImpl() override;
void SetUseStubApi(bool stub_api);
void SetExtensionsString(const char* extensions);
void SetGLVersionString(const char* version_str);
bool HasRobustness();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void FlushForDriverCrashWorkaround() override;
#endif
diff --git a/chromium/ui/gl/gl_context_wgl.cc b/chromium/ui/gl/gl_context_wgl.cc
index bf36be7db7e..9e9633a496b 100644
--- a/chromium/ui/gl/gl_context_wgl.cc
+++ b/chromium/ui/gl/gl_context_wgl.cc
@@ -91,7 +91,7 @@ void GLContextWGL::Destroy() {
}
}
-bool GLContextWGL::MakeCurrent(GLSurface* surface) {
+bool GLContextWGL::MakeCurrentImpl(GLSurface* surface) {
DCHECK(context_);
if (IsCurrent(surface))
return true;
diff --git a/chromium/ui/gl/gl_context_wgl.h b/chromium/ui/gl/gl_context_wgl.h
index be43a5d3161..c792580efb2 100644
--- a/chromium/ui/gl/gl_context_wgl.h
+++ b/chromium/ui/gl/gl_context_wgl.h
@@ -24,7 +24,7 @@ class GL_EXPORT GLContextWGL : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override;
bool IsCurrent(GLSurface* surface) override;
void* GetHandle() override;
diff --git a/chromium/ui/gl/gl_enums_implementation_autogen.h b/chromium/ui/gl/gl_enums_implementation_autogen.h
index 1209923bb44..18362eef373 100644
--- a/chromium/ui/gl/gl_enums_implementation_autogen.h
+++ b/chromium/ui/gl/gl_enums_implementation_autogen.h
@@ -2685,6 +2685,10 @@ static const GLEnums::EnumToString enum_to_string_table[] = {
"GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM",
},
{
+ 0x8AF8,
+ "GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM",
+ },
+ {
0x8B30,
"GL_FRAGMENT_SHADER",
},
diff --git a/chromium/ui/gl/gl_features.cc b/chromium/ui/gl/gl_features.cc
index b59542f01d1..56ef4a3aa74 100644
--- a/chromium/ui/gl/gl_features.cc
+++ b/chromium/ui/gl/gl_features.cc
@@ -13,7 +13,8 @@ namespace features {
// Launched on Windows, still experimental on other platforms.
const base::Feature kDefaultPassthroughCommandDecoder{
"DefaultPassthroughCommandDecoder",
-#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(CHROMECAST_BUILD))
+#if defined(OS_WIN) || \
+ (defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(CHROMECAST_BUILD))
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/chromium/ui/gl/gl_fence.cc b/chromium/ui/gl/gl_fence.cc
index 71f0eea9595..8d3cde4fbb3 100644
--- a/chromium/ui/gl/gl_fence.cc
+++ b/chromium/ui/gl/gl_fence.cc
@@ -14,11 +14,11 @@
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_version_info.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/gl/gl_fence_apple.h"
#endif
-#if defined(USE_EGL) && defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(USE_EGL) && defined(OS_POSIX) && !defined(OS_APPLE)
#define USE_GL_FENCE_ANDROID_NATIVE_FENCE_SYNC
#include "ui/gl/gl_fence_android_native_fence_sync.h"
#include "ui/gl/gl_surface_egl.h"
@@ -37,7 +37,7 @@ bool GLFence::IsSupported() {
return g_current_gl_driver->ext.b_GL_ARB_sync ||
g_current_gl_version->is_es3 ||
g_current_gl_version->is_desktop_core_profile ||
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
g_current_gl_driver->ext.b_GL_APPLE_fence ||
#else
g_driver_egl.ext.b_EGL_KHR_fence_sync ||
@@ -50,7 +50,7 @@ std::unique_ptr<GLFence> GLFence::Create() {
<< "Trying to create fence with no context";
std::unique_ptr<GLFence> fence;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
if (g_driver_egl.ext.b_EGL_KHR_fence_sync &&
g_driver_egl.ext.b_EGL_KHR_wait_sync) {
// Prefer GLFenceEGL which doesn't require GL context switching.
@@ -63,7 +63,7 @@ std::unique_ptr<GLFence> GLFence::Create() {
g_current_gl_version->is_desktop_core_profile) {
// Prefer ARB_sync which supports server-side wait.
fence = std::make_unique<GLFenceARB>();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
} else if (g_current_gl_driver->ext.b_GL_APPLE_fence) {
fence = std::make_unique<GLFenceAPPLE>();
#else
diff --git a/chromium/ui/gl/gl_gl_api_implementation.cc b/chromium/ui/gl/gl_gl_api_implementation.cc
index aa43cde2ffb..2815f8e3f24 100644
--- a/chromium/ui/gl/gl_gl_api_implementation.cc
+++ b/chromium/ui/gl/gl_gl_api_implementation.cc
@@ -352,6 +352,33 @@ void RealGLApi::glTexStorage2DEXTFn(GLenum target,
height);
}
+void RealGLApi::glTexStorageMem2DEXTFn(GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset) {
+ internalformat = GetInternalFormat(version_.get(), internalformat);
+ GLApiBase::glTexStorageMem2DEXTFn(target, levels, internalformat, width,
+ height, memory, offset);
+}
+
+void RealGLApi::glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) {
+ internalformat = GetInternalFormat(version_.get(), internalformat);
+ GLApiBase::glTexStorageMemFlags2DANGLEFn(target, levels, internalformat,
+ width, height, memory, offset,
+ createFlags, usageFlags);
+}
+
void RealGLApi::glRenderbufferStorageEXTFn(GLenum target,
GLenum internalformat,
GLsizei width,
diff --git a/chromium/ui/gl/gl_gl_api_implementation.h b/chromium/ui/gl/gl_gl_api_implementation.h
index c4f10613a65..13ee6343922 100644
--- a/chromium/ui/gl/gl_gl_api_implementation.h
+++ b/chromium/ui/gl/gl_gl_api_implementation.h
@@ -81,6 +81,24 @@ class GL_EXPORT RealGLApi : public GLApiBase {
GLsizei width,
GLsizei height) override;
+ void glTexStorageMem2DEXTFn(GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset) override;
+
+ void glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) override;
+
void glRenderbufferStorageEXTFn(GLenum target,
GLenum internalformat,
GLsizei width,
diff --git a/chromium/ui/gl/gl_image.cc b/chromium/ui/gl/gl_image.cc
index 12388ec21b8..768a48f66a6 100644
--- a/chromium/ui/gl/gl_image.cc
+++ b/chromium/ui/gl/gl_image.cc
@@ -114,6 +114,14 @@ bool GLImage::EmulatingRGB() const {
return false;
}
+bool GLImage::IsInUseByWindowServer() const {
+ return false;
+}
+
+void GLImage::DisableInUseByWindowServer() {
+ NOTIMPLEMENTED();
+}
+
GLImage::Type GLImage::GetType() const {
return Type::NONE;
}
@@ -123,10 +131,6 @@ std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
GLImage::GetAHardwareBuffer() {
return nullptr;
}
-
-gfx::Rect GLImage::GetCropRect() {
- return gfx::Rect();
-}
#endif
bool GLImage::HasMutableState() const {
diff --git a/chromium/ui/gl/gl_image.h b/chromium/ui/gl/gl_image.h
index 2df15a9428a..70581952b6a 100644
--- a/chromium/ui/gl/gl_image.h
+++ b/chromium/ui/gl/gl_image.h
@@ -130,6 +130,13 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> {
// removed. https://crbug.com/581777#c36
virtual bool EmulatingRGB() const;
+ // Return true if the macOS WindowServer is currently using the underlying
+ // storage for the image.
+ virtual bool IsInUseByWindowServer() const;
+
+ // If called, then IsInUseByWindowServer will always return false.
+ virtual void DisableInUseByWindowServer();
+
#if defined(OS_ANDROID)
// Provides the buffer backing this image, if it is backed by an
// AHardwareBuffer. The ScopedHardwareBuffer returned may include a fence
@@ -139,10 +146,6 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> {
// returned.
virtual std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
GetAHardwareBuffer();
-
- // Provides the crop rectangle associated with the image. The crop rectangle
- // specifies the region of valid pixels in the image.
- virtual gfx::Rect GetCropRect();
#endif
// An identifier for subclasses. Necessary for safe downcasting.
diff --git a/chromium/ui/gl/gl_image_dxgi.cc b/chromium/ui/gl/gl_image_dxgi.cc
index 5a912d2fad4..5285918935d 100644
--- a/chromium/ui/gl/gl_image_dxgi.cc
+++ b/chromium/ui/gl/gl_image_dxgi.cc
@@ -247,18 +247,18 @@ bool GLImageDXGI::InitializeHandle(base::win::ScopedHandle handle,
return false;
Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device1;
- if (FAILED(d3d11_device.CopyTo(d3d11_device1.GetAddressOf())))
+ if (FAILED(d3d11_device.As(&d3d11_device1)))
return false;
- if (FAILED(d3d11_device1->OpenSharedResource1(
- handle.Get(), IID_PPV_ARGS(texture_.GetAddressOf())))) {
+ if (FAILED(d3d11_device1->OpenSharedResource1(handle.Get(),
+ IID_PPV_ARGS(&texture_)))) {
return false;
}
D3D11_TEXTURE2D_DESC desc;
texture_->GetDesc(&desc);
if (desc.ArraySize <= level_)
return false;
- if (FAILED(texture_.CopyTo(keyed_mutex_.GetAddressOf())))
+ if (FAILED(texture_.As(&keyed_mutex_)))
return false;
handle_ = std::move(handle);
@@ -303,8 +303,8 @@ bool CopyingGLImageDXGI::Initialize() {
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
- HRESULT hr = d3d11_device_->CreateTexture2D(
- &desc, nullptr, decoder_copy_texture_.GetAddressOf());
+ HRESULT hr =
+ d3d11_device_->CreateTexture2D(&desc, nullptr, &decoder_copy_texture_);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateTexture2D failed: " << std::hex << hr;
return false;
@@ -328,14 +328,14 @@ bool CopyingGLImageDXGI::Initialize() {
return false;
}
- d3d11_device_.CopyTo(video_device_.GetAddressOf());
+ d3d11_device_.As(&video_device_);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
- d3d11_device_->GetImmediateContext(context.GetAddressOf());
- context.CopyTo(video_context_.GetAddressOf());
+ d3d11_device_->GetImmediateContext(&context);
+ context.As(&video_context_);
#if DCHECK_IS_ON()
Microsoft::WRL::ComPtr<ID3D10Multithread> multithread;
- d3d11_device_.CopyTo(multithread.GetAddressOf());
+ d3d11_device_.As(&multithread);
DCHECK(multithread->GetMultithreadProtected());
#endif // DCHECK_IS_ON()
@@ -348,7 +348,7 @@ bool CopyingGLImageDXGI::InitializeVideoProcessor(
output_view_.Reset();
Microsoft::WRL::ComPtr<ID3D11Device> processor_device;
- video_processor->GetDevice(processor_device.GetAddressOf());
+ video_processor->GetDevice(&processor_device);
DCHECK_EQ(d3d11_device_.Get(), processor_device.Get());
d3d11_processor_ = video_processor;
@@ -359,7 +359,7 @@ bool CopyingGLImageDXGI::InitializeVideoProcessor(
Microsoft::WRL::ComPtr<ID3D11VideoProcessorOutputView> output_view;
HRESULT hr = video_device_->CreateVideoProcessorOutputView(
decoder_copy_texture_.Get(), enumerator_.Get(), &output_view_desc,
- output_view_.GetAddressOf());
+ &output_view_);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get output view";
return false;
@@ -377,7 +377,7 @@ bool CopyingGLImageDXGI::BindTexImage(unsigned target) {
DCHECK(video_device_);
Microsoft::WRL::ComPtr<ID3D11Device> texture_device;
- texture_->GetDevice(texture_device.GetAddressOf());
+ texture_->GetDevice(&texture_device);
DCHECK_EQ(d3d11_device_.Get(), texture_device.Get());
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_view_desc = {0};
@@ -386,8 +386,7 @@ bool CopyingGLImageDXGI::BindTexImage(unsigned target) {
input_view_desc.Texture2D.MipSlice = 0;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorInputView> input_view;
HRESULT hr = video_device_->CreateVideoProcessorInputView(
- texture_.Get(), enumerator_.Get(), &input_view_desc,
- input_view.GetAddressOf());
+ texture_.Get(), enumerator_.Get(), &input_view_desc, &input_view);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to create video processor input view.";
return false;
diff --git a/chromium/ui/gl/gl_image_dxgi_unittest.cc b/chromium/ui/gl/gl_image_dxgi_unittest.cc
index 668e7860a51..4499b93fda2 100644
--- a/chromium/ui/gl/gl_image_dxgi_unittest.cc
+++ b/chromium/ui/gl/gl_image_dxgi_unittest.cc
@@ -54,12 +54,11 @@ class GLImageDXGITestDelegate : public GLImageTestDelegateBase {
QueryD3D11DeviceObjectFromANGLE();
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
- HRESULT hr = d3d11_device->CreateTexture2D(&desc, &initial_data,
- texture.GetAddressOf());
+ HRESULT hr = d3d11_device->CreateTexture2D(&desc, &initial_data, &texture);
EXPECT_HRESULT_SUCCEEDED(hr);
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
- hr = texture.CopyTo(dxgi_resource.GetAddressOf());
+ hr = texture.As(&dxgi_resource);
EXPECT_HRESULT_SUCCEEDED(hr);
HANDLE handle = nullptr;
diff --git a/chromium/ui/gl/gl_image_egl_pixmap.cc b/chromium/ui/gl/gl_image_egl_pixmap.cc
new file mode 100644
index 00000000000..b598ccd7ccd
--- /dev/null
+++ b/chromium/ui/gl/gl_image_egl_pixmap.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 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 "ui/gl/gl_image_egl_pixmap.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gl/buffer_format_utils.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_surface_glx.h"
+#include "ui/gl/gl_visual_picker_glx.h"
+
+namespace gl {
+
+inline EGLDisplay FromXDisplay() {
+#if defined(USE_X11)
+ if (auto* x_display = gfx::GetXDisplay())
+ return eglGetDisplay(x_display);
+#endif
+ return EGL_NO_DISPLAY;
+}
+
+GLImageEGLPixmap::GLImageEGLPixmap(const gfx::Size& size,
+ gfx::BufferFormat format)
+ : surface_(nullptr),
+ size_(size),
+ format_(format),
+ display_(FromXDisplay()) {}
+
+GLImageEGLPixmap::~GLImageEGLPixmap() {
+ if (surface_)
+ eglDestroySurface(display_, surface_);
+}
+
+bool GLImageEGLPixmap::Initialize(XID pixmap) {
+ if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE)
+ return false;
+
+ EGLint attribs[] = {EGL_BUFFER_SIZE,
+ 32,
+ EGL_ALPHA_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_RED_SIZE,
+ 8,
+ EGL_SURFACE_TYPE,
+ EGL_PIXMAP_BIT,
+ EGL_BIND_TO_TEXTURE_RGBA,
+ EGL_TRUE,
+ EGL_NONE};
+
+ EGLint num_configs;
+ EGLConfig config = nullptr;
+
+ if ((eglChooseConfig(display_, attribs, &config, 1, &num_configs) !=
+ EGL_TRUE) ||
+ !num_configs) {
+ return false;
+ }
+
+ std::vector<EGLint> attrs = {EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE};
+
+ surface_ = eglCreatePixmapSurface(display_, config, pixmap, attrs.data());
+ return surface_ != EGL_NO_SURFACE;
+}
+
+gfx::Size GLImageEGLPixmap::GetSize() {
+ return size_;
+}
+
+unsigned GLImageEGLPixmap::GetInternalFormat() {
+ return gl::BufferFormatToGLInternalFormat(format_);
+}
+
+unsigned GLImageEGLPixmap::GetDataType() {
+ return GL_UNSIGNED_BYTE;
+}
+
+GLImageEGLPixmap::BindOrCopy GLImageEGLPixmap::ShouldBindOrCopy() {
+ return BIND;
+}
+
+bool GLImageEGLPixmap::BindTexImage(unsigned target) {
+ if (!surface_)
+ return false;
+
+ // Requires TEXTURE_2D target.
+ if (target != GL_TEXTURE_2D)
+ return false;
+
+ if (eglBindTexImage(display_, surface_, EGL_BACK_BUFFER) != EGL_TRUE)
+ return false;
+
+ return true;
+}
+
+void GLImageEGLPixmap::ReleaseTexImage(unsigned target) {
+ DCHECK_NE(nullptr, surface_);
+ DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), target);
+
+ eglReleaseTexImage(display_, surface_, EGL_BACK_BUFFER);
+}
+
+void GLImageEGLPixmap::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
+ uint64_t process_tracing_id,
+ const std::string& dump_name) {
+ // TODO(crbug.com/514914): Implement GLImage OnMemoryDump.
+}
+
+} // namespace gl
diff --git a/chromium/ui/gl/gl_image_egl_pixmap.h b/chromium/ui/gl/gl_image_egl_pixmap.h
new file mode 100644
index 00000000000..caf37a5519f
--- /dev/null
+++ b/chromium/ui/gl/gl_image_egl_pixmap.h
@@ -0,0 +1,55 @@
+// Copyright (c) 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 UI_GL_GL_IMAGE_EGL_PIXMAP_H_
+#define UI_GL_GL_IMAGE_EGL_PIXMAP_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/x/x11_types.h"
+#include "ui/gl/gl_export.h"
+#include "ui/gl/gl_image.h"
+
+typedef void* EGLSurface;
+typedef void* EGLDisplay;
+
+namespace gl {
+
+class GL_EXPORT GLImageEGLPixmap : public GLImage {
+ public:
+ GLImageEGLPixmap(const gfx::Size& size, gfx::BufferFormat format);
+
+ bool Initialize(XID pixmap);
+
+ // Overridden from GLImage:
+ gfx::Size GetSize() override;
+ unsigned GetInternalFormat() override;
+ unsigned GetDataType() override;
+ BindOrCopy ShouldBindOrCopy() override;
+ bool BindTexImage(unsigned target) override;
+ void ReleaseTexImage(unsigned target) override;
+ void Flush() override {}
+ void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
+ uint64_t process_tracing_id,
+ const std::string& dump_name) override;
+
+ protected:
+ ~GLImageEGLPixmap() override;
+
+ gfx::BufferFormat format() const { return format_; }
+
+ private:
+ EGLSurface surface_;
+ const gfx::Size size_;
+ gfx::BufferFormat format_;
+ EGLDisplay display_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLImageEGLPixmap);
+};
+
+} // namespace gl
+
+#endif // UI_GL_GL_IMAGE_EGL_PIXMAP_H_
diff --git a/chromium/ui/gl/gl_image_io_surface.h b/chromium/ui/gl/gl_image_io_surface.h
index c9e3d69b81b..e78cb1a066e 100644
--- a/chromium/ui/gl/gl_image_io_surface.h
+++ b/chromium/ui/gl/gl_image_io_surface.h
@@ -69,15 +69,13 @@ class GL_EXPORT GLImageIOSurface : public GLImage {
uint64_t process_tracing_id,
const std::string& dump_name) override;
bool EmulatingRGB() const override;
+ bool IsInUseByWindowServer() const override;
+ void DisableInUseByWindowServer() override;
gfx::GenericSharedMemoryId io_surface_id() const { return io_surface_id_; }
base::ScopedCFTypeRef<IOSurfaceRef> io_surface();
base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer();
- // Whether checking IOSurfaceIsInUse() will actually provide a meaningful
- // signal about whether the Window Server is still using the IOSurface.
- bool CanCheckIOSurfaceIsInUse() const;
-
// For IOSurfaces that need manual conversion to a GL texture before being
// sampled from, specify the color space in which to do the required YUV to
// RGB transformation.
@@ -115,6 +113,8 @@ class GL_EXPORT GLImageIOSurface : public GLImage {
// The default value of Rec. 601 is based on historical shader code.
gfx::ColorSpace color_space_for_yuv_to_rgb_ = gfx::ColorSpace::CreateREC601();
+ bool disable_in_use_by_window_server_ = false;
+
DISALLOW_COPY_AND_ASSIGN(GLImageIOSurface);
};
diff --git a/chromium/ui/gl/gl_image_io_surface.mm b/chromium/ui/gl/gl_image_io_surface.mm
index 1e4324ff889..94b1fcdc26c 100644
--- a/chromium/ui/gl/gl_image_io_surface.mm
+++ b/chromium/ui/gl/gl_image_io_surface.mm
@@ -50,6 +50,7 @@ bool ValidInternalFormat(unsigned internalformat) {
case GL_RGB10_A2_EXT:
case GL_RGB_YCBCR_420V_CHROMIUM:
case GL_RGB_YCBCR_422_CHROMIUM:
+ case GL_RGB_YCBCR_P010_CHROMIUM:
case GL_RGBA:
return true;
default:
@@ -73,12 +74,13 @@ GLenum TextureFormat(gfx::BufferFormat format) {
return GL_RGBA;
case gfx::BufferFormat::YUV_420_BIPLANAR:
return GL_RGB_YCBCR_420V_CHROMIUM;
+ case gfx::BufferFormat::P010:
+ return GL_RGB_YCBCR_P010_CHROMIUM;
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_1010102:
case gfx::BufferFormat::YVU_420:
- case gfx::BufferFormat::P010:
NOTREACHED() << gfx::BufferFormatToString(format);
return 0;
}
@@ -246,10 +248,13 @@ unsigned GLImageIOSurface::GetDataType() {
}
GLImageIOSurface::BindOrCopy GLImageIOSurface::ShouldBindOrCopy() {
- // YUV_420_BIPLANAR is not supported by BindTexImage.
- // CopyTexImage is supported by this format as that performs conversion to RGB
- // as part of the copy operation.
- return format_ == gfx::BufferFormat::YUV_420_BIPLANAR ? COPY : BIND;
+ // YUV_420_BIPLANAR and P010 are not supported by BindTexImage. CopyTexImage
+ // is supported by these formats as that performs conversion to RGB as part of
+ // the copy operation.
+ return (format_ == gfx::BufferFormat::YUV_420_BIPLANAR ||
+ format_ == gfx::BufferFormat::P010)
+ ? COPY
+ : BIND;
}
bool GLImageIOSurface::BindTexImage(unsigned target) {
@@ -339,12 +344,15 @@ bool GLImageIOSurface::CopyTexImage(unsigned target) {
glBindTexture(target, rgb_texture);
})));
+ const auto src_type =
+ format_ == gfx::BufferFormat::P010 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
+
CGLContextObj cgl_context = CGLGetCurrentContext();
{
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->y_texture());
CGLError cgl_error = CGLTexImageIOSurface2D(
cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RED, size_.width(),
- size_.height(), GL_RED, GL_UNSIGNED_BYTE, io_surface_, 0);
+ size_.height(), GL_RED, src_type, io_surface_, 0);
if (cgl_error != kCGLNoError) {
LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. "
<< cgl_error;
@@ -355,7 +363,7 @@ bool GLImageIOSurface::CopyTexImage(unsigned target) {
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->uv_texture());
CGLError cgl_error = CGLTexImageIOSurface2D(
cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RG, size_.width() / 2,
- size_.height() / 2, GL_RG, GL_UNSIGNED_BYTE, io_surface_, 1);
+ size_.height() / 2, GL_RG, src_type, io_surface_, 1);
if (cgl_error != kCGLNoError) {
LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. "
<< cgl_error;
@@ -363,7 +371,7 @@ bool GLImageIOSurface::CopyTexImage(unsigned target) {
}
}
- yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture);
+ yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture, src_type);
return true;
}
@@ -429,8 +437,17 @@ bool GLImageIOSurface::EmulatingRGB() const {
return client_internalformat_ == GL_RGB;
}
-bool GLImageIOSurface::CanCheckIOSurfaceIsInUse() const {
- return !cv_pixel_buffer_;
+bool GLImageIOSurface::IsInUseByWindowServer() const {
+ // IOSurfaceIsInUse() will always return true if the IOSurface is wrapped in
+ // a CVPixelBuffer. Ignore the signal for such IOSurfaces (which are the ones
+ // output by hardware video decode).
+ if (disable_in_use_by_window_server_)
+ return false;
+ return IOSurfaceIsInUse(io_surface_.get());
+}
+
+void GLImageIOSurface::DisableInUseByWindowServer() {
+ disable_in_use_by_window_server_ = true;
}
void GLImageIOSurface::SetColorSpaceForYUVToRGBConversion(
@@ -497,6 +514,7 @@ bool GLImageIOSurface::ValidFormat(gfx::BufferFormat format) {
case gfx::BufferFormat::RGBA_F16:
case gfx::BufferFormat::BGRA_1010102:
case gfx::BufferFormat::YUV_420_BIPLANAR:
+ case gfx::BufferFormat::P010:
return true;
case gfx::BufferFormat::R_16:
case gfx::BufferFormat::RG_88:
@@ -505,7 +523,6 @@ bool GLImageIOSurface::ValidFormat(gfx::BufferFormat format) {
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_1010102:
case gfx::BufferFormat::YVU_420:
- case gfx::BufferFormat::P010:
return false;
}
diff --git a/chromium/ui/gl/gl_image_io_surface_egl.mm b/chromium/ui/gl/gl_image_io_surface_egl.mm
index 38ba2029b04..39d7ef894af 100644
--- a/chromium/ui/gl/gl_image_io_surface_egl.mm
+++ b/chromium/ui/gl/gl_image_io_surface_egl.mm
@@ -52,15 +52,14 @@ InternalFormatType BufferFormatToInternalFormatType(gfx::BufferFormat format,
return {GL_BGRA_EXT, GL_UNSIGNED_BYTE};
case gfx::BufferFormat::RGBA_F16:
return {GL_RGBA, GL_HALF_FLOAT};
- case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::BGRA_1010102:
- NOTIMPLEMENTED();
- return {GL_NONE, GL_NONE};
+ return {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV};
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_1010102:
case gfx::BufferFormat::YVU_420:
+ case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::P010:
NOTREACHED();
return {GL_NONE, GL_NONE};
@@ -251,8 +250,11 @@ bool GLImageIOSurfaceEGL::CopyTexImage(unsigned target) {
return false;
}
- if (format_ != gfx::BufferFormat::YUV_420_BIPLANAR)
+ // TODO(crbug.com/1115621): We should support gfx::BufferFormat::P010 here,
+ // but EGL doesn't seem to be able to sample from the P010 Y and UV textures.
+ if (format_ != gfx::BufferFormat::YUV_420_BIPLANAR) {
return false;
+ }
GLContext* gl_context = GLContext::GetCurrent();
DCHECK(gl_context);
@@ -362,7 +364,8 @@ bool GLImageIOSurfaceEGL::CopyTexImage(unsigned target) {
return false;
}
- yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture);
+ yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture,
+ GL_UNSIGNED_BYTE);
if (glGetError() != GL_NO_ERROR) {
LOG(ERROR) << "Failed converting from YUV to RGB";
return false;
diff --git a/chromium/ui/gl/gl_implementation.cc b/chromium/ui/gl/gl_implementation.cc
index 0d58dc406c3..18192cec206 100644
--- a/chromium/ui/gl/gl_implementation.cc
+++ b/chromium/ui/gl/gl_implementation.cc
@@ -32,7 +32,7 @@ const struct {
} kGLImplementationNamePairs[] = {
{kGLImplementationDesktopName, kGLImplementationDesktopGL},
{kGLImplementationSwiftShaderName, kGLImplementationSwiftShaderGL},
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
{kGLImplementationAppleName, kGLImplementationAppleGL},
#endif
{kGLImplementationEGLName, kGLImplementationEGLGLES2},
diff --git a/chromium/ui/gl/gl_implementation.h b/chromium/ui/gl/gl_implementation.h
index 4e8dafe2f28..ffe19f448d4 100644
--- a/chromium/ui/gl/gl_implementation.h
+++ b/chromium/ui/gl/gl_implementation.h
@@ -47,7 +47,8 @@ enum class ANGLEImplementation {
kNull = 5,
kVulkan = 6,
kSwiftShader = 7,
- kMaxValue = kSwiftShader,
+ kMetal = 8,
+ kMaxValue = kMetal,
};
struct GL_EXPORT GLWindowSystemBindingInfo {
diff --git a/chromium/ui/gl/gl_mock_autogen_gl.h b/chromium/ui/gl/gl_mock_autogen_gl.h
index c92ea02d6b7..e02fc0b125b 100644
--- a/chromium/ui/gl/gl_mock_autogen_gl.h
+++ b/chromium/ui/gl/gl_mock_autogen_gl.h
@@ -1403,6 +1403,16 @@ MOCK_METHOD7(TexStorageMem2DEXT,
GLsizei height,
GLuint memory,
GLuint64 offset));
+MOCK_METHOD9(TexStorageMemFlags2DANGLE,
+ void(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags));
MOCK_METHOD9(TexSubImage2D,
void(GLenum target,
GLint level,
diff --git a/chromium/ui/gl/gl_share_group.cc b/chromium/ui/gl/gl_share_group.cc
index 62f0ea7685d..03e2c5771f5 100644
--- a/chromium/ui/gl/gl_share_group.cc
+++ b/chromium/ui/gl/gl_share_group.cc
@@ -12,7 +12,7 @@
namespace gl {
GLShareGroup::GLShareGroup()
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
: renderer_id_(-1)
#endif
{
@@ -50,7 +50,7 @@ void GLShareGroup::SetSharedContext(GLContext* context) {
shared_context_ = context;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void GLShareGroup::SetRendererID(int renderer_id) {
renderer_id_ = renderer_id;
}
diff --git a/chromium/ui/gl/gl_share_group.h b/chromium/ui/gl/gl_share_group.h
index d3f606d7f4a..bf9bdf1c9aa 100644
--- a/chromium/ui/gl/gl_share_group.h
+++ b/chromium/ui/gl/gl_share_group.h
@@ -39,7 +39,7 @@ class GL_EXPORT GLShareGroup : public base::RefCounted<GLShareGroup> {
void SetSharedContext(GLContext* context);
GLContext* shared_context() { return shared_context_; }
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Sets and returns the ID of the renderer that all contexts in this share
// group should be on.
void SetRendererID(int renderer_id);
@@ -58,7 +58,7 @@ class GL_EXPORT GLShareGroup : public base::RefCounted<GLShareGroup> {
GLContext* shared_context_ = nullptr;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
int renderer_id_;
#endif
diff --git a/chromium/ui/gl/gl_stub_autogen_gl.h b/chromium/ui/gl/gl_stub_autogen_gl.h
index 7fc5200c4ba..72a2fdfdf31 100644
--- a/chromium/ui/gl/gl_stub_autogen_gl.h
+++ b/chromium/ui/gl/gl_stub_autogen_gl.h
@@ -1463,6 +1463,15 @@ void glTexStorageMem2DEXTFn(GLenum target,
GLsizei height,
GLuint memory,
GLuint64 offset) override {}
+void glTexStorageMemFlags2DANGLEFn(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLuint memory,
+ GLuint64 offset,
+ GLbitfield createFlags,
+ GLbitfield usageFlags) override {}
void glTexSubImage2DFn(GLenum target,
GLint level,
GLint xoffset,
diff --git a/chromium/ui/gl/gl_surface_egl.cc b/chromium/ui/gl/gl_surface_egl.cc
index beda0520c09..327df79456b 100644
--- a/chromium/ui/gl/gl_surface_egl.cc
+++ b/chromium/ui/gl/gl_surface_egl.cc
@@ -116,6 +116,11 @@
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
#endif /* EGL_ANGLE_platform_angle_vulkan */
+#ifndef EGL_ANGLE_platform_angle_metal
+#define EGL_ANGLE_platform_angle_metal 1
+#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489
+#endif /* EGL_ANGLE_platform_angle_metal */
+
#ifndef EGL_ANGLE_x11_visual
#define EGL_ANGLE_x11_visual 1
#define EGL_X11_VISUAL_ID_ANGLE 0x33A3
@@ -188,6 +193,7 @@ bool g_egl_ext_colorspace_display_p3_passthrough = false;
bool g_egl_flexible_surface_compatibility_supported = false;
bool g_egl_robust_resource_init_supported = false;
bool g_egl_display_texture_share_group_supported = false;
+bool g_egl_display_semaphore_share_group_supported = false;
bool g_egl_create_context_client_arrays_supported = false;
bool g_egl_android_native_fence_sync_supported = false;
bool g_egl_ext_pixel_format_float_supported = false;
@@ -461,6 +467,19 @@ EGLDisplay GetDisplayFromType(
native_display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE,
enabled_angle_features, disabled_angle_features,
extra_display_attribs);
+ case ANGLE_METAL:
+ return GetPlatformANGLEDisplay(
+ native_display, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
+ enabled_angle_features, disabled_angle_features,
+ extra_display_attribs);
+ case ANGLE_METAL_NULL:
+ extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
+ extra_display_attribs.push_back(
+ EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
+ return GetPlatformANGLEDisplay(
+ native_display, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
+ enabled_angle_features, disabled_angle_features,
+ extra_display_attribs);
default:
NOTREACHED();
return EGL_NO_DISPLAY;
@@ -489,6 +508,9 @@ ANGLEImplementation GetANGLEImplementationFromDisplayType(
return ANGLEImplementation::kVulkan;
case ANGLE_SWIFTSHADER:
return ANGLEImplementation::kSwiftShader;
+ case ANGLE_METAL:
+ case ANGLE_METAL_NULL:
+ return ANGLEImplementation::kMetal;
default:
return ANGLEImplementation::kNone;
}
@@ -528,6 +550,10 @@ const char* DisplayTypeString(DisplayType display_type) {
return "OpenGLEGL";
case ANGLE_OPENGLES_EGL:
return "OpenGLESEGL";
+ case ANGLE_METAL:
+ return "Metal";
+ case ANGLE_METAL_NULL:
+ return "MetalNull";
default:
NOTREACHED();
return "Err";
@@ -752,6 +778,7 @@ void GetEGLInitDisplays(bool supports_angle_d3d,
bool supports_angle_vulkan,
bool supports_angle_swiftshader,
bool supports_angle_egl,
+ bool supports_angle_metal,
const base::CommandLine* command_line,
std::vector<DisplayType>* init_displays) {
// SwiftShader does not use the platform extensions
@@ -850,6 +877,18 @@ void GetEGLInitDisplays(bool supports_angle_d3d,
}
}
+ if (supports_angle_metal) {
+ if (use_angle_default) {
+ if (!supports_angle_opengl) {
+ AddInitDisplay(init_displays, ANGLE_METAL);
+ }
+ } else if (requested_renderer == kANGLEImplementationMetalName) {
+ AddInitDisplay(init_displays, ANGLE_METAL);
+ } else if (requested_renderer == kANGLEImplementationMetalNULLName) {
+ AddInitDisplay(init_displays, ANGLE_METAL_NULL);
+ }
+ }
+
// If no displays are available due to missing angle extensions or invalid
// flags, request the default display.
if (init_displays->empty()) {
@@ -943,6 +982,8 @@ bool GLSurfaceEGL::InitializeOneOffCommon() {
g_egl_display_texture_share_group_supported =
HasEGLExtension("EGL_ANGLE_display_texture_share_group");
+ g_egl_display_semaphore_share_group_supported =
+ HasEGLExtension("EGL_ANGLE_display_semaphore_share_group");
g_egl_create_context_client_arrays_supported =
HasEGLExtension("EGL_ANGLE_create_context_client_arrays");
g_egl_robust_resource_init_supported =
@@ -1115,6 +1156,10 @@ bool GLSurfaceEGL::IsDisplayTextureShareGroupSupported() {
return g_egl_display_texture_share_group_supported;
}
+bool GLSurfaceEGL::IsDisplaySemaphoreShareGroupSupported() {
+ return g_egl_display_semaphore_share_group_supported;
+}
+
bool GLSurfaceEGL::IsCreateContextClientArraysSupported() {
return g_egl_create_context_client_arrays_supported;
}
@@ -1178,6 +1223,7 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display) {
bool supports_angle_vulkan = false;
bool supports_angle_swiftshader = false;
bool supports_angle_egl = false;
+ bool supports_angle_metal = false;
// Check for availability of ANGLE extensions.
if (client_extensions &&
ExtensionsContain(client_extensions, "EGL_ANGLE_platform_angle")) {
@@ -1193,11 +1239,13 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display) {
client_extensions, "EGL_ANGLE_platform_angle_device_type_swiftshader");
supports_angle_egl = ExtensionsContain(
client_extensions, "EGL_ANGLE_platform_angle_device_type_egl_angle");
+ supports_angle_metal =
+ ExtensionsContain(client_extensions, "EGL_ANGLE_platform_angle_metal");
}
bool supports_angle = supports_angle_d3d || supports_angle_opengl ||
supports_angle_null || supports_angle_vulkan ||
- supports_angle_swiftshader;
+ supports_angle_swiftshader || supports_angle_metal;
if (client_extensions) {
g_egl_angle_feature_control_supported =
@@ -1209,7 +1257,7 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display) {
GetEGLInitDisplays(supports_angle_d3d, supports_angle_opengl,
supports_angle_null, supports_angle_vulkan,
supports_angle_swiftshader, supports_angle_egl,
- command_line, &init_displays);
+ supports_angle_metal, command_line, &init_displays);
std::vector<std::string> enabled_angle_features =
GetStringVectorFromCommandLine(command_line,
diff --git a/chromium/ui/gl/gl_surface_egl.h b/chromium/ui/gl/gl_surface_egl.h
index ec6bf0816d5..34924467adc 100644
--- a/chromium/ui/gl/gl_surface_egl.h
+++ b/chromium/ui/gl/gl_surface_egl.h
@@ -70,7 +70,9 @@ enum DisplayType {
ANGLE_SWIFTSHADER = 14,
ANGLE_OPENGL_EGL = 15,
ANGLE_OPENGLES_EGL = 16,
- DISPLAY_TYPE_MAX = 17,
+ ANGLE_METAL = 17,
+ ANGLE_METAL_NULL = 18,
+ DISPLAY_TYPE_MAX = 19,
};
GL_EXPORT void GetEGLInitDisplays(bool supports_angle_d3d,
@@ -79,6 +81,7 @@ GL_EXPORT void GetEGLInitDisplays(bool supports_angle_d3d,
bool supports_angle_vulkan,
bool supports_angle_swiftshader,
bool supports_angle_egl,
+ bool supports_angle_metal,
const base::CommandLine* command_line,
std::vector<DisplayType>* init_displays);
@@ -113,6 +116,7 @@ class GL_EXPORT GLSurfaceEGL : public GLSurface {
static bool IsEGLFlexibleSurfaceCompatibilitySupported();
static bool IsRobustResourceInitSupported();
static bool IsDisplayTextureShareGroupSupported();
+ static bool IsDisplaySemaphoreShareGroupSupported();
static bool IsCreateContextClientArraysSupported();
static bool IsAndroidNativeFenceSyncSupported();
static bool IsPixelFormatFloatSupported();
diff --git a/chromium/ui/gl/gl_surface_egl_surface_control.cc b/chromium/ui/gl/gl_surface_egl_surface_control.cc
index cf43491e721..24d9bc9847d 100644
--- a/chromium/ui/gl/gl_surface_egl_surface_control.cc
+++ b/chromium/ui/gl/gl_surface_egl_surface_control.cc
@@ -223,10 +223,11 @@ void GLSurfaceEGLSurfaceControl::CommitPendingTransaction(
// the next transaction.
DCHECK_LE(pending_surfaces_count_, surface_list_.size());
for (size_t i = pending_surfaces_count_; i < surface_list_.size(); ++i) {
- const auto& surface_state = surface_list_[i];
+ auto& surface_state = surface_list_[i];
pending_transaction_->SetBuffer(*surface_state.surface, nullptr,
base::ScopedFD());
pending_transaction_->SetVisibility(*surface_state.surface, false);
+ surface_state.visibility = false;
}
// TODO(khushalsagar): Consider using the SetDamageRect API for partial
@@ -250,11 +251,6 @@ void GLSurfaceEGLSurfaceControl::CommitPendingTransaction(
std::move(primary_plane_fences_));
primary_plane_fences_.reset();
pending_transaction_->SetOnCompleteCb(std::move(callback), gpu_task_runner_);
-
- // Cache only those surfaces which were used in this transaction. The surfaces
- // removed here are persisted in |resources_to_release| so we can release
- // them after receiving read fences from the framework.
- surface_list_.resize(pending_surfaces_count_);
pending_surfaces_count_ = 0u;
frame_rate_update_pending_ = false;
@@ -307,6 +303,12 @@ bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane(
pending_surfaces_count_++;
auto& surface_state = surface_list_.at(pending_surfaces_count_ - 1);
+ // Make the surface visible if its hidden or uninitialized..
+ if (uninitialized || !surface_state.visibility) {
+ pending_transaction_->SetVisibility(*surface_state.surface, true);
+ surface_state.visibility = true;
+ }
+
if (uninitialized || surface_state.z_order != z_order) {
surface_state.z_order = z_order;
pending_transaction_->SetZOrder(*surface_state.surface, z_order);
@@ -325,9 +327,11 @@ bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane(
// its the primary plane.
is_primary_plane = !scoped_hardware_buffer->is_video();
DCHECK(!is_primary_plane || !primary_plane_fences_);
- primary_plane_fences_.emplace();
- primary_plane_fences_->available_fence =
- scoped_hardware_buffer->TakeAvailableFence();
+ if (is_primary_plane) {
+ primary_plane_fences_.emplace();
+ primary_plane_fences_->available_fence =
+ scoped_hardware_buffer->TakeAvailableFence();
+ }
auto* a_surface = surface_state.surface->surface();
DCHECK_EQ(pending_frame_resources_.count(a_surface), 0u);
@@ -361,30 +365,12 @@ bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane(
}
if (hardware_buffer) {
- gfx::Rect dst = bounds_rect;
-
- // Get the crop rectangle from the image which is the actual region of valid
- // pixels. This region could be smaller than the buffer dimensions. Hence
- // scale the |crop_rect| according to the valid pixel area rather than
- // buffer dimensions. crbug.com/1027766 for more details.
- gfx::Rect valid_pixel_rect = image->GetCropRect();
gfx::Size buffer_size = GetBufferSize(hardware_buffer);
+ gfx::RectF scaled_rect =
+ gfx::ScaleRect(crop_rect, buffer_size.width(), buffer_size.height());
- // If the image doesn't provide a |valid_pixel_rect|, assume the entire
- // buffer is valid.
- if (valid_pixel_rect.IsEmpty()) {
- valid_pixel_rect.set_size(buffer_size);
- } else {
- // Clamp the |valid_pixel_rect| to the buffer dimensions to make sure for
- // some reason it does not overflows.
- valid_pixel_rect.Intersect(gfx::Rect(buffer_size));
- }
- gfx::RectF scaled_rect = gfx::RectF(
- crop_rect.x() * valid_pixel_rect.width() + valid_pixel_rect.x(),
- crop_rect.y() * valid_pixel_rect.height() + valid_pixel_rect.y(),
- crop_rect.width() * valid_pixel_rect.width(),
- crop_rect.height() * valid_pixel_rect.height());
gfx::Rect src = gfx::ToEnclosedRect(scaled_rect);
+ gfx::Rect dst = bounds_rect;
if (uninitialized || surface_state.src != src || surface_state.dst != dst ||
surface_state.transform != transform) {
diff --git a/chromium/ui/gl/gl_surface_egl_surface_control.h b/chromium/ui/gl/gl_surface_egl_surface_control.h
index a82390e3df3..e93058da5ae 100644
--- a/chromium/ui/gl/gl_surface_egl_surface_control.h
+++ b/chromium/ui/gl/gl_surface_egl_surface_control.h
@@ -110,6 +110,8 @@ class GL_EXPORT GLSurfaceEGLSurfaceControl : public GLSurfaceEGL {
// one pending.
bool buffer_updated_in_pending_transaction = true;
+ // Indicates whether the |surface| will be visible or hidden.
+ bool visibility = true;
scoped_refptr<SurfaceControl::Surface> surface;
};
diff --git a/chromium/ui/gl/gl_surface_egl_unittest.cc b/chromium/ui/gl/gl_surface_egl_unittest.cc
index 6346bf9e353..368d167412e 100644
--- a/chromium/ui/gl/gl_surface_egl_unittest.cc
+++ b/chromium/ui/gl/gl_surface_egl_unittest.cc
@@ -79,6 +79,7 @@ class TestPlatformDelegate : public ui::PlatformWindowDelegate {
void OnWindowStateChanged(ui::PlatformWindowState new_state) override {}
void OnLostCapture() override {}
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {}
+ void OnWillDestroyAcceleratedWidget() override {}
void OnAcceleratedWidgetDestroyed() override {}
void OnActivationChanged(bool active) override {}
void OnMouseEnter() override {}
diff --git a/chromium/ui/gl/gl_surface_egl_x11.cc b/chromium/ui/gl/gl_surface_egl_x11.cc
index 55071e3ff10..b01f8062010 100644
--- a/chromium/ui/gl/gl_surface_egl_x11.cc
+++ b/chromium/ui/gl/gl_surface_egl_x11.cc
@@ -6,6 +6,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/x/x11_display_util.h"
+#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/xproto.h"
@@ -105,22 +106,19 @@ NativeViewGLSurfaceEGLX11::CreateVsyncProviderInternal() {
}
bool NativeViewGLSurfaceEGLX11::DispatchXEvent(x11::Event* x11_event) {
- XEvent* x_event = &x11_event->xlib_event();
// When ANGLE is used for EGL, it creates an X11 child window. Expose events
// from this window need to be forwarded to this class.
- bool can_dispatch =
- x_event->type == Expose &&
- std::find(children_.begin(), children_.end(),
- static_cast<x11::Window>(x_event->xexpose.window)) !=
- children_.end();
-
+ auto* expose = x11_event->As<x11::ExposeEvent>();
+ bool can_dispatch = expose && std::find(children_.begin(), children_.end(),
+ expose->window) != children_.end();
if (!can_dispatch)
return false;
- x_event->xexpose.window = window_;
- Display* x11_display = GetXNativeDisplay();
- XSendEvent(x11_display, window_, x11::False, ExposureMask, x_event);
- XFlush(x11_display);
+ auto expose_copy = *expose;
+ auto window = static_cast<x11::Window>(window_);
+ expose_copy.window = window;
+ ui::SendEvent(expose_copy, window, x11::EventMask::Exposure);
+ x11::Connection::Get()->Flush();
return true;
}
diff --git a/chromium/ui/gl/gl_surface_egl_x11_gles2.cc b/chromium/ui/gl/gl_surface_egl_x11_gles2.cc
index 9ddce7c2dff..593c5d89add 100644
--- a/chromium/ui/gl/gl_surface_egl_x11_gles2.cc
+++ b/chromium/ui/gl/gl_surface_egl_x11_gles2.cc
@@ -4,6 +4,7 @@
#include "ui/gl/gl_surface_egl_x11_gles2.h"
+#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/x11.h"
#include "ui/gl/egl_util.h"
@@ -145,16 +146,15 @@ bool NativeViewGLSurfaceEGLX11GLES2::Resize(const gfx::Size& size,
}
bool NativeViewGLSurfaceEGLX11GLES2::DispatchXEvent(x11::Event* x11_event) {
- XEvent* xev = &x11_event->xlib_event();
- if (xev->type != Expose ||
- xev->xexpose.window != static_cast<Window>(window_))
+ auto* expose = x11_event->As<x11::ExposeEvent>();
+ auto window = static_cast<x11::Window>(window_);
+ if (!expose || expose->window != window)
return false;
- xev->xexpose.window = static_cast<uint32_t>(parent_window_);
- Display* x11_display = GetXNativeDisplay();
- XSendEvent(x11_display, static_cast<uint32_t>(parent_window_), x11::False,
- ExposureMask, xev);
- XFlush(x11_display);
+ auto expose_copy = *expose;
+ expose_copy.window = parent_window_;
+ ui::SendEvent(expose_copy, parent_window_, x11::EventMask::Exposure);
+ x11::Connection::Get()->Flush();
return true;
}
diff --git a/chromium/ui/gl/gl_surface_glx.cc b/chromium/ui/gl/gl_surface_glx.cc
index cbb7513809d..d68d8943faf 100644
--- a/chromium/ui/gl/gl_surface_glx.cc
+++ b/chromium/ui/gl/gl_surface_glx.cc
@@ -26,8 +26,13 @@
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/dri2.h"
+#include "ui/gfx/x/glx.h"
+#include "ui/gfx/x/present.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xf86vidmode.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
@@ -148,8 +153,8 @@ bool CreateDummyWindow(Display* display) {
class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider {
public:
- explicit OMLSyncControlVSyncProvider(GLXWindow glx_window)
- : SyncControlVSyncProvider(), glx_window_(glx_window) {}
+ explicit OMLSyncControlVSyncProvider(x11::Window window)
+ : SyncControlVSyncProvider(), window_(window) {}
~OMLSyncControlVSyncProvider() override = default;
@@ -157,29 +162,85 @@ class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider {
bool GetSyncValues(int64_t* system_time,
int64_t* media_stream_counter,
int64_t* swap_buffer_counter) override {
- return glXGetSyncValuesOML(gfx::GetXDisplay(), glx_window_, system_time,
- media_stream_counter, swap_buffer_counter);
+ auto* connection = x11::Connection::Get();
+
+ // First try to get the counter values using the DRI2 extension.
+ if (auto reply = connection->dri2().GetMSC({window_}).Sync()) {
+ auto merge_counter = [](uint32_t hi, uint32_t lo) {
+ return (static_cast<uint64_t>(hi) << 32) | lo;
+ };
+ *system_time = merge_counter(reply->ust_hi, reply->ust_lo);
+ *media_stream_counter = merge_counter(reply->msc_hi, reply->msc_lo);
+ *swap_buffer_counter = merge_counter(reply->sbc_hi, reply->sbc_lo);
+ return true;
+ }
+
+ // Next try the present extension.
+ auto& present = connection->present();
+ // Check if the present extension is available.
+ if (!present.present())
+ return false;
+
+ // Issue a NotifyMSC request and listen for the resulting event which will
+ // contain the counter values.
+ auto context = connection->GenerateId<x11::Present::Event>();
+ present.SelectInput(
+ {context, window_, x11::Present::EventMask::CompleteNotify});
+ connection->present().NotifyMSC({window_});
+ present.SelectInput({context, window_, x11::Present::EventMask::NoEvent});
+ connection->Sync();
+ connection->ReadResponses();
+ for (const auto& event : connection->events()) {
+ auto* complete = event.As<x11::Present::CompleteNotifyEvent>();
+ if (complete && complete->kind == x11::Present::CompleteKind::NotifyMSC &&
+ complete->window == window_ && complete->serial == 0) {
+ *system_time = complete->ust;
+ *media_stream_counter = complete->msc;
+ *swap_buffer_counter = 0;
+ return true;
+ }
+ }
+
+ return false;
}
bool GetMscRate(int32_t* numerator, int32_t* denominator) override {
if (!g_glx_get_msc_rate_oml_supported)
return false;
- if (!glXGetMscRateOML(gfx::GetXDisplay(), glx_window_, numerator,
- denominator)) {
- // Once glXGetMscRateOML has been found to fail, don't try again,
+ auto* connection = x11::Connection::Get();
+ connection->xf86vidmode().SetClientVersion(
+ {x11::XF86VidMode::major_version, x11::XF86VidMode::minor_version});
+ auto reply = connection->xf86vidmode()
+ .GetModeLine({connection->DefaultScreenId()})
+ .Sync();
+ if (!reply) {
+ // Once GetModeLine has been found to fail, don't try again,
// since each failing call may spew an error message.
g_glx_get_msc_rate_oml_supported = false;
return false;
}
+ *numerator = static_cast<uint32_t>(reply->dotclock) * 1000;
+ *denominator = static_cast<uint32_t>(reply->vtotal) * reply->htotal;
+
+ // These adjustments are from mesa's __glxGetMscRate().
+ if (static_cast<bool>(reply->flags &
+ x11::XF86VidMode::ModeFlag::Interlace)) {
+ *numerator *= 2;
+ }
+ if (static_cast<bool>(reply->flags &
+ x11::XF86VidMode::ModeFlag::Composite_Sync)) {
+ *denominator *= 2;
+ }
+
return true;
}
bool IsHWClock() const override { return true; }
private:
- GLXWindow glx_window_;
+ x11::Window window_;
DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
};
@@ -671,8 +732,8 @@ bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) {
}
if (g_glx_oml_sync_control_supported) {
- vsync_provider_ =
- std::make_unique<OMLSyncControlVSyncProvider>(glx_window_);
+ vsync_provider_ = std::make_unique<OMLSyncControlVSyncProvider>(
+ static_cast<x11::Window>(window_));
presentation_helper_ =
std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
} else if (g_glx_sgi_video_sync_supported) {
@@ -816,17 +877,16 @@ NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
}
void NativeViewGLSurfaceGLX::ForwardExposeEvent(x11::Event* event) {
- XEvent& forwarded_event = event->xlib_event();
- forwarded_event.xexpose.window = static_cast<uint32_t>(parent_window_);
- XSendEvent(gfx::GetXDisplay(), static_cast<uint32_t>(parent_window_),
- x11::False, ExposureMask, &forwarded_event);
- XFlush(gfx::GetXDisplay());
+ auto forwarded_event = *event->As<x11::ExposeEvent>();
+ auto window = static_cast<x11::Window>(parent_window_);
+ forwarded_event.window = window;
+ ui::SendEvent(forwarded_event, window, x11::EventMask::Exposure);
+ x11::Connection::Get()->Flush();
}
bool NativeViewGLSurfaceGLX::CanHandleEvent(x11::Event* x11_event) {
- XEvent* event = &x11_event->xlib_event();
- return event->type == Expose &&
- event->xexpose.window == static_cast<Window>(window_);
+ auto* expose = x11_event->As<x11::ExposeEvent>();
+ return expose && expose->window == static_cast<x11::Window>(window_);
}
GLXDrawable NativeViewGLSurfaceGLX::GetDrawableHandle() const {
diff --git a/chromium/ui/gl/gl_switches.cc b/chromium/ui/gl/gl_switches.cc
index f50a1528af6..06b82b91feb 100644
--- a/chromium/ui/gl/gl_switches.cc
+++ b/chromium/ui/gl/gl_switches.cc
@@ -29,12 +29,14 @@ const char kANGLEImplementationOpenGLESEGLName[] = "gles-egl";
const char kANGLEImplementationNullName[] = "null";
const char kANGLEImplementationVulkanName[] = "vulkan";
const char kANGLEImplementationSwiftShaderName[] = "swiftshader";
+const char kANGLEImplementationMetalName[] = "metal";
// Special switches for "NULL"/stub driver implementations.
const char kANGLEImplementationD3D11NULLName[] = "d3d11-null";
const char kANGLEImplementationOpenGLNULLName[] = "gl-null";
const char kANGLEImplementationOpenGLESNULLName[] = "gles-null";
const char kANGLEImplementationVulkanNULLName[] = "vulkan-null";
+const char kANGLEImplementationMetalNULLName[] = "metal-null";
// The command decoder names that can be passed to --use-cmd-decoder.
const char kCmdDecoderValidatingName[] = "validating";
@@ -173,33 +175,21 @@ const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches =
namespace features {
-// Allow putting content with complex transforms (e.g. rotations) into an
-// overlay.
-const base::Feature kDirectCompositionComplexOverlays{
- "DirectCompositionComplexOverlays", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Use IDXGIOutput::WaitForVBlank() to drive begin frames.
-const base::Feature kDirectCompositionGpuVSync{
- "DirectCompositionGpuVSync", base::FEATURE_ENABLED_BY_DEFAULT};
+// Forces Chrome's main backbuffer to full damage if the actual damage
+// is large enough and allows DWM to consider the main backbuffer as an
+// an overlay candidate.
+const base::Feature kDirectCompositionForceFullDamage{
+ "DirectCompositionForceFullDamage", base::FEATURE_DISABLED_BY_DEFAULT};
// Use presentation feedback event queries (must be enabled) to limit latency.
const base::Feature kDirectCompositionLowLatencyPresentation{
"DirectCompositionLowLatencyPresentation",
base::FEATURE_DISABLED_BY_DEFAULT};
-// Allow using overlays for non-root render passes.
-const base::Feature kDirectCompositionNonrootOverlays{
- "DirectCompositionNonrootOverlays", base::FEATURE_DISABLED_BY_DEFAULT};
-
// Overrides preferred overlay format to NV12 instead of YUY2.
const base::Feature kDirectCompositionPreferNV12Overlays{
"DirectCompositionPreferNV12Overlays", base::FEATURE_ENABLED_BY_DEFAULT};
-// Use per-present event queries to issue presentation feedback to clients.
-// Also needs DirectCompositionGpuVSync.
-const base::Feature kDirectCompositionPresentationFeedback{
- "DirectCompositionPresentationFeedback", base::FEATURE_ENABLED_BY_DEFAULT};
-
// Allow overlay swapchain to present on all GPUs even if they only support
// software overlays.
const base::Feature kDirectCompositionSoftwareOverlays{
diff --git a/chromium/ui/gl/gl_switches.h b/chromium/ui/gl/gl_switches.h
index 775c6c702c2..17b6f3f93e1 100644
--- a/chromium/ui/gl/gl_switches.h
+++ b/chromium/ui/gl/gl_switches.h
@@ -34,11 +34,13 @@ GL_EXPORT extern const char kANGLEImplementationOpenGLESEGLName[];
GL_EXPORT extern const char kANGLEImplementationNullName[];
GL_EXPORT extern const char kANGLEImplementationVulkanName[];
GL_EXPORT extern const char kANGLEImplementationSwiftShaderName[];
+GL_EXPORT extern const char kANGLEImplementationMetalName[];
GL_EXPORT extern const char kANGLEImplementationD3D11NULLName[];
GL_EXPORT extern const char kANGLEImplementationOpenGLNULLName[];
GL_EXPORT extern const char kANGLEImplementationOpenGLESNULLName[];
GL_EXPORT extern const char kANGLEImplementationVulkanNULLName[];
+GL_EXPORT extern const char kANGLEImplementationMetalNULLName[];
GL_EXPORT extern const char kCmdDecoderValidatingName[];
GL_EXPORT extern const char kCmdDecoderPassthroughName[];
@@ -81,10 +83,9 @@ GL_EXPORT extern const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches;
} // namespace switches
namespace features {
-GL_EXPORT extern const base::Feature kDirectCompositionComplexOverlays;
+GL_EXPORT extern const base::Feature kDirectCompositionForceFullDamage;
GL_EXPORT extern const base::Feature kDirectCompositionGpuVSync;
GL_EXPORT extern const base::Feature kDirectCompositionLowLatencyPresentation;
-GL_EXPORT extern const base::Feature kDirectCompositionNonrootOverlays;
GL_EXPORT extern const base::Feature kDirectCompositionPreferNV12Overlays;
GL_EXPORT extern const base::Feature kDirectCompositionPresentationFeedback;
GL_EXPORT extern const base::Feature kDirectCompositionSoftwareOverlays;
diff --git a/chromium/ui/gl/gl_utils.cc b/chromium/ui/gl/gl_utils.cc
index 6e87f0d04a7..f8a494b05e4 100644
--- a/chromium/ui/gl/gl_utils.cc
+++ b/chromium/ui/gl/gl_utils.cc
@@ -12,6 +12,10 @@
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_switches.h"
+#if defined(USE_EGL)
+#include "ui/gl/gl_surface_egl.h"
+#endif // defined(USE_EGL)
+
#if defined(OS_ANDROID)
#include "base/posix/eintr_wrapper.h"
#include "third_party/libsync/src/include/sync/sync.h"
@@ -81,10 +85,53 @@ bool UsePassthroughCommandDecoder(const base::CommandLine* command_line) {
}
}
+bool PassthroughCommandDecoderSupported() {
+#if defined(USE_EGL)
+ // Using the passthrough command buffer requires that specific ANGLE
+ // extensions are exposed
+ return gl::GLSurfaceEGL::IsCreateContextBindGeneratesResourceSupported() &&
+ gl::GLSurfaceEGL::IsCreateContextWebGLCompatabilitySupported() &&
+ gl::GLSurfaceEGL::IsRobustResourceInitSupported() &&
+ gl::GLSurfaceEGL::IsDisplayTextureShareGroupSupported() &&
+ gl::GLSurfaceEGL::IsCreateContextClientArraysSupported();
+#else
+ // The passthrough command buffer is only supported on top of ANGLE/EGL
+ return false;
+#endif // defined(USE_EGL)
+}
+
#if defined(OS_WIN)
// This function is thread safe.
bool AreOverlaysSupportedWin() {
return gl::DirectCompositionSurfaceWin::AreOverlaysSupported();
}
-#endif
+
+unsigned int FrameRateToPresentDuration(float frame_rate) {
+ if (frame_rate == 0)
+ return 0u;
+ // Present duration unit is 100 ns.
+ return static_cast<unsigned int>(1.0E7 / frame_rate);
+}
+
+UINT GetOverlaySupportFlags(DXGI_FORMAT format) {
+ return gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(format);
+}
+
+bool ShouldForceDirectCompositionRootSurfaceFullDamage() {
+ static bool should_force = []() {
+ if (!base::FeatureList::IsEnabled(
+ features::kDirectCompositionForceFullDamage)) {
+ return false;
+ }
+ UINT brga_flags = DirectCompositionSurfaceWin::GetOverlaySupportFlags(
+ DXGI_FORMAT_B8G8R8A8_UNORM);
+ constexpr UINT kSupportBits =
+ DXGI_OVERLAY_SUPPORT_FLAG_DIRECT | DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
+ if ((brga_flags & kSupportBits) == 0)
+ return false;
+ return true;
+ }();
+ return should_force;
+}
+#endif // OS_WIN
} // namespace gl
diff --git a/chromium/ui/gl/gl_utils.h b/chromium/ui/gl/gl_utils.h
index 32eacdaa4fc..26fe7bd2840 100644
--- a/chromium/ui/gl/gl_utils.h
+++ b/chromium/ui/gl/gl_utils.h
@@ -11,6 +11,10 @@
#include "build/build_config.h"
#include "ui/gl/gl_export.h"
+#if defined(OS_WIN)
+#include <dxgi1_6.h>
+#endif
+
#if defined(OS_ANDROID)
#include "base/files/scoped_file.h"
#endif
@@ -26,8 +30,19 @@ GL_EXPORT base::ScopedFD MergeFDs(base::ScopedFD a, base::ScopedFD b);
GL_EXPORT bool UsePassthroughCommandDecoder(
const base::CommandLine* command_line);
+GL_EXPORT bool PassthroughCommandDecoderSupported();
+
#if defined(OS_WIN)
GL_EXPORT bool AreOverlaysSupportedWin();
+
+// Calculates present during in 100 ns from number of frames per second.
+GL_EXPORT unsigned int FrameRateToPresentDuration(float frame_rate);
+
+GL_EXPORT UINT GetOverlaySupportFlags(DXGI_FORMAT format);
+
+// Whether to use full damage when direct compostion root surface presents.
+// This function is thread safe.
+GL_EXPORT bool ShouldForceDirectCompositionRootSurfaceFullDamage();
#endif
} // namespace gl
diff --git a/chromium/ui/gl/gl_version_info.cc b/chromium/ui/gl/gl_version_info.cc
index f66cfd1a8b3..a8cf65628a3 100644
--- a/chromium/ui/gl/gl_version_info.cc
+++ b/chromium/ui/gl/gl_version_info.cc
@@ -29,19 +29,7 @@ namespace gl {
GLVersionInfo::GLVersionInfo(const char* version_str,
const char* renderer_str,
- const gfx::ExtensionSet& extensions)
- : is_es(false),
- is_angle(false),
- is_d3d(false),
- is_mesa(false),
- is_swiftshader(false),
- is_angle_swiftshader(false),
- major_version(0),
- minor_version(0),
- is_es2(false),
- is_es3(false),
- is_desktop_core_profile(false),
- is_es3_capable(false) {
+ const gfx::ExtensionSet& extensions) {
Initialize(version_str, renderer_str, extensions);
}
@@ -60,6 +48,8 @@ void GLVersionInfo::Initialize(const char* version_str,
if (is_angle) {
is_angle_swiftshader =
renderer_string.find("SwiftShader Device") != std::string::npos;
+ is_angle_vulkan = renderer_string.find("Vulkan") != std::string::npos;
+ is_angle_metal = renderer_string.find("Metal") != std::string::npos;
}
is_swiftshader = base::StartsWith(renderer_str, "Google SwiftShader",
@@ -203,6 +193,9 @@ void GLVersionInfo::ExtractDriverVendorANGLE(const char* renderer_str) {
DCHECK(base::StartsWith(rstr, "SwiftShader", base::CompareCase::SENSITIVE));
driver_vendor = "ANGLE (Google)";
}
+ if (is_angle_metal) {
+ DCHECK(base::StartsWith(rstr, "Metal", base::CompareCase::SENSITIVE));
+ }
if (base::StartsWith(rstr, "NVIDIA ", base::CompareCase::SENSITIVE))
driver_vendor = "ANGLE (NVIDIA)";
else if (base::StartsWith(rstr, "Radeon ", base::CompareCase::SENSITIVE))
diff --git a/chromium/ui/gl/gl_version_info.h b/chromium/ui/gl/gl_version_info.h
index f44cf44eb19..63ceab91436 100644
--- a/chromium/ui/gl/gl_version_info.h
+++ b/chromium/ui/gl/gl_version_info.h
@@ -60,18 +60,20 @@ struct GL_EXPORT GLVersionInfo {
return !is_angle && !is_swiftshader && (is_es3 || is_desktop_core_profile);
}
- bool is_es;
- bool is_angle;
- bool is_d3d;
- bool is_mesa;
- bool is_swiftshader;
- bool is_angle_swiftshader;
- unsigned major_version;
- unsigned minor_version;
- bool is_es2;
- bool is_es3;
- bool is_desktop_core_profile;
- bool is_es3_capable;
+ bool is_es = false;
+ bool is_angle = false;
+ bool is_d3d = false;
+ bool is_mesa = false;
+ bool is_swiftshader = false;
+ bool is_angle_metal = false;
+ bool is_angle_swiftshader = false;
+ bool is_angle_vulkan = false;
+ unsigned major_version = 0;
+ unsigned minor_version = 0;
+ bool is_es2 = false;
+ bool is_es3 = false;
+ bool is_desktop_core_profile = false;
+ bool is_es3_capable = false;
std::string driver_vendor;
std::string driver_version;
diff --git a/chromium/ui/gl/init/BUILD.gn b/chromium/ui/gl/init/BUILD.gn
index 9141ba379fd..a0013554bfe 100644
--- a/chromium/ui/gl/init/BUILD.gn
+++ b/chromium/ui/gl/init/BUILD.gn
@@ -2,11 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/gl/features.gni")
-jumbo_component("init") {
+component("init") {
output_name = "gl_init"
public = [
@@ -55,7 +54,7 @@ jumbo_component("init") {
"gl_initializer_mac.cc",
]
- libs = [ "OpenGL.framework" ]
+ frameworks = [ "OpenGL.framework" ]
} else if (use_ozone) {
sources += [
"gl_display_egl_util_ozone.cc",
diff --git a/chromium/ui/gl/init/create_gr_gl_interface.cc b/chromium/ui/gl/init/create_gr_gl_interface.cc
index cb283629719..a41f3661886 100644
--- a/chromium/ui/gl/init/create_gr_gl_interface.cc
+++ b/chromium/ui/gl/init/create_gr_gl_interface.cc
@@ -89,7 +89,7 @@ GLboolean glIsSyncEmulateEGL(GLsync sync) {
return true;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
std::map<GLuint, base::TimeTicks>& GetProgramCreateTimesMap() {
static base::NoDestructor<std::map<GLuint, base::TimeTicks>> instance;
return *instance.get();
@@ -136,7 +136,7 @@ template <bool droppable_call = false, typename R, typename... Args>
GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow_on_mac(
R(GL_BINDING_CALL* func)(Args...),
gl::ProgressReporter* progress_reporter) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (!progress_reporter) {
return maybe_drop_call<droppable_call>(func);
}
@@ -154,7 +154,7 @@ GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow_on_mac(
template <bool droppable_call = false, typename R, typename... Args>
GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_with_flush_on_mac(
R(GL_BINDING_CALL* func)(Args...)) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return [func](Args... args) {
// Conditional may be optimized out because droppable_call is set at compile
// time.
@@ -312,7 +312,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface(
bind_slow(gl->glCompressedTexSubImage2DFn, progress_reporter);
functions->fCopyTexSubImage2D =
bind_slow(gl->glCopyTexSubImage2DFn, progress_reporter);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
functions->fCreateProgram = [func = gl->glCreateProgramFn]() {
auto& program_create_times = GetProgramCreateTimesMap();
GLuint program = func();
@@ -326,7 +326,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface(
functions->fCullFace = gl->glCullFaceFn;
functions->fDeleteBuffers =
bind_slow(gl->glDeleteBuffersARBFn, progress_reporter);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
functions->fDeleteProgram = [func = gl->glDeleteProgramFn](GLuint program) {
auto& program_create_times = GetProgramCreateTimesMap();
program_create_times.erase(program);
@@ -356,12 +356,18 @@ sk_sp<GrGLInterface> CreateGrGLInterface(
gl->glDrawArraysInstancedANGLEFn, progress_reporter);
functions->fDrawArraysInstancedBaseInstance = bind_slow_on_mac<true>(
gl->glDrawArraysInstancedBaseInstanceANGLEFn, progress_reporter);
+ functions->fMultiDrawArraysInstancedBaseInstance = bind_slow_on_mac<true>(
+ gl->glMultiDrawArraysInstancedBaseInstanceANGLEFn, progress_reporter);
functions->fDrawElementsInstanced = bind_slow_on_mac<true>(
gl->glDrawElementsInstancedANGLEFn, progress_reporter);
functions->fDrawElementsInstancedBaseVertexBaseInstance =
bind_slow_on_mac<true>(
gl->glDrawElementsInstancedBaseVertexBaseInstanceANGLEFn,
progress_reporter);
+ functions->fMultiDrawElementsInstancedBaseVertexBaseInstance =
+ bind_slow_on_mac<true>(
+ gl->glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLEFn,
+ progress_reporter);
// GL 4.0 or GL_ARB_draw_indirect or ES 3.1
functions->fDrawArraysIndirect =
@@ -390,7 +396,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface(
functions->fGetQueryiv = gl->glGetQueryivFn;
functions->fGetProgramBinary = gl->glGetProgramBinaryFn;
functions->fGetProgramInfoLog = gl->glGetProgramInfoLogFn;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
functions->fGetProgramiv = [func = gl->glGetProgramivFn](
GLuint program, GLenum pname, GLint* params) {
func(program, pname, params);
diff --git a/chromium/ui/gl/init/gl_factory_android.cc b/chromium/ui/gl/init/gl_factory_android.cc
index f7e497b5ab5..1e3143bd5e9 100644
--- a/chromium/ui/gl/init/gl_factory_android.cc
+++ b/chromium/ui/gl/init/gl_factory_android.cc
@@ -33,7 +33,7 @@ class GLNonOwnedContext : public GLContextReal {
// Implement GLContext.
bool Initialize(GLSurface* compatible_surface,
const GLContextAttribs& attribs) override;
- bool MakeCurrent(GLSurface* surface) override;
+ bool MakeCurrentImpl(GLSurface* surface) override;
void ReleaseCurrent(GLSurface* surface) override {}
bool IsCurrent(GLSurface* surface) override { return true; }
void* GetHandle() override { return nullptr; }
@@ -56,7 +56,7 @@ bool GLNonOwnedContext::Initialize(GLSurface* compatible_surface,
return true;
}
-bool GLNonOwnedContext::MakeCurrent(GLSurface* surface) {
+bool GLNonOwnedContext::MakeCurrentImpl(GLSurface* surface) {
BindGLApi();
SetCurrent(surface);
InitializeDynamicBindings();
diff --git a/chromium/ui/gl/init/gl_factory_linux_x11.cc b/chromium/ui/gl/init/gl_factory_linux_x11.cc
index ed722461046..400372bc0c1 100644
--- a/chromium/ui/gl/init/gl_factory_linux_x11.cc
+++ b/chromium/ui/gl/init/gl_factory_linux_x11.cc
@@ -80,10 +80,12 @@ scoped_refptr<GLSurface> CreateViewGLSurfaceX11(gfx::AcceleratedWidget window) {
case kGLImplementationSwiftShaderGL:
case kGLImplementationEGLGLES2:
DCHECK(window != gfx::kNullAcceleratedWidget);
- return InitializeGLSurface(new NativeViewGLSurfaceEGLX11GLES2(window));
+ return InitializeGLSurface(
+ new NativeViewGLSurfaceEGLX11GLES2(static_cast<x11::Window>(window)));
case kGLImplementationEGLANGLE:
DCHECK(window != gfx::kNullAcceleratedWidget);
- return InitializeGLSurface(new NativeViewGLSurfaceEGLX11(window));
+ return InitializeGLSurface(
+ new NativeViewGLSurfaceEGLX11(static_cast<x11::Window>(window)));
case kGLImplementationMockGL:
case kGLImplementationStubGL:
return new GLSurfaceStub;
diff --git a/chromium/ui/gl/swap_chain_presenter.cc b/chromium/ui/gl/swap_chain_presenter.cc
index d5ba73d26a3..67ce268a608 100644
--- a/chromium/ui/gl/swap_chain_presenter.cc
+++ b/chromium/ui/gl/swap_chain_presenter.cc
@@ -19,9 +19,11 @@
#include "ui/gl/gl_image_dxgi.h"
#include "ui/gl/gl_image_memory.h"
#include "ui/gl/gl_switches.h"
+#include "ui/gl/gl_utils.h"
namespace gl {
namespace {
+
// Some drivers fail to correctly handle BT.709 video in overlays. This flag
// converts them to BT.601 in the video processor.
const base::Feature kFallbackBT709VideoToBT601{
@@ -121,6 +123,8 @@ const char* DxgiFormatToString(DXGI_FORMAT format) {
// Please also modify histogram enum and trace integration tests if new
// formats are added.
switch (format) {
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ return "RGB10A2";
case DXGI_FORMAT_B8G8R8A8_UNORM:
return "BGRA";
case DXGI_FORMAT_YUY2:
@@ -129,9 +133,15 @@ const char* DxgiFormatToString(DXGI_FORMAT format) {
return "NV12";
default:
NOTREACHED();
- return nullptr;
+ return "UNKNOWN";
}
}
+
+bool IsYUVSwapChainFormat(DXGI_FORMAT format) {
+ if (format == DXGI_FORMAT_NV12 || format == DXGI_FORMAT_YUY2)
+ return true;
+ return false;
+}
} // namespace
SwapChainPresenter::PresentationHistory::PresentationHistory() = default;
@@ -182,14 +192,21 @@ SwapChainPresenter::~SwapChainPresenter() {
base::PowerMonitor::RemoveObserver(this);
}
-bool SwapChainPresenter::ShouldUseYUVSwapChain(
- gfx::ProtectedVideoType protected_video_type) {
+DXGI_FORMAT SwapChainPresenter::GetSwapChainFormat(
+ gfx::ProtectedVideoType protected_video_type,
+ bool content_is_hdr) {
+ DXGI_FORMAT yuv_overlay_format =
+ DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR();
// TODO(crbug.com/850799): Assess power/perf impact when protected video
// swap chain is composited by DWM.
// Always prefer YUV swap chain for hardware protected video for now.
if (protected_video_type == gfx::ProtectedVideoType::kHardwareProtected)
- return true;
+ return yuv_overlay_format;
+
+ // Prefer RGB10A2 swapchain when playing HDR content.
+ if (content_is_hdr)
+ return DXGI_FORMAT_R10G10B10A2_UNORM;
// For software protected video, BGRA swap chain is preferred if hardware
// overlay is not supported for better power efficiency.
@@ -197,14 +214,14 @@ bool SwapChainPresenter::ShouldUseYUVSwapChain(
// chain is used when hardware overlay is not supported.
if (protected_video_type == gfx::ProtectedVideoType::kSoftwareProtected &&
!DirectCompositionSurfaceWin::AreOverlaysSupported())
- return false;
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
if (failed_to_create_yuv_swapchain_)
- return false;
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
// Start out as YUV.
if (!presentation_history_.Valid())
- return true;
+ return yuv_overlay_format;
int composition_count = presentation_history_.composed_count();
@@ -212,13 +229,16 @@ bool SwapChainPresenter::ShouldUseYUVSwapChain(
// aren't being used, as otherwise DWM will use the video processor a second
// time to convert it to BGRA before displaying it on screen.
- if (is_yuv_swapchain_) {
+ if (swap_chain_format_ == yuv_overlay_format) {
// Switch to BGRA once 3/4 of presents are composed.
- return composition_count < (PresentationHistory::kPresentsToStore * 3 / 4);
+ if (composition_count >= (PresentationHistory::kPresentsToStore * 3 / 4))
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
} else {
// Switch to YUV once 3/4 are using overlays (or unknown).
- return composition_count < (PresentationHistory::kPresentsToStore / 4);
+ if (composition_count < (PresentationHistory::kPresentsToStore / 4))
+ return yuv_overlay_format;
}
+ return swap_chain_format_;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> SwapChainPresenter::UploadVideoImages(
@@ -486,7 +506,8 @@ bool SwapChainPresenter::TryPresentToDecodeSwapChain(
auto not_used_reason = DecodeSwapChainNotUsedReason::kFailedToPresent;
bool nv12_supported =
- (DXGI_FORMAT_NV12 == DirectCompositionSurfaceWin::GetOverlayFormatUsed());
+ (DXGI_FORMAT_NV12 ==
+ DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR());
// TODO(sunnyps): Try using decode swap chain for uploaded video images.
if (nv12_image && nv12_supported && !failed_to_present_decode_swapchain_) {
D3D11_TEXTURE2D_DESC texture_desc = {};
@@ -611,6 +632,7 @@ bool SwapChainPresenter::PresentToDecodeSwapChain(
return false;
}
DCHECK(decode_swap_chain_);
+ SetSwapChainPresentDuration();
Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
dcomp_device_.As(&desktop_device);
@@ -649,21 +671,23 @@ bool SwapChainPresenter::PresentToDecodeSwapChain(
// internal color space state and do a better job.
// Common color spaces have primaries and transfer function similar to BT 709
// and there are no other choices anyway.
- int flags = DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_BT709;
+ int color_space_flags = DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_BT709;
// Proper Rec 709 and 601 have limited or nominal color range.
if (color_space == gfx::ColorSpace::CreateREC709() ||
color_space == gfx::ColorSpace::CreateREC601()) {
- flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_NOMINAL_RANGE;
+ color_space_flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_NOMINAL_RANGE;
}
// xvYCC allows colors outside nominal range to encode negative colors that
// allows for a wider gamut.
if (color_space.FullRangeEncodedValues()) {
- flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_xvYCC;
+ color_space_flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_xvYCC;
}
decode_swap_chain_->SetColorSpace(
- static_cast<DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAGS>(flags));
+ static_cast<DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAGS>(color_space_flags));
- HRESULT hr = decode_swap_chain_->PresentBuffer(nv12_image->level(), 1, 0);
+ UINT present_flags = DXGI_PRESENT_USE_DURATION;
+ HRESULT hr =
+ decode_swap_chain_->PresentBuffer(nv12_image->level(), 1, present_flags);
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates
// that the window is occluded and we can stop rendering.
if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
@@ -674,14 +698,13 @@ bool SwapChainPresenter::PresentToDecodeSwapChain(
last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
last_presented_images_[kNV12ImageIndex] = nv12_image;
swap_chain_size_ = swap_chain_size;
- if (is_yuv_swapchain_) {
+ if (swap_chain_format_ == DXGI_FORMAT_NV12) {
frames_since_color_space_change_++;
} else {
UMA_HISTOGRAM_COUNTS_1000(
"GPU.DirectComposition.FramesSinceColorSpaceChange",
frames_since_color_space_change_);
frames_since_color_space_change_ = 0;
- is_yuv_swapchain_ = true;
}
RecordPresentationStatistics();
return true;
@@ -689,7 +712,7 @@ bool SwapChainPresenter::PresentToDecodeSwapChain(
bool SwapChainPresenter::PresentToSwapChain(
const ui::DCRendererLayerParams& params) {
- GLImageDXGI* nv12_image =
+ GLImageDXGI* image_dxgi =
GLImageDXGI::FromGLImage(params.images[kNV12ImageIndex].get());
GLImageMemory* y_image_memory =
GLImageMemory::FromGLImage(params.images[kYPlaneImageIndex].get());
@@ -698,7 +721,7 @@ bool SwapChainPresenter::PresentToSwapChain(
GLImageD3D* swap_chain_image =
GLImageD3D::FromGLImage(params.images[kSwapChainImageIndex].get());
- if (!nv12_image && (!y_image_memory || !uv_image_memory) &&
+ if (!image_dxgi && (!y_image_memory || !uv_image_memory) &&
!swap_chain_image) {
DLOG(ERROR) << "Video GLImages are missing";
ReleaseSwapChainResources();
@@ -708,8 +731,8 @@ bool SwapChainPresenter::PresentToSwapChain(
return true;
}
- if (nv12_image && !nv12_image->texture()) {
- // We can't proceed if |nv12_image| has no underlying d3d11 texture. It's
+ if (image_dxgi && !image_dxgi->texture()) {
+ // We can't proceed if |image_dxgi| has no underlying d3d11 texture. It's
// unclear how we get into this state, but we do observe crashes due to it.
// Just stop here instead, and render incorrectly.
// https://crbug.com/1077645
@@ -719,7 +742,7 @@ bool SwapChainPresenter::PresentToSwapChain(
}
std::string image_type = "software video frame";
- if (nv12_image)
+ if (image_dxgi)
image_type = "hardware video frame";
if (swap_chain_image)
image_type = "swap chain";
@@ -730,6 +753,7 @@ bool SwapChainPresenter::PresentToSwapChain(
TRACE_EVENT2("gpu", "SwapChainPresenter::PresentToSwapChain", "image_type",
image_type, "swap_chain_size", swap_chain_size.ToString());
+ bool content_is_hdr = image_dxgi && image_dxgi->color_space().IsHDR();
// Do not create a swap chain if swap chain size will be empty.
if (swap_chain_size.IsEmpty()) {
swap_chain_size_ = swap_chain_size;
@@ -756,7 +780,7 @@ bool SwapChainPresenter::PresentToSwapChain(
return true;
}
- if (TryPresentToDecodeSwapChain(nv12_image, params.content_rect,
+ if (TryPresentToDecodeSwapChain(image_dxgi, params.content_rect,
swap_chain_size)) {
return true;
}
@@ -766,16 +790,18 @@ bool SwapChainPresenter::PresentToSwapChain(
if (swap_chain_resized) {
presentation_history_.Clear();
}
- bool use_yuv_swap_chain = ShouldUseYUVSwapChain(params.protected_video_type);
- bool toggle_yuv_swapchain = use_yuv_swap_chain != is_yuv_swapchain_;
+ DXGI_FORMAT swap_chain_format =
+ GetSwapChainFormat(params.protected_video_type, content_is_hdr);
+ bool swap_chain_format_changed = swap_chain_format != swap_chain_format_;
bool toggle_protected_video =
protected_video_type_ != params.protected_video_type;
// Try reallocating swap chain if resizing fails.
- if (!swap_chain_ || swap_chain_resized || toggle_yuv_swapchain ||
+ if (!swap_chain_ || swap_chain_resized || swap_chain_format_changed ||
toggle_protected_video) {
- if (!ReallocateSwapChain(swap_chain_size, use_yuv_swap_chain,
- params.protected_video_type, params.z_order)) {
+ if (!ReallocateSwapChain(swap_chain_size, swap_chain_format,
+ params.protected_video_type, params.z_order,
+ content_is_hdr)) {
ReleaseSwapChainResources();
return false;
}
@@ -792,11 +818,11 @@ bool SwapChainPresenter::PresentToSwapChain(
Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
UINT input_level;
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
- if (nv12_image) {
- input_texture = nv12_image->texture();
- input_level = (UINT)nv12_image->level();
+ if (image_dxgi) {
+ input_texture = image_dxgi->texture();
+ input_level = static_cast<UINT>(image_dxgi->level());
// Keyed mutex may not exist.
- keyed_mutex = nv12_image->keyed_mutex();
+ keyed_mutex = image_dxgi->keyed_mutex();
staging_texture_.Reset();
copy_texture_.Reset();
} else {
@@ -813,18 +839,19 @@ bool SwapChainPresenter::PresentToSwapChain(
// TODO(sunnyps): Use correct color space for uploaded video frames.
gfx::ColorSpace src_color_space = gfx::ColorSpace::CreateREC709();
- if (nv12_image && nv12_image->color_space().IsValid())
- src_color_space = nv12_image->color_space();
+ if (image_dxgi && image_dxgi->color_space().IsValid())
+ src_color_space = image_dxgi->color_space();
if (!VideoProcessorBlt(input_texture, input_level, keyed_mutex,
- params.content_rect, src_color_space)) {
+ params.content_rect, src_color_space,
+ content_is_hdr)) {
return false;
}
if (first_present_) {
first_present_ = false;
-
- HRESULT hr = swap_chain_->Present(0, 0);
+ UINT flags = DXGI_PRESENT_USE_DURATION;
+ HRESULT hr = swap_chain_->Present(0, flags);
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates
// that the window is occluded and we can stop rendering.
if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
@@ -864,6 +891,7 @@ bool SwapChainPresenter::PresentToSwapChain(
const bool use_swap_chain_tearing =
DirectCompositionSurfaceWin::AllowTearing();
UINT flags = use_swap_chain_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0;
+ flags |= DXGI_PRESENT_USE_DURATION;
UINT interval = use_swap_chain_tearing ? 0 : 1;
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates
// that the window is occluded and we can stop rendering.
@@ -877,12 +905,14 @@ bool SwapChainPresenter::PresentToSwapChain(
return true;
}
+void SwapChainPresenter::SetFrameRate(float frame_rate) {
+ frame_rate_ = frame_rate;
+ SetSwapChainPresentDuration();
+}
+
void SwapChainPresenter::RecordPresentationStatistics() {
- DXGI_FORMAT swap_chain_format =
- is_yuv_swapchain_ ? DirectCompositionSurfaceWin::GetOverlayFormatUsed()
- : DXGI_FORMAT_B8G8R8A8_UNORM;
base::UmaHistogramSparse("GPU.DirectComposition.SwapChainFormat3",
- swap_chain_format);
+ swap_chain_format_);
VideoPresentationMode presentation_mode;
if (decode_swap_chain_) {
@@ -900,18 +930,11 @@ void SwapChainPresenter::RecordPresentationStatistics() {
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("gpu.service"),
"SwapChain::Present", TRACE_EVENT_SCOPE_THREAD,
- "PixelFormat", DxgiFormatToString(swap_chain_format),
+ "PixelFormat", DxgiFormatToString(swap_chain_format_),
"ZeroCopy", !!decode_swap_chain_);
- HRESULT hr = 0;
- Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
- if (decode_swap_chain_) {
- hr = decode_swap_chain_.As(&swap_chain_media);
- } else {
- DCHECK(swap_chain_);
- hr = swap_chain_.As(&swap_chain_media);
- }
- if (SUCCEEDED(hr)) {
- DCHECK(swap_chain_media);
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media =
+ GetSwapChainMedia();
+ if (swap_chain_media) {
DXGI_FRAME_STATISTICS_MEDIA stats = {};
// GetFrameStatisticsMedia fails with DXGI_ERROR_FRAME_STATISTICS_DISJOINT
// sometimes, which means an event (such as power cycle) interrupted the
@@ -924,6 +947,12 @@ void SwapChainPresenter::RecordPresentationStatistics() {
if (SUCCEEDED(hr)) {
base::UmaHistogramSparse("GPU.DirectComposition.CompositionMode",
stats.CompositionMode);
+ if (frame_rate_ != 0) {
+ // [1ms, 10s] covers the fps between [0.1hz, 1000hz].
+ base::UmaHistogramTimes("GPU.DirectComposition.ApprovedPresentDuration",
+ base::TimeDelta::FromMilliseconds(
+ stats.ApprovedPresentDuration / 10000));
+ }
presentation_history_.AddSample(stats.CompositionMode);
mode = stats.CompositionMode;
}
@@ -939,7 +968,8 @@ bool SwapChainPresenter::VideoProcessorBlt(
UINT input_level,
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
const gfx::Rect& content_rect,
- const gfx::ColorSpace& src_color_space) {
+ const gfx::ColorSpace& src_color_space,
+ bool content_is_hdr) {
TRACE_EVENT2("gpu", "SwapChainPresenter::VideoProcessorBlt", "content_rect",
content_rect.ToString(), "swap_chain_size",
swap_chain_size_.ToString());
@@ -952,13 +982,16 @@ bool SwapChainPresenter::VideoProcessorBlt(
Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor =
layer_tree_->video_processor();
- gfx::ColorSpace output_color_space =
- is_yuv_swapchain_ ? src_color_space : gfx::ColorSpace::CreateSRGB();
+ gfx::ColorSpace output_color_space = IsYUVSwapChainFormat(swap_chain_format_)
+ ? src_color_space
+ : gfx::ColorSpace::CreateSRGB();
if (base::FeatureList::IsEnabled(kFallbackBT709VideoToBT601) &&
(output_color_space == gfx::ColorSpace::CreateREC709())) {
output_color_space = gfx::ColorSpace::CreateREC601();
}
+ if (content_is_hdr)
+ output_color_space = gfx::ColorSpace::CreateHDR10();
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1;
@@ -973,7 +1006,9 @@ bool SwapChainPresenter::VideoProcessorBlt(
// Set output color space.
DXGI_COLOR_SPACE_TYPE output_dxgi_color_space =
gfx::ColorSpaceWin::GetDXGIColorSpace(
- output_color_space, is_yuv_swapchain_ /* force_yuv */);
+ output_color_space,
+ IsYUVSwapChainFormat(swap_chain_format_) /* force_yuv */);
+
if (SUCCEEDED(swap_chain3->SetColorSpace1(output_dxgi_color_space))) {
context1->VideoProcessorSetOutputColorSpace1(video_processor.Get(),
output_dxgi_color_space);
@@ -1085,9 +1120,12 @@ void SwapChainPresenter::ReleaseSwapChainResources() {
bool SwapChainPresenter::ReallocateSwapChain(
const gfx::Size& swap_chain_size,
- bool use_yuv_swap_chain,
+ DXGI_FORMAT swap_chain_format,
gfx::ProtectedVideoType protected_video_type,
- bool z_order) {
+ bool z_order,
+ bool content_is_hdr) {
+ bool use_yuv_swap_chain = IsYUVSwapChainFormat(swap_chain_format);
+
TRACE_EVENT2("gpu", "SwapChainPresenter::ReallocateSwapChain", "size",
swap_chain_size.ToString(), "yuv", use_yuv_swap_chain);
@@ -1095,7 +1133,7 @@ bool SwapChainPresenter::ReallocateSwapChain(
swap_chain_size_ = swap_chain_size;
// ResizeBuffers can't change YUV flags so only attempt it when size changes.
- if (swap_chain_ && (is_yuv_swapchain_ == use_yuv_swap_chain) &&
+ if (swap_chain_ && (swap_chain_format_ == swap_chain_format) &&
(protected_video_type_ == protected_video_type)) {
output_view_.Reset();
DXGI_SWAP_CHAIN_DESC1 desc = {};
@@ -1110,13 +1148,12 @@ bool SwapChainPresenter::ReallocateSwapChain(
protected_video_type_ = protected_video_type;
- if (is_yuv_swapchain_ != use_yuv_swap_chain) {
+ if (swap_chain_format_ != swap_chain_format) {
UMA_HISTOGRAM_COUNTS_1000(
"GPU.DirectComposition.FramesSinceColorSpaceChange",
frames_since_color_space_change_);
frames_since_color_space_change_ = 0;
}
- is_yuv_swapchain_ = false;
ReleaseSwapChainResources();
@@ -1142,7 +1179,7 @@ bool SwapChainPresenter::ReallocateSwapChain(
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = swap_chain_size_.width();
desc.Height = swap_chain_size_.height();
- desc.Format = DirectCompositionSurfaceWin::GetOverlayFormatUsed();
+ desc.Format = swap_chain_format;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.BufferCount = 2;
@@ -1167,33 +1204,38 @@ bool SwapChainPresenter::ReallocateSwapChain(
const std::string protected_video_type_string =
ProtectedVideoTypeToString(protected_video_type);
- DXGI_FORMAT format_used = DirectCompositionSurfaceWin::GetOverlayFormatUsed();
if (use_yuv_swap_chain) {
TRACE_EVENT1("gpu", "SwapChainPresenter::ReallocateSwapChain::YUV",
- "format", DxgiFormatToString(format_used));
+ "format", DxgiFormatToString(swap_chain_format));
HRESULT hr = media_factory->CreateSwapChainForCompositionSurfaceHandle(
d3d11_device_.Get(), swap_chain_handle_.Get(), &desc, nullptr,
&swap_chain_);
- is_yuv_swapchain_ = SUCCEEDED(hr);
- failed_to_create_yuv_swapchain_ = !is_yuv_swapchain_;
+ failed_to_create_yuv_swapchain_ = FAILED(hr);
base::UmaHistogramSparse(kSwapChainCreationResultByFormatUmaPrefix +
- DxgiFormatToString(format_used),
+ DxgiFormatToString(swap_chain_format),
hr);
base::UmaHistogramSparse(kSwapChainCreationResultByVideoTypeUmaPrefix +
protected_video_type_string,
hr);
- if (FAILED(hr)) {
- DLOG(ERROR) << "Failed to create " << DxgiFormatToString(format_used)
+ if (failed_to_create_yuv_swapchain_) {
+ DLOG(ERROR) << "Failed to create "
+ << DxgiFormatToString(swap_chain_format)
<< " swap chain of size " << swap_chain_size.ToString()
<< " with error 0x" << std::hex << hr
<< "\nFalling back to BGRA";
+ use_yuv_swap_chain = false;
+ swap_chain_format = DXGI_FORMAT_B8G8R8A8_UNORM;
}
}
- if (!is_yuv_swapchain_) {
- TRACE_EVENT0("gpu", "SwapChainPresenter::ReallocateSwapChain::BGRA");
- desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ if (!use_yuv_swap_chain) {
+ std::ostringstream trace_event_stream;
+ trace_event_stream << "SwapChainPresenter::ReallocateSwapChain::"
+ << DxgiFormatToString(swap_chain_format);
+ TRACE_EVENT0("gpu", trace_event_stream.str().c_str());
+
+ desc.Format = swap_chain_format;
desc.Flags = 0;
if (IsProtectedVideo(protected_video_type))
desc.Flags |= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
@@ -1207,7 +1249,7 @@ bool SwapChainPresenter::ReallocateSwapChain(
&swap_chain_);
base::UmaHistogramSparse(kSwapChainCreationResultByFormatUmaPrefix +
- DxgiFormatToString(DXGI_FORMAT_B8G8R8A8_UNORM),
+ DxgiFormatToString(swap_chain_format),
hr);
base::UmaHistogramSparse(kSwapChainCreationResultByVideoTypeUmaPrefix +
protected_video_type_string,
@@ -1217,12 +1259,16 @@ bool SwapChainPresenter::ReallocateSwapChain(
// Disable overlay support so dc_layer_overlay will stop sending down
// overlay frames here and uses GL Composition instead.
DirectCompositionSurfaceWin::DisableOverlays();
- DLOG(ERROR) << "Failed to create BGRA swap chain of size "
- << swap_chain_size.ToString() << " with error 0x" << std::hex
- << hr << ". Disable overlay swap chains";
+ DLOG(ERROR) << "Failed to create "
+ << DxgiFormatToString(swap_chain_format)
+ << " swap chain of size " << swap_chain_size.ToString()
+ << " with error 0x" << std::hex << hr
+ << ". Disable overlay swap chains";
return false;
}
}
+ swap_chain_format_ = swap_chain_format;
+ SetSwapChainPresentDuration();
return true;
}
@@ -1234,4 +1280,31 @@ bool SwapChainPresenter::ShouldUseVideoProcessorScaling() {
return (!is_on_battery_power_ && !layer_tree_->disable_vp_scaling());
}
+void SwapChainPresenter::SetSwapChainPresentDuration() {
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media =
+ GetSwapChainMedia();
+ if (swap_chain_media) {
+ UINT duration_100ns = FrameRateToPresentDuration(frame_rate_);
+ HRESULT hr = swap_chain_media->SetPresentDuration(duration_100ns);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "SetPresentDuration failed with error " << std::hex << hr;
+ }
+ }
+}
+
+Microsoft::WRL::ComPtr<IDXGISwapChainMedia>
+SwapChainPresenter::GetSwapChainMedia() const {
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
+ HRESULT hr = 0;
+ if (decode_swap_chain_) {
+ hr = decode_swap_chain_.As(&swap_chain_media);
+ } else {
+ DCHECK(swap_chain_);
+ hr = swap_chain_.As(&swap_chain_media);
+ }
+ if (SUCCEEDED(hr))
+ return swap_chain_media;
+ return nullptr;
+}
+
} // namespace gl
diff --git a/chromium/ui/gl/swap_chain_presenter.h b/chromium/ui/gl/swap_chain_presenter.h
index 14fc56459cc..5947cc350c5 100644
--- a/chromium/ui/gl/swap_chain_presenter.h
+++ b/chromium/ui/gl/swap_chain_presenter.h
@@ -42,6 +42,8 @@ class SwapChainPresenter : public base::PowerObserver {
return clip_visual_;
}
+ void SetFrameRate(float frame_rate);
+
private:
// Mapped to DirectCompositonVideoPresentationMode UMA enum. Do not remove or
// remap existing entries!
@@ -100,13 +102,15 @@ class SwapChainPresenter : public base::PowerObserver {
// |use_yuv_swap_chain| is true, or BGRA otherwise. Sets flags based on
// |protected_video_type|. Returns true on success.
bool ReallocateSwapChain(const gfx::Size& swap_chain_size,
- bool use_yuv_swap_chain,
+ DXGI_FORMAT swap_chain_format,
gfx::ProtectedVideoType protected_video_type,
- bool z_order);
+ bool z_order,
+ bool content_is_hdr);
- // Returns true if YUV swap chain should be preferred over BGRA swap chain.
+ // Returns DXGI format that swap chain uses.
// This changes over time based on stats recorded in |presentation_history|.
- bool ShouldUseYUVSwapChain(gfx::ProtectedVideoType protected_video_type);
+ DXGI_FORMAT GetSwapChainFormat(gfx::ProtectedVideoType protected_video_type,
+ bool content_is_hdr);
// Perform a blit using video processor from given input texture to swap chain
// backbuffer. |input_texture| is the input texture (array), and |input_level|
@@ -118,7 +122,8 @@ class SwapChainPresenter : public base::PowerObserver {
UINT input_level,
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
const gfx::Rect& content_rect,
- const gfx::ColorSpace& src_color_space);
+ const gfx::ColorSpace& src_color_space,
+ bool content_is_hdr);
// Returns optimal swap chain size for given layer.
gfx::Size CalculateSwapChainSize(const ui::DCRendererLayerParams& params);
@@ -153,14 +158,22 @@ class SwapChainPresenter : public base::PowerObserver {
// the upscaling because it produces better results.
bool ShouldUseVideoProcessorScaling();
+ // This is called when a new swap chain is created, or when a new frame
+ // rate is received.
+ void SetSwapChainPresentDuration();
+
+ // Returns swap chain media for either |swap_chain_| or |decode_swap_chain_|,
+ // whichever is currently used.
+ Microsoft::WRL::ComPtr<IDXGISwapChainMedia> GetSwapChainMedia() const;
+
// Layer tree instance that owns this swap chain presenter.
DCLayerTree* layer_tree_ = nullptr;
// Current size of swap chain.
gfx::Size swap_chain_size_;
- // Whether the current swap chain is using the preferred YUV format.
- bool is_yuv_swapchain_ = false;
+ // Current swap chain format.
+ DXGI_FORMAT swap_chain_format_ = DXGI_FORMAT_B8G8R8A8_UNORM;
// Whether the swap chain was reallocated, and next present will be the first.
bool first_present_ = false;
@@ -231,6 +244,9 @@ class SwapChainPresenter : public base::PowerObserver {
Microsoft::WRL::ComPtr<IUnknown> decode_surface_;
bool is_on_battery_power_;
+ // Number of frames per second.
+ float frame_rate_ = 0.f;
+
DISALLOW_COPY_AND_ASSIGN(SwapChainPresenter);
};
diff --git a/chromium/ui/gl/sync_control_vsync_provider.cc b/chromium/ui/gl/sync_control_vsync_provider.cc
index 79359e7287a..bc90cd847de 100644
--- a/chromium/ui/gl/sync_control_vsync_provider.cc
+++ b/chromium/ui/gl/sync_control_vsync_provider.cc
@@ -11,7 +11,7 @@
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// These constants define a reasonable range for a calculated refresh interval.
// Calculating refreshes out of this range will be considered a fatal error.
const int64_t kMinVsyncIntervalUs = base::Time::kMicrosecondsPerSecond / 400;
@@ -26,7 +26,7 @@ const double kRelativeIntervalDifferenceThreshold = 0.05;
namespace gl {
SyncControlVSyncProvider::SyncControlVSyncProvider() : gfx::VSyncProvider() {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// On platforms where we can't get an accurate reading on the refresh
// rate we fall back to the assumption that we're displaying 60 frames
// per second.
@@ -48,7 +48,7 @@ bool SyncControlVSyncProvider::GetVSyncParametersIfAvailable(
base::TimeTicks* timebase_out,
base::TimeDelta* interval_out) {
TRACE_EVENT0("gpu", "SyncControlVSyncProvider::GetVSyncParameters");
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// The actual clock used for the system time returned by glXGetSyncValuesOML
// is unspecified. In practice, the clock used is likely to be either
// CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the
@@ -156,11 +156,11 @@ bool SyncControlVSyncProvider::GetVSyncParametersIfAvailable(
return true;
#else
return false;
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
bool SyncControlVSyncProvider::SupportGetVSyncParametersIfAvailable() const {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
return true;
#else
return false;
diff --git a/chromium/ui/gl/sync_control_vsync_provider.h b/chromium/ui/gl/sync_control_vsync_provider.h
index d958a2b23b4..3c8bb7843af 100644
--- a/chromium/ui/gl/sync_control_vsync_provider.h
+++ b/chromium/ui/gl/sync_control_vsync_provider.h
@@ -26,11 +26,11 @@ class SyncControlVSyncProvider : public gfx::VSyncProvider {
bool SupportGetVSyncParametersIfAvailable() const override;
static constexpr bool IsSupported() {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
return true;
#else
return false;
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
protected:
@@ -41,7 +41,7 @@ class SyncControlVSyncProvider : public gfx::VSyncProvider {
virtual bool GetMscRate(int32_t* numerator, int32_t* denominator) = 0;
private:
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
base::TimeTicks last_timebase_;
uint64_t last_media_stream_counter_ = 0;
base::TimeDelta last_good_interval_;
@@ -52,7 +52,7 @@ class SyncControlVSyncProvider : public gfx::VSyncProvider {
// from configuration change (monitor reconfiguration, moving windows
// between monitors, suspend and resume, etc.).
base::queue<base::TimeDelta> last_computed_intervals_;
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
DISALLOW_COPY_AND_ASSIGN(SyncControlVSyncProvider);
};
diff --git a/chromium/ui/gl/vsync_observer.h b/chromium/ui/gl/vsync_observer.h
index 5d0fe9643d8..7f7b115ab06 100644
--- a/chromium/ui/gl/vsync_observer.h
+++ b/chromium/ui/gl/vsync_observer.h
@@ -5,6 +5,8 @@
#ifndef UI_GL_VSYNC_OBSERVER_H_
#define UI_GL_VSYNC_OBSERVER_H_
+#include "base/time/time.h"
+
namespace gl {
class GL_EXPORT VSyncObserver {
public:
diff --git a/chromium/ui/gl/vsync_thread_win.h b/chromium/ui/gl/vsync_thread_win.h
index e3d65f122c2..fd516d12c4f 100644
--- a/chromium/ui/gl/vsync_thread_win.h
+++ b/chromium/ui/gl/vsync_thread_win.h
@@ -36,6 +36,8 @@ class GL_EXPORT VSyncThreadWin {
void AddObserver(VSyncObserver* obs);
void RemoveObserver(VSyncObserver* obs);
+ gfx::VSyncProvider* vsync_provider() { return &vsync_provider_; }
+
private:
friend struct base::DefaultSingletonTraits<VSyncThreadWin>;
diff --git a/chromium/ui/gl/yuv_to_rgb_converter.cc b/chromium/ui/gl/yuv_to_rgb_converter.cc
index e76c9410523..090fd55258a 100644
--- a/chromium/ui/gl/yuv_to_rgb_converter.cc
+++ b/chromium/ui/gl/yuv_to_rgb_converter.cc
@@ -129,11 +129,12 @@ YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info,
// On MacOS, the default texture target for native GpuMemoryBuffers is
// GL_TEXTURE_RECTANGLE_ARB. This is due to CGL's requirements for creating
- // a GL surface. However, when ANGLE is used on top of SwiftShader, it's
- // necessary to use GL_TEXTURE_2D instead.
+ // a GL surface. However, when ANGLE is used on top of SwiftShader or Metal,
+ // it's necessary to use GL_TEXTURE_2D instead.
// TODO(crbug.com/1056312): The proper behavior is to check the config
// parameter set by the EGL_ANGLE_iosurface_client_buffer extension
- bool is_rect = !gl_version_info.is_angle_swiftshader;
+ bool is_rect =
+ !gl_version_info.is_angle_swiftshader && !gl_version_info.is_angle_metal;
source_texture_target_ = (is_rect ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D);
const char* fragment_header = nullptr;
@@ -205,7 +206,8 @@ YUVToRGBConverter::~YUVToRGBConverter() {
void YUVToRGBConverter::CopyYUV420ToRGB(unsigned target,
const gfx::Size& size,
- unsigned rgb_texture) {
+ unsigned rgb_texture,
+ unsigned rgb_texture_type) {
GLenum source_target_getter = 0;
switch (source_texture_target_) {
case GL_TEXTURE_2D:
@@ -232,8 +234,8 @@ void YUVToRGBConverter::CopyYUV420ToRGB(unsigned target,
// Allocate the rgb texture.
glActiveTexture(old_active_texture);
glBindTexture(target, rgb_texture);
- glTexImage2D(target, 0, GL_RGB, size.width(), size.height(),
- 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(target, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB,
+ rgb_texture_type, nullptr);
// Set up and issue the draw call.
glActiveTexture(GL_TEXTURE0);
diff --git a/chromium/ui/gl/yuv_to_rgb_converter.h b/chromium/ui/gl/yuv_to_rgb_converter.h
index d228de30e59..2477a059283 100644
--- a/chromium/ui/gl/yuv_to_rgb_converter.h
+++ b/chromium/ui/gl/yuv_to_rgb_converter.h
@@ -28,7 +28,8 @@ class YUVToRGBConverter {
void CopyYUV420ToRGB(unsigned target,
const gfx::Size& size,
- unsigned rgb_texture);
+ unsigned rgb_texture,
+ unsigned rgb_texture_type);
private:
unsigned framebuffer_ = 0;
diff --git a/chromium/ui/gtk/BUILD.gn b/chromium/ui/gtk/BUILD.gn
index 02e12b9db88..3759400373b 100644
--- a/chromium/ui/gtk/BUILD.gn
+++ b/chromium/ui/gtk/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-assert(is_linux, "This file should only be referenced on Linux")
+assert(is_linux || is_chromeos, "This file should only be referenced on Linux")
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/linux/gtk/gtk.gni")
import("//build/config/ui.gni")
import("//printing/buildflags/buildflags.gni")
@@ -21,7 +20,7 @@ component("gtk_ui_delegate") {
defines = [ "IS_GTK_IMPL" ]
}
-jumbo_component("gtk") {
+component("gtk") {
public = [ "gtk_ui.h" ]
sources = [
"gtk_key_bindings_handler.cc",
diff --git a/chromium/ui/gtk/gtk_ui.cc b/chromium/ui/gtk/gtk_ui.cc
index 69d7afecc3a..9905c4f0b35 100644
--- a/chromium/ui/gtk/gtk_ui.cc
+++ b/chromium/ui/gtk/gtk_ui.cc
@@ -70,6 +70,7 @@
#if defined(USE_OZONE)
#include "ui/base/ime/input_method.h"
+#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
@@ -404,10 +405,11 @@ void GtkUi::Initialize() {
// instance instead of using GtkUi context factory. This step is made upon
// CreateInputMethod call. If the factory is not set, use the GtkUi context
// factory.
- if (!ui::OzonePlatform::GetInstance()->CreateInputMethod(
+ if (!features::IsUsingOzonePlatform() ||
+ !ui::OzonePlatform::GetInstance()->CreateInputMethod(
nullptr, gfx::kNullAcceleratedWidget)) {
- DCHECK(!ui::LinuxInputMethodContextFactory::instance());
- ui::LinuxInputMethodContextFactory::SetInstance(this);
+ if (!ui::LinuxInputMethodContextFactory::instance())
+ ui::LinuxInputMethodContextFactory::SetInstance(this);
}
#endif
diff --git a/chromium/ui/gtk/gtk_ui_delegate.h b/chromium/ui/gtk/gtk_ui_delegate.h
index 22ae1ce976e..9daed0a8f46 100644
--- a/chromium/ui/gtk/gtk_ui_delegate.h
+++ b/chromium/ui/gtk/gtk_ui_delegate.h
@@ -50,6 +50,7 @@ class COMPONENT_EXPORT(GTK) GtkUiDelegate {
// function abstracts away such functionality.
virtual bool SetGdkWindowTransientFor(GdkWindow* window,
gfx::AcceleratedWidget parent) = 0;
+ virtual void ClearTransientFor(gfx::AcceleratedWidget parent) = 0;
// Presents |window|, doing all the necessary platform-specific operations
// needed, if any.
diff --git a/chromium/ui/gtk/gtk_util.cc b/chromium/ui/gtk/gtk_util.cc
index 5a8d3a8e33a..6cd7e64be6c 100644
--- a/chromium/ui/gtk/gtk_util.cc
+++ b/chromium/ui/gtk/gtk_util.cc
@@ -156,8 +156,10 @@ aura::Window* GetAuraTransientParent(GtkWidget* dialog) {
g_object_get_data(G_OBJECT(dialog), kAuraTransientParent));
}
-void ClearAuraTransientParent(GtkWidget* dialog) {
+void ClearAuraTransientParent(GtkWidget* dialog, aura::Window* parent) {
g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, nullptr);
+ GtkUi::GetDelegate()->ClearTransientFor(
+ parent->GetHost()->GetAcceleratedWidget());
}
void ParseButtonLayout(const std::string& button_string,
diff --git a/chromium/ui/gtk/gtk_util.h b/chromium/ui/gtk/gtk_util.h
index 1104755bdf7..1512a55ed7b 100644
--- a/chromium/ui/gtk/gtk_util.h
+++ b/chromium/ui/gtk/gtk_util.h
@@ -48,7 +48,7 @@ void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent);
aura::Window* GetAuraTransientParent(GtkWidget* dialog);
// Clears the transient parent for |dialog|.
-void ClearAuraTransientParent(GtkWidget* dialog);
+void ClearAuraTransientParent(GtkWidget* dialog, aura::Window* parent);
// Parses |button_string| into |leading_buttons| and
// |trailing_buttons|. The string is of the format
diff --git a/chromium/ui/gtk/printing/print_dialog_gtk.cc b/chromium/ui/gtk/printing/print_dialog_gtk.cc
index 57900d2854d..de7cfff0801 100644
--- a/chromium/ui/gtk/printing/print_dialog_gtk.cc
+++ b/chromium/ui/gtk/printing/print_dialog_gtk.cc
@@ -190,7 +190,7 @@ PrintDialogGtk::~PrintDialogGtk() {
aura::Window* parent = gtk::GetAuraTransientParent(dialog_);
if (parent) {
parent->RemoveObserver(this);
- gtk::ClearAuraTransientParent(dialog_);
+ gtk::ClearAuraTransientParent(dialog_, parent);
}
gtk_widget_destroy(dialog_);
dialog_ = nullptr;
@@ -254,8 +254,8 @@ void PrintDialogGtk::UpdateSettings(
std::string color_value;
std::string color_setting_name;
- printing::GetColorModelForMode(settings->color(), &color_setting_name,
- &color_value);
+ printing::GetColorModelForMode(static_cast<int>(settings->color()),
+ &color_setting_name, &color_value);
gtk_print_settings_set(gtk_settings_, color_setting_name.c_str(),
color_value.c_str());
@@ -389,7 +389,7 @@ void PrintDialogGtk::PrintDocument(const printing::MetafilePlayer& metafile,
success = metafile.SaveTo(&file);
file.Close();
if (!success)
- base::DeleteFile(path_to_pdf_, false);
+ base::DeleteFile(path_to_pdf_);
}
if (!success) {
@@ -544,7 +544,7 @@ void PrintDialogGtk::InitPrintSettings(
void PrintDialogGtk::OnWindowDestroying(aura::Window* window) {
DCHECK_EQ(gtk::GetAuraTransientParent(dialog_), window);
- gtk::ClearAuraTransientParent(dialog_);
+ gtk::ClearAuraTransientParent(dialog_, window);
window->RemoveObserver(this);
if (callback_)
std::move(callback_).Run(PrintingContextLinux::CANCEL);
diff --git a/chromium/ui/gtk/select_file_dialog_impl.cc b/chromium/ui/gtk/select_file_dialog_impl.cc
index b20a1a58948..8dfc91f918e 100644
--- a/chromium/ui/gtk/select_file_dialog_impl.cc
+++ b/chromium/ui/gtk/select_file_dialog_impl.cc
@@ -9,6 +9,7 @@
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/nix/xdg_util.h"
+#include "base/no_destructor.h"
#include "base/threading/thread_restrictions.h"
namespace {
@@ -16,6 +17,10 @@ namespace {
enum UseKdeFileDialogStatus { UNKNOWN, NO_KDE, YES_KDE };
UseKdeFileDialogStatus use_kde_ = UNKNOWN;
+std::string& KDialogVersion() {
+ static base::NoDestructor<std::string> version;
+ return *version;
+}
} // namespace
@@ -42,7 +47,8 @@ ui::SelectFileDialog* SelectFileDialogImpl::Create(
// Check to see if the user dislikes the KDE file dialog.
if (!env->HasVar("NO_CHROME_KDE_FILE_DIALOG")) {
// Check to see if the KDE dialog works.
- if (SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread()) {
+ if (SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread(
+ KDialogVersion())) {
use_kde_ = YES_KDE;
}
}
@@ -58,7 +64,7 @@ ui::SelectFileDialog* SelectFileDialogImpl::Create(
base::nix::DesktopEnvironment desktop =
base::nix::GetDesktopEnvironment(env.get());
return SelectFileDialogImpl::NewSelectFileDialogImplKDE(
- listener, std::move(policy), desktop);
+ listener, std::move(policy), desktop, KDialogVersion());
}
SelectFileDialogImpl::SelectFileDialogImpl(
diff --git a/chromium/ui/gtk/select_file_dialog_impl.h b/chromium/ui/gtk/select_file_dialog_impl.h
index 64a82ff5023..3cb8ee5d6a9 100644
--- a/chromium/ui/gtk/select_file_dialog_impl.h
+++ b/chromium/ui/gtk/select_file_dialog_impl.h
@@ -37,11 +37,12 @@ class SelectFileDialogImpl : public ui::SelectFileDialog {
static SelectFileDialogImpl* NewSelectFileDialogImplKDE(
Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
- base::nix::DesktopEnvironment desktop);
+ base::nix::DesktopEnvironment desktop,
+ const std::string& kdialog_version);
// Returns true if the SelectFileDialog class returned by
// NewSelectFileDialogImplKDE will actually work.
- static bool CheckKDEDialogWorksOnUIThread();
+ static bool CheckKDEDialogWorksOnUIThread(std::string& kdialog_version);
// BaseShellDialog implementation.
void ListenerDestroyed() override;
diff --git a/chromium/ui/gtk/select_file_dialog_impl_gtk.cc b/chromium/ui/gtk/select_file_dialog_impl_gtk.cc
index 9d58610dfee..f810e48d364 100644
--- a/chromium/ui/gtk/select_file_dialog_impl_gtk.cc
+++ b/chromium/ui/gtk/select_file_dialog_impl_gtk.cc
@@ -117,7 +117,7 @@ void SelectFileDialogImplGTK::OnWindowDestroying(aura::Window* window) {
it != dialogs_.end(); ++it) {
aura::Window* parent = GetAuraTransientParent(*it);
if (parent == window)
- ClearAuraTransientParent(*it);
+ ClearAuraTransientParent(*it, parent);
}
std::set<aura::Window*>::iterator iter = parents_.find(window);
@@ -548,6 +548,7 @@ void SelectFileDialogImplGTK::OnFileChooserDestroy(GtkWidget* dialog) {
aura::Window* parent = GetAuraTransientParent(dialog);
if (!parent)
return;
+ ClearAuraTransientParent(dialog, parent);
std::set<aura::Window*>::iterator iter = parents_.find(parent);
if (iter != parents_.end()) {
(*iter)->RemoveObserver(this);
diff --git a/chromium/ui/gtk/select_file_dialog_impl_kde.cc b/chromium/ui/gtk/select_file_dialog_impl_kde.cc
index 4eba97d573d..464aa72e1d4 100644
--- a/chromium/ui/gtk/select_file_dialog_impl_kde.cc
+++ b/chromium/ui/gtk/select_file_dialog_impl_kde.cc
@@ -25,6 +25,7 @@
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
+#include "base/version.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/x/x11.h"
@@ -49,7 +50,8 @@ class SelectFileDialogImplKDE : public SelectFileDialogImpl {
public:
SelectFileDialogImplKDE(Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
- base::nix::DesktopEnvironment desktop);
+ base::nix::DesktopEnvironment desktop,
+ const std::string& kdialog_version);
protected:
~SelectFileDialogImplKDE() override;
@@ -174,6 +176,11 @@ class SelectFileDialogImplKDE : public SelectFileDialogImpl {
// dialogs. This should only be accessed on the UI thread.
std::set<gfx::AcceleratedWidget> parents_;
+ // Set to true if the kdialog version is new enough to support passing
+ // multiple extensions with descriptions, eliminating the need for the lossy
+ // conversion of extensions to mime-types.
+ bool kdialog_supports_multiple_extensions_ = false;
+
// A task runner for blocking pipe reads.
scoped_refptr<base::SequencedTaskRunner> pipe_task_runner_;
@@ -183,7 +190,8 @@ class SelectFileDialogImplKDE : public SelectFileDialogImpl {
};
// static
-bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
+bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread(
+ std::string& kdialog_version) {
// No choice. UI thread can't continue without an answer here. Fortunately we
// only do this once, the first time a file dialog is displayed.
base::ThreadRestrictions::ScopedAllowIO allow_io;
@@ -192,22 +200,24 @@ bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
cmd_vector.push_back(kKdialogBinary);
cmd_vector.push_back("--version");
base::CommandLine command_line(cmd_vector);
- std::string dummy;
- return base::GetAppOutput(command_line, &dummy);
+ return base::GetAppOutput(command_line, &kdialog_version);
}
// static
SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
- base::nix::DesktopEnvironment desktop) {
- return new SelectFileDialogImplKDE(listener, std::move(policy), desktop);
+ base::nix::DesktopEnvironment desktop,
+ const std::string& kdialog_version) {
+ return new SelectFileDialogImplKDE(listener, std::move(policy), desktop,
+ kdialog_version);
}
SelectFileDialogImplKDE::SelectFileDialogImplKDE(
Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
- base::nix::DesktopEnvironment desktop)
+ base::nix::DesktopEnvironment desktop,
+ const std::string& kdialog_version)
: SelectFileDialogImpl(listener, std::move(policy)),
desktop_(desktop),
pipe_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
@@ -216,6 +226,19 @@ SelectFileDialogImplKDE::SelectFileDialogImplKDE(
DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE5);
+ // |kdialog_version| should be of the form "kdialog 1.2.3", so split on
+ // whitespace and then try to parse a version from the second piece. If
+ // parsing fails for whatever reason, we fall back to the behavior that works
+ // with all currently known versions of kdialog.
+ std::vector<base::StringPiece> version_pieces = base::SplitStringPiece(
+ kdialog_version, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (version_pieces.size() >= 2) {
+ base::Version parsed_version(version_pieces[1]);
+ if (parsed_version.IsValid()) {
+ kdialog_supports_multiple_extensions_ =
+ parsed_version >= base::Version("19.12");
+ }
+ }
}
SelectFileDialogImplKDE::~SelectFileDialogImplKDE() = default;
@@ -288,27 +311,70 @@ bool SelectFileDialogImplKDE::HasMultipleFileTypeChoicesImpl() {
std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
DCHECK(pipe_task_runner_->RunsTasksInCurrentSequence());
- // We need a filter set because the same mime type can appear multiple times.
- std::set<std::string> filter_set;
- for (auto& extensions : file_types_.extensions) {
- for (auto& extension : extensions) {
- if (!extension.empty()) {
- std::string mime_type = base::nix::GetFileMimeType(
- base::FilePath("name").ReplaceExtension(extension));
- filter_set.insert(mime_type);
+
+ if (!kdialog_supports_multiple_extensions_) {
+ // We need a filter set because the same mime type can appear multiple
+ // times.
+ std::set<std::string> filter_set;
+ for (auto& extensions : file_types_.extensions) {
+ for (auto& extension : extensions) {
+ if (!extension.empty()) {
+ std::string mime_type = base::nix::GetFileMimeType(
+ base::FilePath("name").ReplaceExtension(extension));
+ filter_set.insert(mime_type);
+ }
}
}
+ std::vector<std::string> filter_vector(filter_set.cbegin(),
+ filter_set.cend());
+ // Add the *.* filter, but only if we have added other filters (otherwise it
+ // is implied). It needs to be added last to avoid being picked as the
+ // default filter.
+ if (file_types_.include_all_files && !file_types_.extensions.empty()) {
+ DCHECK(filter_set.find("application/octet-stream") == filter_set.end());
+ filter_vector.push_back("application/octet-stream");
+ }
+ return base::JoinString(filter_vector, " ");
}
- std::vector<std::string> filter_vector(filter_set.cbegin(),
- filter_set.cend());
- // Add the *.* filter, but only if we have added other filters (otherwise it
- // is implied). It needs to be added last to avoid being picked as the default
- // filter.
- if (file_types_.include_all_files && !file_types_.extensions.empty()) {
- DCHECK(filter_set.find("application/octet-stream") == filter_set.end());
- filter_vector.push_back("application/octet-stream");
+
+ std::vector<std::string> filters;
+ for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
+ std::set<std::string> extension_filters;
+ for (const auto& extension : file_types_.extensions[i]) {
+ if (extension.empty())
+ continue;
+ extension_filters.insert(std::string("*.") + extension);
+ }
+
+ // We didn't find any non-empty extensions to filter on.
+ if (extension_filters.empty())
+ continue;
+
+ std::vector<std::string> extension_filters_vector(extension_filters.begin(),
+ extension_filters.end());
+
+ std::string description;
+ // The description vector may be blank, in which case we are supposed to
+ // use some sort of default description based on the filter.
+ if (i < file_types_.extension_description_overrides.size()) {
+ description =
+ base::UTF16ToUTF8(file_types_.extension_description_overrides[i]);
+ // Filter out any characters that would mess up kdialog's parsing.
+ base::ReplaceChars(description, "|()", "", &description);
+ } else {
+ // There is no system default filter description so we use
+ // the extensions themselves if the description is blank.
+ description = base::JoinString(extension_filters_vector, ",");
+ }
+
+ filters.push_back(description + " (" +
+ base::JoinString(extension_filters_vector, " ") + ")");
}
- return base::JoinString(filter_vector, " ");
+
+ if (file_types_.include_all_files && !file_types_.extensions.empty())
+ filters.push_back(l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES) + " (*)");
+
+ return base::JoinString(filters, "|");
}
std::unique_ptr<SelectFileDialogImplKDE::KDialogOutputParams>
diff --git a/chromium/ui/gtk/x/BUILD.gn b/chromium/ui/gtk/x/BUILD.gn
index 9cdf6607336..f773ee3ced9 100644
--- a/chromium/ui/gtk/x/BUILD.gn
+++ b/chromium/ui/gtk/x/BUILD.gn
@@ -21,6 +21,7 @@ component("x") {
"//ui/base",
"//ui/events/platform/x11",
"//ui/gfx/x",
+ "//ui/platform_window/x11",
]
public_deps = [ "//ui/gtk:gtk_ui_delegate" ]
defines = [ "IS_UI_GTK_X_IMPL" ]
diff --git a/chromium/ui/gtk/x/DEPS b/chromium/ui/gtk/x/DEPS
new file mode 100644
index 00000000000..098786a5764
--- /dev/null
+++ b/chromium/ui/gtk/x/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/platform_window/x11",
+]
diff --git a/chromium/ui/gtk/x/gtk_event_loop_x11.cc b/chromium/ui/gtk/x/gtk_event_loop_x11.cc
index abda48cbf04..efec73ec9ce 100644
--- a/chromium/ui/gtk/x/gtk_event_loop_x11.cc
+++ b/chromium/ui/gtk/x/gtk_event_loop_x11.cc
@@ -96,7 +96,6 @@ void GtkEventLoopX11::ProcessGdkEventKey(const GdkEventKey& gdk_event_key) {
// XProto.
int state =
BuildXkbStateFromGdkEvent(gdk_event_key.state, gdk_event_key.group);
- event.xlib_event().xkey.state = state;
event.As<x11::KeyEvent>()->state = static_cast<x11::KeyButMask>(state);
// We want to process the gtk event; mapped to an X11 event immediately
diff --git a/chromium/ui/gtk/x/gtk_ui_delegate_x11.cc b/chromium/ui/gtk/x/gtk_ui_delegate_x11.cc
index 8ae0f44fe54..16a3ae2e7ae 100644
--- a/chromium/ui/gtk/x/gtk_ui_delegate_x11.cc
+++ b/chromium/ui/gtk/x/gtk_ui_delegate_x11.cc
@@ -8,15 +8,19 @@
#include <gtk/gtk.h>
#include "base/check.h"
+#include "ui/base/x/x11_util.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
#include "ui/gtk/x/gtk_event_loop_x11.h"
+#include "ui/platform_window/x11/x11_window.h"
+#include "ui/platform_window/x11/x11_window_manager.h"
namespace ui {
-GtkUiDelegateX11::GtkUiDelegateX11(XDisplay* display) : xdisplay_(display) {
- DCHECK(xdisplay_);
+GtkUiDelegateX11::GtkUiDelegateX11(x11::Connection* connection)
+ : connection_(connection) {
+ DCHECK(connection_);
gdk_set_allowed_backends("x11");
}
@@ -45,16 +49,28 @@ GdkWindow* GtkUiDelegateX11::GetGdkWindow(gfx::AcceleratedWidget window_id) {
bool GtkUiDelegateX11::SetGdkWindowTransientFor(GdkWindow* window,
gfx::AcceleratedWidget parent) {
- XSetTransientForHint(xdisplay_, GDK_WINDOW_XID(window),
- static_cast<uint32_t>(parent));
+ SetProperty(static_cast<x11::Window>(GDK_WINDOW_XID(window)),
+ x11::Atom::WM_TRANSIENT_FOR, x11::Atom::WINDOW, parent);
+
+ ui::X11Window* parent_window =
+ ui::X11WindowManager::GetInstance()->GetWindow(parent);
+ parent_window->SetTransientWindow(
+ static_cast<x11::Window>(gdk_x11_window_get_xid(window)));
+
return true;
}
+void GtkUiDelegateX11::ClearTransientFor(gfx::AcceleratedWidget parent) {
+ ui::X11Window* parent_window =
+ ui::X11WindowManager::GetInstance()->GetWindow(parent);
+ // parent_window might be dead if there was a top-down window close
+ if (parent_window)
+ parent_window->SetTransientWindow(x11::Window::None);
+}
+
GdkDisplay* GtkUiDelegateX11::GetGdkDisplay() {
- if (!display_) {
- GdkDisplay* display = gdk_x11_lookup_xdisplay(xdisplay_);
- display_ = !display ? gdk_display_get_default() : display;
- }
+ if (!display_)
+ display_ = gdk_display_get_default();
return display_;
}
@@ -62,8 +78,9 @@ void GtkUiDelegateX11::ShowGtkWindow(GtkWindow* window) {
// We need to call gtk_window_present after making the widgets visible to make
// sure window gets correctly raised and gets focus.
DCHECK(X11EventSource::HasInstance());
- gtk_window_present_with_time(window,
- X11EventSource::GetInstance()->GetTimestamp());
+ gtk_window_present_with_time(
+ window,
+ static_cast<uint32_t>(X11EventSource::GetInstance()->GetTimestamp()));
}
} // namespace ui
diff --git a/chromium/ui/gtk/x/gtk_ui_delegate_x11.h b/chromium/ui/gtk/x/gtk_ui_delegate_x11.h
index e1f252df708..730af74ed73 100644
--- a/chromium/ui/gtk/x/gtk_ui_delegate_x11.h
+++ b/chromium/ui/gtk/x/gtk_ui_delegate_x11.h
@@ -7,7 +7,7 @@
#include "base/component_export.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gtk/gtk_ui_delegate.h"
using GdkDisplay = struct _GdkDisplay;
@@ -21,7 +21,7 @@ namespace ui {
// Ozone is completed.
class COMPONENT_EXPORT(UI_GTK_X) GtkUiDelegateX11 : public GtkUiDelegate {
public:
- explicit GtkUiDelegateX11(XDisplay* display);
+ explicit GtkUiDelegateX11(x11::Connection* connection);
GtkUiDelegateX11(const GtkUiDelegateX11&) = delete;
GtkUiDelegateX11& operator=(const GtkUiDelegateX11&) = delete;
~GtkUiDelegateX11() override;
@@ -32,12 +32,13 @@ class COMPONENT_EXPORT(UI_GTK_X) GtkUiDelegateX11 : public GtkUiDelegate {
GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override;
bool SetGdkWindowTransientFor(GdkWindow* window,
gfx::AcceleratedWidget parent) override;
+ void ClearTransientFor(gfx::AcceleratedWidget parent) override;
void ShowGtkWindow(GtkWindow* window) override;
private:
GdkDisplay* GetGdkDisplay();
- XDisplay* const xdisplay_;
+ x11::Connection* const connection_;
GdkDisplay* display_ = nullptr;
};
diff --git a/chromium/ui/latency/BUILD.gn b/chromium/ui/latency/BUILD.gn
index 0e3639e5cd0..5130c405ed9 100644
--- a/chromium/ui/latency/BUILD.gn
+++ b/chromium/ui/latency/BUILD.gn
@@ -2,13 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//testing/test.gni")
-jumbo_source_set("latency") {
+source_set("latency") {
sources = [
- "average_lag_tracker.cc",
- "average_lag_tracker.h",
"latency_histogram_macros.h",
"latency_info.cc",
"latency_info.h",
@@ -25,7 +22,7 @@ jumbo_source_set("latency") {
public_deps = [ "//services/metrics/public/cpp:metrics_cpp" ]
}
-jumbo_source_set("test_support") {
+source_set("test_support") {
testonly = true
sources = [ "latency_info_test_support.cc" ]
@@ -33,10 +30,7 @@ jumbo_source_set("test_support") {
}
test("latency_unittests") {
- sources = [
- "average_lag_tracker_unittest.cc",
- "latency_info_unittest.cc",
- ]
+ sources = [ "latency_info_unittest.cc" ]
deps = [
":latency",
diff --git a/chromium/ui/latency/average_lag_tracker.cc b/chromium/ui/latency/average_lag_tracker.cc
deleted file mode 100644
index 347b32d3050..00000000000
--- a/chromium/ui/latency/average_lag_tracker.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/latency/average_lag_tracker.h"
-
-#include "base/metrics/histogram_functions.h"
-
-namespace ui {
-
-AverageLagTracker::AverageLagTracker() = default;
-
-AverageLagTracker::~AverageLagTracker() = default;
-
-void AverageLagTracker::AddLatencyInFrame(
- const ui::LatencyInfo& latency,
- base::TimeTicks gpu_swap_begin_timestamp,
- const std::string& scroll_name) {
- base::TimeTicks event_timestamp;
- bool found_component = latency.FindLatency(
- ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
- &event_timestamp);
- DCHECK(found_component);
- // Skip if no event timestamp.
- if (!found_component)
- return;
-
- if (scroll_name == "ScrollBegin") {
- AddScrollBeginInFrame(gpu_swap_begin_timestamp, event_timestamp);
- } else if (scroll_name == "ScrollUpdate" &&
- !last_event_timestamp_.is_null()) {
- AddScrollUpdateInFrame(latency, gpu_swap_begin_timestamp, event_timestamp);
- }
-
- last_event_timestamp_ = event_timestamp;
- last_event_accumulated_delta_ += latency.scroll_update_delta();
- last_rendered_accumulated_delta_ += latency.predicted_scroll_update_delta();
-}
-
-void AverageLagTracker::AddScrollBeginInFrame(
- base::TimeTicks gpu_swap_begin_timestamp,
- base::TimeTicks event_timestamp) {
- // Flush all unfinished frames.
- while (!frame_lag_infos_.empty()) {
- frame_lag_infos_.front().lag_area += LagForUnfinishedFrame(
- frame_lag_infos_.front().rendered_accumulated_delta);
- frame_lag_infos_.front().lag_area_no_prediction += LagForUnfinishedFrame(
- frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
-
- // Record UMA when it's the last item in queue.
- CalculateAndReportAverageLagUma(frame_lag_infos_.size() == 1);
- }
- // |accumulated_lag_| should be cleared/reset.
- DCHECK(accumulated_lag_ == 0);
-
- // Create ScrollBegin report, with report time equals to gpu swap time.
- LagAreaInFrame first_frame(gpu_swap_begin_timestamp);
- frame_lag_infos_.push_back(first_frame);
-
- // Reset fields.
- last_reported_time_ = event_timestamp;
- last_finished_frame_time_ = event_timestamp;
- last_event_accumulated_delta_ = 0;
- last_rendered_accumulated_delta_ = 0;
- is_begin_ = true;
-}
-
-void AverageLagTracker::AddScrollUpdateInFrame(
- const LatencyInfo& latency,
- base::TimeTicks gpu_swap_begin_timestamp,
- base::TimeTicks event_timestamp) {
- // Only accept events in nondecreasing order.
- if ((event_timestamp - last_event_timestamp_).InMilliseconds() < 0)
- return;
-
- // Pop all frames where frame_time <= event_timestamp.
- while (!frame_lag_infos_.empty() &&
- frame_lag_infos_.front().frame_time <= event_timestamp) {
- base::TimeTicks front_time =
- std::max(last_event_timestamp_, last_finished_frame_time_);
- base::TimeTicks back_time = frame_lag_infos_.front().frame_time;
- frame_lag_infos_.front().lag_area +=
- LagBetween(front_time, back_time, latency, event_timestamp,
- frame_lag_infos_.front().rendered_accumulated_delta);
- frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
- front_time, back_time, latency, event_timestamp,
- frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
-
- CalculateAndReportAverageLagUma();
- }
-
- // Initialize a new LagAreaInFrame when current_frame_time > frame_time.
- if (frame_lag_infos_.empty() ||
- gpu_swap_begin_timestamp > frame_lag_infos_.back().frame_time) {
- LagAreaInFrame new_frame(gpu_swap_begin_timestamp,
- last_rendered_accumulated_delta_,
- last_event_accumulated_delta_);
- frame_lag_infos_.push_back(new_frame);
- }
-
- // last_frame_time <= event_timestamp < frame_time
- if (!frame_lag_infos_.empty()) {
- // The front element in queue (if any) must satisfy frame_time >
- // event_timestamp, otherwise it would be popped in the while loop.
- DCHECK(last_finished_frame_time_ <= event_timestamp &&
- event_timestamp <= frame_lag_infos_.front().frame_time);
- base::TimeTicks front_time =
- std::max(last_finished_frame_time_, last_event_timestamp_);
- base::TimeTicks back_time = event_timestamp;
-
- frame_lag_infos_.front().lag_area +=
- LagBetween(front_time, back_time, latency, event_timestamp,
- frame_lag_infos_.front().rendered_accumulated_delta);
-
- frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
- front_time, back_time, latency, event_timestamp,
- frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
- }
-}
-
-float AverageLagTracker::LagBetween(base::TimeTicks front_time,
- base::TimeTicks back_time,
- const LatencyInfo& latency,
- base::TimeTicks event_timestamp,
- float rendered_accumulated_delta) {
- // In some tests, we use const event time. return 0 to avoid divided by 0.
- if (event_timestamp == last_event_timestamp_)
- return 0;
-
- float front_delta =
- (last_event_accumulated_delta_ +
- (latency.scroll_update_delta() *
- ((front_time - last_event_timestamp_).InMillisecondsF() /
- (event_timestamp - last_event_timestamp_).InMillisecondsF()))) -
- rendered_accumulated_delta;
-
- float back_delta =
- (last_event_accumulated_delta_ +
- latency.scroll_update_delta() *
-
- ((back_time - last_event_timestamp_).InMillisecondsF() /
- (event_timestamp - last_event_timestamp_).InMillisecondsF())) -
- rendered_accumulated_delta;
-
- // Calculate the trapezoid area.
- if (front_delta * back_delta >= 0) {
- return 0.5f * std::abs(front_delta + back_delta) *
- (back_time - front_time).InMillisecondsF();
- }
-
- // Corner case that rendered_accumulated_delta is in between of front_pos
- // and back_pos.
- return 0.5f *
- std::abs((front_delta * front_delta + back_delta * back_delta) /
- (back_delta - front_delta)) *
- (back_time - front_time).InMillisecondsF();
-}
-
-float AverageLagTracker::LagForUnfinishedFrame(
- float rendered_accumulated_delta) {
- base::TimeTicks last_time =
- std::max(last_event_timestamp_, last_finished_frame_time_);
- return std::abs(last_event_accumulated_delta_ - rendered_accumulated_delta) *
- (frame_lag_infos_.front().frame_time - last_time).InMillisecondsF();
-}
-
-void AverageLagTracker::CalculateAndReportAverageLagUma(bool send_anyway) {
- DCHECK(!frame_lag_infos_.empty());
- const LagAreaInFrame& frame_lag = frame_lag_infos_.front();
-
- DCHECK(frame_lag.lag_area >= 0.f);
- DCHECK(frame_lag.lag_area_no_prediction >= 0.f);
- accumulated_lag_ += frame_lag.lag_area;
- accumulated_lag_no_prediction_ += frame_lag.lag_area_no_prediction;
-
- if (is_begin_) {
- DCHECK(accumulated_lag_ == accumulated_lag_no_prediction_);
- }
-
- // |send_anyway| is true when we are flush all remaining frames on next
- // |ScrollBegin|. Otherwise record UMA when it's ScrollBegin, or when
- // reaching the 1 second gap.
- if (send_anyway || is_begin_ ||
- (frame_lag.frame_time - last_reported_time_).InSecondsF() >= 1.0f) {
- std::string scroll_name = is_begin_ ? "ScrollBegin" : "ScrollUpdate";
- const float time_delta =
- (frame_lag.frame_time - last_reported_time_).InMillisecondsF();
- const float scaled_lag = accumulated_lag_ / time_delta;
- base::UmaHistogramCounts1000(
- "Event.Latency." + scroll_name + ".Touch.AverageLag", scaled_lag);
-
- const float prediction_effect =
- (accumulated_lag_no_prediction_ - accumulated_lag_) / time_delta;
- // Log positive and negative prediction effects. ScrollBegin currently
- // doesn't take prediction into account so don't log for it.
- // Positive effect means that the prediction reduced the perceived lag,
- // where negative means prediction made lag worse (most likely due to
- // misprediction).
- if (!is_begin_) {
- if (prediction_effect >= 0.f) {
- base::UmaHistogramCounts1000(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive",
- prediction_effect);
- } else {
- base::UmaHistogramCounts1000(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative",
- -prediction_effect);
- }
- }
- accumulated_lag_ = 0;
- accumulated_lag_no_prediction_ = 0;
- last_reported_time_ = frame_lag.frame_time;
- is_begin_ = false;
- }
-
- last_finished_frame_time_ = frame_lag.frame_time;
- frame_lag_infos_.pop_front();
-}
-
-} // namespace ui
diff --git a/chromium/ui/latency/average_lag_tracker.h b/chromium/ui/latency/average_lag_tracker.h
deleted file mode 100644
index f97738f17cf..00000000000
--- a/chromium/ui/latency/average_lag_tracker.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_LATENCY_AVERAGE_LAG_TRACKER_H_
-#define UI_LATENCY_AVERAGE_LAG_TRACKER_H_
-
-#include <deque>
-
-#include "base/macros.h"
-#include "ui/latency/latency_info.h"
-
-namespace ui {
-
-// A class for reporting AverageLag metrics. See
-// https://docs.google.com/document/d/1e8NuzPblIv2B9bz01oSj40rmlse7_PHq5oFS3lqz6N4/
-class AverageLagTracker {
- public:
- AverageLagTracker();
- ~AverageLagTracker();
- void AddLatencyInFrame(const LatencyInfo& latency,
- base::TimeTicks gpu_swap_begin_timestamp,
- const std::string& scroll_name);
-
- private:
- typedef struct LagAreaInFrame {
- LagAreaInFrame(base::TimeTicks time,
- float rendered_pos = 0,
- float rendered_pos_no_prediction = 0)
- : frame_time(time),
- rendered_accumulated_delta(rendered_pos),
- lag_area(0),
- rendered_accumulated_delta_no_prediction(rendered_pos_no_prediction),
- lag_area_no_prediction(0) {}
- base::TimeTicks frame_time;
- // |rendered_accumulated_delta| is the cumulative delta that was swapped for
- // this frame; this is based on the predicted delta, if prediction is
- // enabled.
- float rendered_accumulated_delta;
- // |lag_area| is computed once a future input is processed that occurs after
- // the swap timestamp (so that we can compute how far the rendered delta
- // was from the actual position at the swap time).
- float lag_area;
- // |rendered_accumulated_delta_no_prediction| is the what would have been
- // rendered if prediction was not taken into account, i.e., the actual delta
- // from the input event.
- float rendered_accumulated_delta_no_prediction;
- // |lag_area_no_prediction| is computed the same as |lag_area| but using
- // rendered_accumulated_delta_no_prediction as the rendered delta.
- float lag_area_no_prediction;
- } LagAreaInFrame;
-
- void AddScrollBeginInFrame(base::TimeTicks gpu_swap_begin_timestamp,
- base::TimeTicks event_timestamp);
- void AddScrollUpdateInFrame(const LatencyInfo& latency,
- base::TimeTicks gpu_swap_begin_timestamp,
- base::TimeTicks event_timestamp);
-
- // Calculate lag in 1 seconds intervals and report UMA.
- void CalculateAndReportAverageLagUma(bool send_anyway = false);
-
- // Helper function to calculate lag area between |front_time| to
- // |back_time|.
- float LagBetween(base::TimeTicks front_time,
- base::TimeTicks back_time,
- const LatencyInfo& latency,
- base::TimeTicks event_time,
- float rendered_accumulated_delta);
-
- float LagForUnfinishedFrame(float rendered_accumulated_delta);
-
- std::deque<LagAreaInFrame> frame_lag_infos_;
-
- // Last scroll event's timestamp in the sequence, reset on ScrollBegin.
- base::TimeTicks last_event_timestamp_;
- // Timestamp of the last frame popped from |frame_lag_infos_| queue.
- base::TimeTicks last_finished_frame_time_;
-
- // Accumulated scroll delta for actual scroll update events. Cumulated from
- // latency.scroll_update_delta(). Reset on ScrollBegin.
- float last_event_accumulated_delta_ = 0;
- // Accumulated scroll delta got rendered on gpu swap. Cumulated from
- // latency.predicted_scroll_update_delta(). It always has same value as
- // |last_event_accumulated_delta_| when scroll prediction is disabled.
- float last_rendered_accumulated_delta_ = 0;
-
- // This keeps track of the last report_time when we report to UMA, so we can
- // calculate the report's duration by current - last. Reset on ScrollBegin.
- base::TimeTicks last_reported_time_;
-
- // True if the first element of |frame_lag_infos_| is for ScrollBegin.
- // For ScrollBegin, we don't wait for the 1 second interval but record the
- // UMA once the frame is finished.
- bool is_begin_ = false;
-
- // Accumulated lag area in the 1 second intervals.
- float accumulated_lag_ = 0;
- // Accumulated lag not taking into account the predicted deltas.
- float accumulated_lag_no_prediction_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(AverageLagTracker);
-};
-
-} // namespace ui
-
-#endif // UI_LATENCY_AVERAGE_LAG_TRACKER_H_
diff --git a/chromium/ui/latency/average_lag_tracker_unittest.cc b/chromium/ui/latency/average_lag_tracker_unittest.cc
deleted file mode 100644
index e2a0f223e60..00000000000
--- a/chromium/ui/latency/average_lag_tracker_unittest.cc
+++ /dev/null
@@ -1,540 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/latency/average_lag_tracker.h"
-
-#include "base/test/metrics/histogram_tester.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::Bucket;
-using testing::ElementsAre;
-
-namespace ui {
-namespace {
-
-class AverageLagTrackerTest : public testing::Test {
- public:
- AverageLagTrackerTest() { ResetHistograms(); }
-
- void ResetHistograms() {
- histogram_tester_ = std::make_unique<base::HistogramTester>();
- }
-
- const base::HistogramTester& histogram_tester() { return *histogram_tester_; }
-
- void SetUp() override {
- average_lag_tracker_ = std::make_unique<AverageLagTracker>();
- }
-
- void SyntheticTouchScrollBeginLatencyInfo(base::TimeTicks event_time,
- base::TimeTicks frame_time,
- float delta,
- float predicted_delta = 0) {
- ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
- touch_latency.set_scroll_update_delta(delta);
- touch_latency.set_predicted_scroll_update_delta(
- predicted_delta != 0 ? predicted_delta : delta);
-
- touch_latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
- event_time);
- touch_latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
- average_lag_tracker_->AddLatencyInFrame(touch_latency, frame_time,
- "ScrollBegin");
- }
-
- void SyntheticTouchScrollUpdateLatencyInfo(base::TimeTicks event_time,
- base::TimeTicks frame_time,
- float delta,
- float predicted_delta = 0) {
- ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
- touch_latency.set_scroll_update_delta(delta);
- touch_latency.set_predicted_scroll_update_delta(
- predicted_delta != 0 ? predicted_delta : delta);
- touch_latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, event_time);
- touch_latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
- average_lag_tracker_->AddLatencyInFrame(touch_latency, frame_time,
- "ScrollUpdate");
- }
-
- protected:
- std::unique_ptr<AverageLagTracker> average_lag_tracker_;
-
- std::unique_ptr<base::HistogramTester> histogram_tester_;
-};
-
-base::TimeTicks MillisecondsToTimeTicks(float t_ms) {
- return base::TimeTicks() + base::TimeDelta::FromMilliseconds(t_ms);
-}
-
-// Simulate a simple situation that events at every 10ms and start at t=15ms,
-// frame swaps at every 10ms too and start at t=20ms and test we record one
-// UMA for ScrollUpdate in one second.
-TEST_F(AverageLagTrackerTest, OneSecondInterval) {
- base::TimeTicks event_time =
- base::TimeTicks() + base::TimeDelta::FromMilliseconds(5);
- base::TimeTicks frame_time =
- base::TimeTicks() + base::TimeDelta::FromMilliseconds(10);
- float scroll_delta = 10;
-
- // ScrollBegin
- event_time += base::TimeDelta::FromMilliseconds(10); // 15ms
- frame_time += base::TimeDelta::FromMilliseconds(10); // 20ms
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
-
- // Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
- // per 1 second.
- const int kUpdates = 101;
- for (int i = 0; i < kUpdates; i++) {
- event_time += base::TimeDelta::FromMilliseconds(10);
- frame_time += base::TimeDelta::FromMilliseconds(10);
- // First 50 has positive delta, others negetive delta.
- const int sign = (i < kUpdates / 2) ? 1 : -1;
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- sign * scroll_delta);
- }
-
- // ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
- // at 1020ms. The last event_time that finish this report should be later than
- // 1020ms.
- EXPECT_EQ(event_time,
- base::TimeTicks() + base::TimeDelta::FromMilliseconds(1025));
- EXPECT_EQ(frame_time,
- base::TimeTicks() + base::TimeDelta::FromMilliseconds(1030));
-
- // ScrollBegin AverageLag are the area between the event original component
- // (time=15ms, delta=10px) to the frame swap time (time=20ms, expect finger
- // position at delta=15px). The AverageLag scaled to 1 second is
- // (0.5*(10px+15px)*5ms)/5ms = 12.5px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(12, 1)));
- // This ScrollUpdate AverageLag are calculated as the finger uniformly scroll
- // 10px each frame. For scroll up/down frame, the Lag at the last frame swap
- // is 5px, and Lag at this frame swap is 15px. For the one changing direction,
- // the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100,
- // plus 75. the AverageLag in 1 second is 9.975px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(9, 1)));
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
- ElementsAre(Bucket(0, 1)));
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
- ResetHistograms();
-
- // Send another ScrollBegin to end the unfinished ScrollUpdate report.
- event_time += base::TimeDelta::FromMilliseconds(10);
- frame_time += base::TimeDelta::FromMilliseconds(10);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
-
- // The last ScrollUpdate's lag is 8.75px and truncated to 8.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(8, 1)));
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
- ElementsAre(Bucket(0, 1)));
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
-}
-
-// Test the case that event's frame swap time is later than next event's
-// creation time. (i.e, event at t=10ms will be dispatch at t=30ms, while next
-// event is at t=20ms).
-TEST_F(AverageLagTrackerTest, LargerLatency) {
- base::TimeTicks event_time = MillisecondsToTimeTicks(10);
- base::TimeTicks frame_time =
- event_time + base::TimeDelta::FromMilliseconds(20);
- float scroll_delta = 10;
-
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
-
- // Send 2 ScrollUpdate. The second one will record AverageLag.ScrollBegin as
- // it's event_time is larger or equal to ScrollBegin's frame_time.
- for (int i = 0; i < 2; i++) {
- event_time += base::TimeDelta::FromMilliseconds(10);
- frame_time = event_time + base::TimeDelta::FromMilliseconds(20);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
- }
-
- // ScrollBegin AveragLag are from t=10ms to t=30ms, with absolute scroll
- // position from 10 to 30. The AverageLag should be:
- // (0.5*(10px + 30px)*20ms/20ms) = 20px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(20, 1)));
-
- // Another ScrollBegin to flush unfinished frames.
- // event_time doesn't matter here because the previous frames' lag are
- // compute from their frame_time.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
- // The to unfinished frames' lag are (finger_positon-rendered_position)*time,
- // AverageLag is ((30px-10px)*10ms+(30px-20px)*10ms)/20ms = 15px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(14, 1)));
-}
-
-// Test that multiple latency being flush in the same frame swap.
-TEST_F(AverageLagTrackerTest, TwoLatencyInfoInSameFrame) {
- // ScrollBegin
- base::TimeTicks event_time = MillisecondsToTimeTicks(10);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
- -10 /* scroll_delta */);
-
- // ScrollUpdate with event_time >= ScrollBegin frame_time will generate
- // a histogram for AverageLag.ScrollBegin.
- event_time = MillisecondsToTimeTicks(20);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- -10 /* scroll_delta */);
-
- // Absolute position from -10 to -20. The AverageLag should be:
- // (0.5*(10px + 20px)*10ms/10ms) = 15px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(14, 1)));
-
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- 5 /* scroll_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- // The ScrollUpdates are at t=20ms, finger_pos=-20px, rendered_pos=-10px,
- // at t=25ms, finger_pos=-15px, rendered_pos=-10px;
- // To t=30ms both events get flush.
- // AverageLag is (0.5*(10px+5px)*5ms + 5px*5ms)/10ms = 6.25px
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(6, 1)));
-}
-
-// Test the case that switching direction causes lag at current frame
-// time and previous frame time are in different direction.
-TEST_F(AverageLagTrackerTest, ChangeDirectionInFrame) {
- // ScrollBegin
- base::TimeTicks event_time = MillisecondsToTimeTicks(10);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
- 10 /* scroll_delta */);
-
- // At t=20, lag = 10px.
- event_time = MillisecondsToTimeTicks(20);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- 10 /* scroll_delta */);
-
- // At t=30, lag = -10px.
- event_time = MillisecondsToTimeTicks(30);
- frame_time = MillisecondsToTimeTicks(40);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- -20 /* scroll_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- // From t=20 to t=30, lag_area=2*(0.5*10px*5ms)=50px*ms.
- // From t=30 to t=40, lag_area=20px*10ms=200px*ms
- // AverageLag = (50+200)/20 = 12.5px.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(12, 1)));
-}
-
-// A simple case without scroll prediction to compare with the two with
-// prediction cases below.
-TEST_F(AverageLagTrackerTest, NoScrollPrediction) {
- // ScrollBegin, at t=5, finger_pos=5px.
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
- 5 /* scroll_delta */);
-
- // ScrollUpdate, at t=15, finger_pos=15px.
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- 10 /* scroll_delta */);
-
- // ScrollUpdate, at t=25, finger_pos=25px.
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
- 10 /* scroll_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(7, 1)));
- // At t=10, finger_pos = 10px, rendered_pos = 5px.
- // At t=20, finger_pos = 20px, rendered_pos = 15px.
- // At t=30, finger_pos = 25px, rendered_pos = 25px.
- // AverageLag = ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
- // = 9.375
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(9, 1)));
-}
-
-// Test AverageLag with perfect scroll prediction.
-TEST_F(AverageLagTrackerTest, ScrollPrediction) {
- // ScrollBegin, at t=5, finger_pos=5px.
- // Predict frame_time=10, predicted_pos = 10px.
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- SyntheticTouchScrollBeginLatencyInfo(
- event_time, frame_time, 5 /* scroll_delta */, 10 /* predicted_delta */);
-
- // ScrollUpdate, at t=15, finger_pos=15px.
- // Predict frame_time=20, predicted_pos = 20px.
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
-
- // ScrollUpdate, at t=25, finger_pos=25px.
- // Predict frame_time=30, predicted_pos = 30px.
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(7, 1)));
- // At t=10, finger_pos = 10px, rendered_pos = 10px.
- // At t=20, finger_pos = 20px, rendered_pos = 20px.
- // At t=30, finger_pos = 25px, rendered_pos = 30px.
- // AverageLag = ((0px+10px)*10ms/2 + (0px+5px)*10ms/2 + 5px*5ms)/20ms
- // = 4.375px
- // AverageLag (w/o prediction)
- // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
- // = 9.375px
- // Positive effect of prediction = 5px
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(4, 1)));
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
- ElementsAre(Bucket(5, 1)));
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
-}
-
-// Test AverageLag with imperfect scroll prediction.
-TEST_F(AverageLagTrackerTest, ImperfectScrollPrediction) {
- // ScrollBegin, at t=5, finger_pos=5px.
- // Predict frame_time=10, predicted_pos(over) = 12px.
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- SyntheticTouchScrollBeginLatencyInfo(
- event_time, frame_time, 5 /* scroll_delta */, 12 /* predicted_delta */);
-
- // ScrollUpdate, at t=15, finger_pos=15px.
- // Predict frame_time=20, predicted_pos(under) = 17px.
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 5 /* predicted_delta */);
-
- // ScrollUpdate, at t=25, finger_pos=25px.
- // Predict frame_time=30, predicted_pos(over) = 31px.
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 14 /* predicted_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(7, 1)));
- // AverageLag = ((2px*2ms/2+8px*8ms/2)+ ((3px+8px)*5ms/2+8px*5ms))/20ms
- // = 5.075px
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(5, 1)));
- // AverageLag (w/o prediction =
- // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
- // = 9.375px
- // Positive effect of prediction = 4.3px
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
- ElementsAre(Bucket(4, 1)));
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
-}
-
-TEST_F(AverageLagTrackerTest, NegativePredictionEffect) {
- // ScrollBegin, at t=5, finger_pos=5px.
- // Predict frame_time=10, predicted_pos(over) = 20px.
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- SyntheticTouchScrollBeginLatencyInfo(
- event_time, frame_time, 5 /* scroll_delta */, 20 /* predicted_delta */);
-
- // ScrollUpdate, at t=15, finger_pos=15px.
- // Predict frame_time=20, predicted_pos(over) = 60px.
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 40 /* predicted_delta */);
-
- // ScrollUpdate, at t=25, finger_pos=25px.
- // Predict frame_time=30, predicted_pos(over) = 60px.
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 0 /* predicted_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(7, 1)));
- // AverageLag = ((10px+0px)*10ms/2)+ ((40px+35px)*5ms/2+35px*5ms))/20ms
- // = 20.625px
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(20, 1)));
- // AverageLag (w/o prediction =
- // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
- // = 9.375px
- // Negative effect of prediction = 11.25
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive", 0);
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative"),
- ElementsAre(Bucket(11, 1)));
-}
-
-TEST_F(AverageLagTrackerTest, NoPredictionEffect) {
- // ScrollBegin, at t=5, finger_pos=5px.
- // Predict frame_time=10, predicted_pos(over) = 25px.
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- SyntheticTouchScrollBeginLatencyInfo(
- event_time, frame_time, 5 /* scroll_delta */, 25 /* predicted_delta */);
-
- // ScrollUpdate, at t=15, finger_pos=15px.
- // Predict frame_time=20, predicted_pos(over) = 32px.
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 7 /* predicted_delta */);
-
- // ScrollUpdate, at t=25, finger_pos=25px.
- // Predict frame_time=30, predicted_pos(over) = 37px.
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(
- event_time, frame_time, 10 /* scroll_delta */, 5 /* predicted_delta */);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollBegin.Touch.AverageLag"),
- ElementsAre(Bucket(7, 1)));
- // AverageLag = ((15px+5px)*10ms/2 + (12px+7px)*5ms/2 + 7px*5ms)/20ms
- // = 9.125px
- EXPECT_THAT(histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag"),
- ElementsAre(Bucket(9, 1)));
- // AverageLag (w/o prediction) =
- // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
- // = 9.375px
- // Prediction slightly positive, we should see a 0 bucket in
- // PredictionPositive UMA
- EXPECT_THAT(
- histogram_tester().GetAllSamples(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
- ElementsAre(Bucket(0, 1)));
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
-}
-
-// Tests that when an event arrives out-of-order, the average lag tracker
-// properly ignores it.
-TEST_F(AverageLagTrackerTest, EventOutOfOrder) {
- base::TimeTicks event_time = MillisecondsToTimeTicks(5);
- base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
- float scroll_delta = 5.f;
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
-
- event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(20);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
-
- event_time = MillisecondsToTimeTicks(25);
- frame_time = MillisecondsToTimeTicks(30);
- SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
-
- // A ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(1000);
- frame_time = MillisecondsToTimeTicks(1000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag", 1);
-
- // Send an event whose timestamp is earlier than the most recent event,
- // representing an event that gets process out of order.
- base::TimeTicks earlier_event_time = MillisecondsToTimeTicks(15);
- frame_time = MillisecondsToTimeTicks(1010);
- SyntheticTouchScrollUpdateLatencyInfo(earlier_event_time, frame_time,
- scroll_delta);
-
- // Another ScrollBegin to flush unfinished frames.
- event_time = MillisecondsToTimeTicks(2000);
- frame_time = MillisecondsToTimeTicks(2000);
- SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
-
- // Ensure that the event was ignored.
- histogram_tester().ExpectTotalCount(
- "Event.Latency.ScrollUpdate.Touch.AverageLag", 1);
-}
-
-} // namespace
-} // namespace ui
diff --git a/chromium/ui/latency/latency_info.dot b/chromium/ui/latency/latency_info.dot
index 3490ac1a989..887d4e9eae4 100644
--- a/chromium/ui/latency/latency_info.dot
+++ b/chromium/ui/latency/latency_info.dot
@@ -8,8 +8,6 @@ digraph LatencyInfo {
node[style="dotted,rounded"];
"Event.Latency.EventToRender.TouchpadPinch";
- "Event.Latency.QueueingTime.<event_name><default_action_status>";
- "Event.Latency.BlockingTime.<event_name><default_action_status>";
end_to_end_metrics
[label="\
Event.Latency.EndToEnd.KeyPress\n\
@@ -56,11 +54,7 @@ INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT"];
// Layout the rest of the components.
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT->
"Event.Latency.EventToRender.TouchpadPinch"->
- INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT->
- "Event.Latency.QueueingTime.<event_name><default_action_status>"->
- INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT->
- "Event.Latency.BlockingTime.<event_name><default_action_status>"->
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT
+ INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT
[weight=3];
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT->
@@ -94,7 +88,7 @@ INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT"];
"Event.Latency.<scroll_name>.Touch.RAFTimeToFrameSwapEnd"->
INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT;
- // Add legend and position it under INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT.
+ // Add legend and position it under INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT.
legend
[shape=plaintext,label="\
LEGEND:\l\
@@ -102,7 +96,7 @@ LEGEND:\l\
<input_modality> = (Wheel | Touch)\l\
<scroll_name> = (ScrollBegin | ScrollUpdate)\l\
<thread_name> = (Main | Impl)\l"];
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT->
+ INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT->
legend
- [style=invis,minlen=3];
+ [style=invis,minlen=7];
}
diff --git a/chromium/ui/latency/latency_tracker.cc b/chromium/ui/latency/latency_tracker.cc
index a724b21d32e..7f059ff7672 100644
--- a/chromium/ui/latency/latency_tracker.cc
+++ b/chromium/ui/latency/latency_tracker.cc
@@ -308,11 +308,6 @@ void LatencyTracker::ComputeEndToEndLatencyHistograms(
DCHECK_AND_RETURN_ON_FAIL(found_component);
}
- if (!IsInertialScroll(latency) && input_modality == "Touch") {
- average_lag_tracker_.AddLatencyInFrame(latency, gpu_swap_begin_timestamp,
- scroll_name);
- }
-
// Inertial and scrollbar scrolls are excluded from Ukm metrics.
if ((input_modality == "Touch" && !IsInertialScroll(latency)) ||
input_modality == "Wheel") {
diff --git a/chromium/ui/latency/latency_tracker.h b/chromium/ui/latency/latency_tracker.h
index e0bb699a1f3..88f56c4a41b 100644
--- a/chromium/ui/latency/latency_tracker.h
+++ b/chromium/ui/latency/latency_tracker.h
@@ -7,7 +7,6 @@
#include "base/callback_forward.h"
#include "base/macros.h"
-#include "ui/latency/average_lag_tracker.h"
#include "ui/latency/latency_info.h"
namespace ui {
@@ -53,8 +52,6 @@ class LatencyTracker {
const LatencyInfo& latency,
bool top_controls_visible_height_changed);
- AverageLagTracker average_lag_tracker_;
-
DISALLOW_COPY_AND_ASSIGN(LatencyTracker);
};
diff --git a/chromium/ui/login/account_picker/chromeos_user_pod_row.js b/chromium/ui/login/account_picker/chromeos_user_pod_row.js
index 90981aa758e..fa262d692c2 100644
--- a/chromium/ui/login/account_picker/chromeos_user_pod_row.js
+++ b/chromium/ui/login/account_picker/chromeos_user_pod_row.js
@@ -4684,9 +4684,6 @@ cr.define('login', function() {
}
this.handleAfterPodPlacement_();
- // This is a hack for https://crbug.com/875128.
- if (Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE)
- document.documentElement.removeAttribute('full-screen-dialog');
},
/**
diff --git a/chromium/ui/login/display_manager.js b/chromium/ui/login/display_manager.js
index a57441e1816..9b0d5c7038f 100644
--- a/chromium/ui/login/display_manager.js
+++ b/chromium/ui/login/display_manager.js
@@ -9,9 +9,9 @@
// <include src="display_manager_types.js">
// TODO(xiyuan): Find a better to share those constants.
+/** @const */ var SCREEN_WELCOME = 'connect';
/** @const */ var SCREEN_OOBE_NETWORK = 'network-selection';
/** @const */ var SCREEN_OOBE_HID_DETECTION = 'hid-detection';
-/** @const */ var SCREEN_OOBE_EULA = 'eula';
/** @const */ var SCREEN_OOBE_ENABLE_DEBUGGING = 'debugging';
/** @const */ var SCREEN_OOBE_UPDATE = 'oobe-update';
/** @const */ var SCREEN_OOBE_RESET = 'reset';
@@ -42,9 +42,10 @@
/** @const */ var SCREEN_DISCOVER = 'discover';
/** @const */ var SCREEN_MARKETING_OPT_IN = 'marketing-opt-in';
-/* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */
+/* Accelerator identifiers.
+ * Must be kept in sync with webui_accelerator_mapping.cc.
+ */
/** @const */ var ACCELERATOR_CANCEL = 'cancel';
-/** @const */ var ACCELERATOR_ENABLE_DEBBUGING = 'debugging';
/** @const */ var ACCELERATOR_ENROLLMENT = 'enrollment';
/** @const */ var ACCELERATOR_KIOSK_ENABLE = 'kiosk_enable';
/** @const */ var ACCELERATOR_VERSION = 'version';
@@ -55,7 +56,6 @@
/** @const */ var ACCELERATOR_APP_LAUNCH_BAILOUT = 'app_launch_bailout';
/** @const */ var ACCELERATOR_APP_LAUNCH_NETWORK_CONFIG =
'app_launch_network_config';
-/** @const */ var ACCELERATOR_DEMO_MODE = "demo_mode";
/** @const */ var ACCELERATOR_SEND_FEEDBACK = "send_feedback";
/* Possible UI states of the error screen. */
@@ -93,7 +93,6 @@ cr.define('cr.ui.login', function() {
*/
var RESET_AVAILABLE_SCREEN_GROUP = [
SCREEN_OOBE_NETWORK,
- SCREEN_OOBE_EULA,
SCREEN_OOBE_AUTO_ENROLLMENT_CHECK,
SCREEN_GAIA_SIGNIN,
SCREEN_ACCOUNT_PICKER,
@@ -111,19 +110,6 @@ cr.define('cr.ui.login', function() {
];
/**
- * Group of screens (screen IDs) where enable debuggingscreen invocation is
- * available. Newer screens using Polymer use the attribute
- * `enableDebuggingAllowed` in their `ready()` method.
- * @type Array<string>
- * @const
- */
- var ENABLE_DEBUGGING_AVAILABLE_SCREEN_GROUP = [
- SCREEN_OOBE_NETWORK,
- SCREEN_OOBE_EULA,
- SCREEN_OOBE_UPDATE
- ];
-
- /**
* As Polymer behaviors do not provide true inheritance, when two behaviors
* would declare same method one of them will be hidden. Also, if element
* re-declares the method it needs explicitly iterate over behaviors and call
@@ -392,24 +378,12 @@ cr.define('cr.ui.login', function() {
if (this.currentScreen && this.currentScreen.cancel) {
this.currentScreen.cancel();
}
- } else if (name == ACCELERATOR_ENABLE_DEBBUGING) {
- if (attributes.enableDebuggingAllowed ||
- ENABLE_DEBUGGING_AVAILABLE_SCREEN_GROUP.indexOf(currentStepId) !=
- -1) {
- chrome.send('toggleEnableDebuggingScreen');
- }
} else if (name == ACCELERATOR_ENROLLMENT) {
if (attributes.startEnrollmentAllowed ||
currentStepId == SCREEN_GAIA_SIGNIN ||
currentStepId == SCREEN_PACKAGED_LICENSE ||
currentStepId == SCREEN_ACCOUNT_PICKER) {
chrome.send('toggleEnrollmentScreen');
- } else if (attributes.postponeEnrollmentAllowed ||
- currentStepId == SCREEN_OOBE_NETWORK ||
- currentStepId == SCREEN_OOBE_EULA) {
- // In this case update check will be skipped and OOBE will
- // proceed straight to enrollment screen when EULA is accepted.
- chrome.send('skipUpdateEnrollAfterEula');
} else {
console.warn('No action for current step ID: ' + currentStepId);
}
@@ -442,8 +416,6 @@ cr.define('cr.ui.login', function() {
} else if (name == ACCELERATOR_APP_LAUNCH_NETWORK_CONFIG) {
if (currentStepId == SCREEN_APP_LAUNCH_SPLASH)
chrome.send('networkConfigRequest');
- } else if (name == ACCELERATOR_DEMO_MODE) {
- this.startDemoModeFlow();
} else if (name == ACCELERATOR_SEND_FEEDBACK) {
chrome.send('sendFeedback');
}
@@ -777,7 +749,12 @@ cr.define('cr.ui.login', function() {
initializeDemoModeMultiTapListener: function() {
if (this.displayType_ == DISPLAY_TYPE.OOBE) {
this.demoModeStartListener_ = new MultiTapDetector(
- $('outer-container'), 10, this.startDemoModeFlow.bind(this));
+ $('outer-container'), 10, () => {
+ let currentScreen = Oobe.getInstance().currentScreen;
+ if (currentScreen.id === SCREEN_WELCOME) {
+ currentScreen.onSetupDemoModeGesture();
+ }
+ });
}
},
@@ -864,43 +841,6 @@ cr.define('cr.ui.login', function() {
},
/**
- * Starts demo mode flow. Shows the enable demo mode dialog if needed.
- */
- startDemoModeFlow: function() {
- var isDemoModeEnabled = loadTimeData.getBoolean('isDemoModeEnabled');
- if (!isDemoModeEnabled) {
- console.warn('Cannot setup demo mode, because it is disabled.');
- return;
- }
-
- var currentStepId = this.screens_[this.currentStep_];
- var attributes = this.screensAttributes_[this.currentStep_] || {};
- if (!attributes.enterDemoModeAllowed)
- return;
-
- if (!this.enableDemoModeDialog_) {
- this.enableDemoModeDialog_ =
- new cr.ui.dialogs.ConfirmDialog(document.body);
- this.enableDemoModeDialog_.setOkLabel(
- loadTimeData.getString('enableDemoModeDialogConfirm'));
- this.enableDemoModeDialog_.setCancelLabel(
- loadTimeData.getString('enableDemoModeDialogCancel'));
- }
- var configuration = Oobe.getInstance().getOobeConfiguration();
- if (configuration && configuration.enableDemoMode) {
- // Bypass showing dialog.
- chrome.send('setupDemoMode');
- } else {
- this.enableDemoModeDialog_.showWithTitle(
- loadTimeData.getString('enableDemoModeDialogTitle'),
- loadTimeData.getString('enableDemoModeDialogText'),
- function() { // onOk
- chrome.send('setupDemoMode');
- });
- }
- },
-
- /**
* Returns true if Oobe UI is shown.
*/
isOobeUI: function() {
diff --git a/chromium/ui/login/display_manager_types.js b/chromium/ui/login/display_manager_types.js
index d94b11a1d8e..0d60aef4790 100644
--- a/chromium/ui/login/display_manager_types.js
+++ b/chromium/ui/login/display_manager_types.js
@@ -9,9 +9,6 @@
/**
* @typedef {{
- * enableDebuggingAllowed: (boolean|undefined),
- * enterDemoModeAllowed: (boolean|undefined),
- * postponeEnrollmentAllowed: (boolean|undefined),
* resetAllowed: (boolean|undefined),
* startEnrollmentAllowed: (boolean|undefined),
* toggleKioskAllowed: (boolean|undefined),
@@ -21,24 +18,6 @@
var DisplayManagerScreenAttributes = {};
/**
- * True if showing "enable debugging" is allowed for the screen.
- * @type {boolean|undefined}
- */
-DisplayManagerScreenAttributes.enableDebuggingAllowed;
-
-/**
- * True if enabling demo mode is allowed for the screen.
- * @type {boolean|undefined}
- */
-DisplayManagerScreenAttributes.enterDemoModeAllowed;
-
-/**
- * True if enrollment accelerator should schedule postponed enrollment.
- * @type {boolean|undefined}
- */
-DisplayManagerScreenAttributes.postponeEnrollmentAllowed;
-
-/**
* True if device reset is allowed on the screen.
* @type {boolean|undefined}
*/
@@ -97,4 +76,5 @@ var OOBE_UI_STATE = {
BLOCKING: 10,
KIOSK: 11,
MIGRATION: 12,
+ USER_CREATION: 15,
};
diff --git a/chromium/ui/login/oobe.css b/chromium/ui/login/oobe.css
index 5a117bdb754..1bbcb525e90 100644
--- a/chromium/ui/login/oobe.css
+++ b/chromium/ui/login/oobe.css
@@ -38,7 +38,9 @@ body {
top: 0;
}
-html[full-screen-dialog] body {
+/* Keep full-screen white background even when user signs into session after
+ * oobe */
+html[screen=oobe] body {
background-color: white;
}
@@ -47,6 +49,7 @@ html {
--oobe-dialog-footer-height: 96px;
--oobe-dialog-footer-padding: 32px;
--oobe-dialog-content-padding: 64px;
+ --oobe-dialog-content-padding-top: 20px;
/* This size fits 675px screen with docked magnifier and shelf. Basically this
* is calc(675px * (1 - 1 / 3) - 10px - var(--shelf-area-height-base)) where
@@ -57,6 +60,14 @@ html {
--oobe-dialog-side-margin: 48px;
--oobe-dialog-adaptable-flex-direction: column;
+
+ --oobe-forward-slide-animation: translateX(+100%);
+ --oobe-backward-slide-animation: translateX(-100%);
+}
+
+html[dir=rtl] {
+ --oobe-forward-slide-animation: translateX(-100%);
+ --oobe-backward-slide-animation: translateX(+100%);
}
html[screen=gaia-signin] {
diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn
index 1b1ae8aec81..6bff398e051 100644
--- a/chromium/ui/message_center/BUILD.gn
+++ b/chromium/ui/message_center/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//components/vector_icons/vector_icons.gni")
import("//testing/test.gni")
@@ -24,7 +23,7 @@ aggregate_vector_icons("message_center_vector_icons") {
}
# TODO(msw|mukai|dewittj): Move ash-specific files: crbug.com/585175
-jumbo_component("message_center") {
+component("message_center") {
deps = [
"//base",
"//ui/base",
@@ -122,7 +121,7 @@ jumbo_component("message_center") {
"//ui/views",
]
if (is_mac) {
- libs = [ "Foundation.framework" ]
+ frameworks = [ "Foundation.framework" ]
}
}
} else {
@@ -138,6 +137,8 @@ if (enable_message_center) {
sources = [
"fake_message_center.cc",
"fake_message_center.h",
+ "lock_screen/fake_lock_screen_controller.cc",
+ "lock_screen/fake_lock_screen_controller.h",
]
deps = [
@@ -155,9 +156,11 @@ if (enable_message_center) {
}
test("message_center_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [
- "lock_screen/fake_lock_screen_controller.cc",
- "lock_screen/fake_lock_screen_controller.h",
"message_center_impl_unittest.cc",
"notification_list_unittest.cc",
"public/cpp/notification_delegate_unittest.cc",
diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc
index eb61bf4e61c..55eacef790c 100644
--- a/chromium/ui/message_center/message_center_impl.cc
+++ b/chromium/ui/message_center/message_center_impl.cc
@@ -247,10 +247,14 @@ void MessageCenterImpl::RemoveNotification(const std::string& id,
scoped_refptr<NotificationDelegate> delegate =
notification_list_->GetNotificationDelegate(copied_id);
+
+ // Remove notification before calling the Close method in case it calls
+ // RemoveNotification reentrantly.
+ notification_list_->RemoveNotification(copied_id);
+
if (delegate.get())
delegate->Close(by_user);
- notification_list_->RemoveNotification(copied_id);
visible_notifications_ =
notification_list_->GetVisibleNotifications(blockers_);
for (auto& observer : observer_list_)
diff --git a/chromium/ui/message_center/message_center_impl_unittest.cc b/chromium/ui/message_center/message_center_impl_unittest.cc
index 757a5e7e96e..c12ef6d2bf7 100644
--- a/chromium/ui/message_center/message_center_impl_unittest.cc
+++ b/chromium/ui/message_center/message_center_impl_unittest.cc
@@ -10,11 +10,11 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "base/test/task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -136,6 +136,28 @@ class TestDelegate : public NotificationDelegate {
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
+class DeleteOnCloseDelegate : public NotificationDelegate {
+ public:
+ DeleteOnCloseDelegate(MessageCenter* message_center,
+ const std::string& notification_id)
+ : message_center_(message_center), notification_id_(notification_id) {}
+ DeleteOnCloseDelegate(const DeleteOnCloseDelegate&) = delete;
+ DeleteOnCloseDelegate& operator=(const DeleteOnCloseDelegate&) = delete;
+
+ void Close(bool by_user) override {
+ // Removing the same notification inside Close should be a noop.
+ message_center_->RemoveNotification(notification_id_, false /* by_user */);
+ }
+ void Click(const base::Optional<int>& button_index,
+ const base::Optional<base::string16>& reply) override {}
+
+ private:
+ ~DeleteOnCloseDelegate() override = default;
+
+ MessageCenter* message_center_;
+ std::string notification_id_;
+};
+
// The default app id used to create simple notifications.
const std::string kDefaultAppId = "app1";
@@ -415,7 +437,7 @@ TEST_F(MessageCenterImplTest, PopupTimersControllerRestartOnUpdate) {
scoped_refptr<base::TestMockTimeTaskRunner> task_runner(
new base::TestMockTimeTaskRunner(base::Time::Now(),
base::TimeTicks::Now()));
- base::MessageLoopCurrent::Get()->SetTaskRunner(task_runner);
+ base::CurrentThread::Get()->SetTaskRunner(task_runner);
NotifierId notifier_id(GURL("https://example.com"));
@@ -456,7 +478,7 @@ TEST_F(MessageCenterImplTest, PopupTimersControllerRestartOnUpdate) {
task_runner->FastForwardBy(base::TimeDelta::FromSeconds(2));
ASSERT_EQ(popup_timers_controller->timer_finished(), 1);
- base::MessageLoopCurrent::Get()->SetTaskRunner(old_task_runner);
+ base::CurrentThread::Get()->SetTaskRunner(old_task_runner);
}
TEST_F(MessageCenterImplTest, Renotify) {
@@ -912,6 +934,24 @@ TEST_F(MessageCenterImplTest, RemoveNonVisibleNotification) {
EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size());
}
+TEST_F(MessageCenterImplTest, RemoveInCloseHandler) {
+ std::string id("id1");
+
+ // Create a notification that calls RemoveNotification() on close.
+ auto notification = std::make_unique<Notification>(
+ NOTIFICATION_TYPE_SIMPLE, id, UTF8ToUTF16("title"), UTF8ToUTF16(id),
+ gfx::Image() /* icon */, base::string16() /* display_source */, GURL(),
+ NotifierId(NotifierType::APPLICATION, kDefaultAppId),
+ RichNotificationData(),
+ base::MakeRefCounted<DeleteOnCloseDelegate>(message_center(), id));
+ message_center()->AddNotification(std::move(notification));
+ EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));
+
+ // Then remove the notification which calls RemoveNotification() reentrantly.
+ message_center()->RemoveNotification(id, true /* by_user */);
+ EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));
+}
+
TEST_F(MessageCenterImplTest, FindNotificationsByAppId) {
message_center()->SetHasMessageCenterView(true);
diff --git a/chromium/ui/message_center/public/cpp/BUILD.gn b/chromium/ui/message_center/public/cpp/BUILD.gn
index 6dc7ee3b7af..59337d831c0 100644
--- a/chromium/ui/message_center/public/cpp/BUILD.gn
+++ b/chromium/ui/message_center/public/cpp/BUILD.gn
@@ -2,10 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
# C++ headers and sources that can be used outside message_center.
-jumbo_component("cpp") {
+component("cpp") {
output_name = "ui_message_center_cpp"
sources = [
diff --git a/chromium/ui/message_center/public/cpp/message_center_constants.h b/chromium/ui/message_center/public/cpp/message_center_constants.h
index b912fa72a42..c623637a0a5 100644
--- a/chromium/ui/message_center/public/cpp/message_center_constants.h
+++ b/chromium/ui/message_center/public/cpp/message_center_constants.h
@@ -93,7 +93,7 @@ constexpr SkColor kHoveredButtonBackgroundColor = SkColorSetRGB(243, 243, 243);
// Progress bar.
const int kProgressBarTopPadding = 16;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
const int kProgressBarThickness = 5;
const int kProgressBarCornerRadius = 3;
#endif
diff --git a/chromium/ui/message_center/public/cpp/notification.h b/chromium/ui/message_center/public/cpp/notification.h
index 4214799f36f..03817627f7f 100644
--- a/chromium/ui/message_center/public/cpp/notification.h
+++ b/chromium/ui/message_center/public/cpp/notification.h
@@ -168,7 +168,7 @@ class MESSAGE_CENTER_PUBLIC_EXPORT RichNotificationData {
// Usually, it should not be set directly.
// For system notification, ash::CreateSystemNotification with
// SystemNotificationWarningLevel should be used.
- SkColor accent_color = SK_ColorTRANSPARENT;
+ base::Optional<SkColor> accent_color;
// Controls whether a settings button should appear on the notification. See
// enum definition. TODO(estade): turn this into a boolean. See
@@ -393,7 +393,9 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification {
return optional_fields_.accessible_name;
}
- SkColor accent_color() const { return optional_fields_.accent_color; }
+ base::Optional<SkColor> accent_color() const {
+ return optional_fields_.accent_color;
+ }
void set_accent_color(SkColor accent_color) {
optional_fields_.accent_color = accent_color;
}
diff --git a/chromium/ui/message_center/views/message_popup_collection_unittest.cc b/chromium/ui/message_center/views/message_popup_collection_unittest.cc
index 9d5a3d675b9..91ccc31ebab 100644
--- a/chromium/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/chromium/ui/message_center/views/message_popup_collection_unittest.cc
@@ -438,7 +438,7 @@ TEST_F(MessagePopupCollectionTest, UpdateContents) {
}
// Failiing on MacOS 10.10. https://crbug.com/1047503
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_UpdateContentsCausesPopupClose \
DISABLED_UpdateContentsCausesPopupClose
#else
diff --git a/chromium/ui/message_center/views/message_popup_view.cc b/chromium/ui/message_center/views/message_popup_view.cc
index bd6fac810b5..826d95b2199 100644
--- a/chromium/ui/message_center/views/message_popup_view.cc
+++ b/chromium/ui/message_center/views/message_popup_view.cc
@@ -70,7 +70,7 @@ void MessagePopupView::UpdateContents(const Notification& notification) {
}
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
float MessagePopupView::GetOpacity() const {
if (!IsWidgetValid())
return 0.f;
diff --git a/chromium/ui/message_center/views/notification_header_view.cc b/chromium/ui/message_center/views/notification_header_view.cc
index a9f6984e6cb..a9d2c14c944 100644
--- a/chromium/ui/message_center/views/notification_header_view.cc
+++ b/chromium/ui/message_center/views/notification_header_view.cc
@@ -351,8 +351,8 @@ void NotificationHeaderView::SetExpanded(bool expanded) {
NotifyAccessibilityEvent(ax::mojom::Event::kStateChanged, true);
}
-void NotificationHeaderView::SetAccentColor(SkColor color) {
- accent_color_ = color;
+void NotificationHeaderView::SetAccentColor(base::Optional<SkColor> color) {
+ accent_color_ = std::move(color);
UpdateColors();
}
diff --git a/chromium/ui/message_center/views/notification_header_view.h b/chromium/ui/message_center/views/notification_header_view.h
index f3285564b70..bc391f180d3 100644
--- a/chromium/ui/message_center/views/notification_header_view.h
+++ b/chromium/ui/message_center/views/notification_header_view.h
@@ -41,9 +41,10 @@ class MESSAGE_CENTER_EXPORT NotificationHeaderView : public views::Button {
void SetExpandButtonEnabled(bool enabled);
void SetExpanded(bool expanded);
- // Calls UpdateColors() to set the unified theme color used among the
- // app icon, app name, and expand button.
- void SetAccentColor(SkColor color);
+ // Calls UpdateColors() to set the unified theme color used among the app
+ // icon, app name, and expand button. If set to base::nullopt it will use the
+ // NotificationDefaultAccentColor from the native theme.
+ void SetAccentColor(base::Optional<SkColor> color);
// Sets the background color of the notification. This is used to ensure that
// the accent color has enough contrast against the background.
diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc
index 606a596838c..efc7a520c74 100644
--- a/chromium/ui/message_center/views/notification_view_md.cc
+++ b/chromium/ui/message_center/views/notification_view_md.cc
@@ -5,7 +5,9 @@
#include "ui/message_center/views/notification_view_md.h"
#include <stddef.h>
+#include <algorithm>
#include <memory>
+#include <utility>
#include "base/i18n/case_conversion.h"
#include "base/metrics/histogram_macros.h"
@@ -39,10 +41,10 @@
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
#include "ui/views/animation/ink_drop_highlight.h"
#include "ui/views/animation/ink_drop_impl.h"
-#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/image_view.h"
@@ -53,6 +55,7 @@
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/native_cursor.h"
+#include "ui/views/style/typography.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -64,7 +67,6 @@ namespace {
constexpr gfx::Insets kContentRowPadding(0, 12, 16, 12);
constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8);
constexpr int kActionsRowHorizontalSpacing = 8;
-constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12);
constexpr gfx::Insets kStatusTextPadding(4, 0, 0, 0);
constexpr gfx::Size kActionButtonMinSize(0, 32);
// TODO(tetsui): Move |kIconViewSize| to public/cpp/message_center_constants.h
@@ -81,13 +83,6 @@ constexpr gfx::Insets kSettingsRowPadding(8, 0, 0, 0);
constexpr gfx::Insets kSettingsRadioButtonPadding(14, 18, 14, 18);
constexpr gfx::Insets kSettingsButtonRowPadding(8);
-// Ripple ink drop opacity of action buttons.
-const float kActionButtonInkDropRippleVisibleOpacity = 0.08f;
-// Highlight (hover) ink drop opacity of action buttons.
-const float kActionButtonInkDropHighlightVisibleOpacity = 0.08f;
-// Text color of action button.
-constexpr SkColor kActionButtonTextColor = gfx::kGoogleBlue600;
-
// The icon size of inline reply input field.
constexpr int kInputReplyButtonSize = 20;
@@ -311,46 +306,27 @@ gfx::Size LargeImageView::GetResizedImageSize() {
return resized_size;
}
-// NotificationButtonMD ////////////////////////////////////////////////////////
+// NotificationMDTextButton ////////////////////////////////////////////////
-NotificationButtonMD::NotificationButtonMD(
+NotificationMdTextButton::NotificationMdTextButton(
views::ButtonListener* listener,
const base::string16& label,
const base::Optional<base::string16>& placeholder)
- : views::LabelButton(listener,
- base::i18n::ToUpper(label),
- views::style::CONTEXT_BUTTON_MD),
- placeholder_(placeholder) {
- SetHorizontalAlignment(gfx::ALIGN_CENTER);
- SetInkDropMode(InkDropMode::ON);
- set_has_ink_drop_action_on_click(true);
- set_ink_drop_base_color(SK_ColorBLACK);
- set_ink_drop_visible_opacity(kActionButtonInkDropRippleVisibleOpacity);
- SetEnabledTextColors(kActionButtonTextColor);
- SetBorder(views::CreateEmptyBorder(kActionButtonPadding));
+ : views::MdTextButton(listener, label), placeholder_(placeholder) {
SetMinSize(kActionButtonMinSize);
- SetFocusForPlatform();
-
views::InstallRectHighlightPathGenerator(this);
+ SetTextSubpixelRenderingEnabled(false);
}
-NotificationButtonMD::~NotificationButtonMD() = default;
+NotificationMdTextButton::~NotificationMdTextButton() = default;
-void NotificationButtonMD::SetText(const base::string16& text) {
- views::LabelButton::SetText(base::i18n::ToUpper(text));
+void NotificationMdTextButton::UpdateBackgroundColor() {
+ // Overridden as no-op so we don't draw any background or border.
}
-const char* NotificationButtonMD::GetClassName() const {
- return "NotificationButtonMD";
-}
-
-std::unique_ptr<views::InkDropHighlight>
-NotificationButtonMD::CreateInkDropHighlight() const {
- std::unique_ptr<views::InkDropHighlight> highlight =
- views::LabelButton::CreateInkDropHighlight();
- highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity);
- return highlight;
-}
+BEGIN_METADATA(NotificationMdTextButton)
+METADATA_PARENT_CLASS(views::MdTextButton)
+END_METADATA()
// NotificationInputContainerMD ////////////////////////////////////////////////
@@ -419,7 +395,8 @@ NotificationInputContainerMD::CreateInkDropRipple() const {
}
SkColor NotificationInputContainerMD::GetInkDropBaseColor() const {
- return gfx::kGoogleBlue600;
+ return GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_NotificationInkDropBase);
}
void NotificationInputContainerMD::OnThemeChanged() {
@@ -430,7 +407,7 @@ void NotificationInputContainerMD::OnThemeChanged() {
textfield_->SetTextColor(SK_ColorWHITE);
textfield_->SetBackgroundColor(SK_ColorTRANSPARENT);
textfield_->set_placeholder_text_color(theme->GetSystemColor(
- ui::NativeTheme::kColorId_TextfieldPlaceholderColor));
+ ui::NativeTheme::kColorId_NotificationEmptyPlaceholderTextColor));
SetButtonImage();
}
@@ -489,12 +466,6 @@ class InlineSettingsRadioButton : public views::RadioButton {
}
private:
- // views::RadioButton:
- SkColor GetIconImageColor(int icon_state) const override {
- return (icon_state & IconState::CHECKED) ? kActionButtonTextColor
- : GetTextColor();
- }
-
SkColor GetTextColor() const {
return GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_LabelEnabledColor);
@@ -861,8 +832,7 @@ void NotificationViewMD::OnNotificationInputSubmit(size_t index,
void NotificationViewMD::CreateOrUpdateContextTitleView(
const Notification& notification) {
- if (notification.accent_color() != SK_ColorTRANSPARENT)
- header_row_->SetAccentColor(notification.accent_color());
+ header_row_->SetAccentColor(notification.accent_color());
header_row_->SetTimestamp(notification.timestamp());
header_row_->SetAppNameElideBehavior(gfx::ELIDE_TAIL);
header_row_->SetSummaryText(base::string16());
@@ -992,7 +962,6 @@ void NotificationViewMD::CreateOrUpdateProgressBarView(
/* allow_round_corner */ false);
progress_bar_view_->SetBorder(
views::CreateEmptyBorder(kProgressBarTopPadding, 0, 0, 0));
- progress_bar_view_->SetForegroundColor(kActionButtonTextColor);
left_content_->AddChildViewAt(progress_bar_view_, left_content_count_);
}
@@ -1087,10 +1056,8 @@ void NotificationViewMD::CreateOrUpdateSmallIconView(
// cache images if so. (crbug.com/768748)
gfx::Image masked_small_icon = notification.GenerateMaskedSmallIcon(
kSmallImageSizeMD,
- notification.accent_color() == SK_ColorTRANSPARENT
- ? GetNativeTheme()->GetSystemColor(
- ui::NativeTheme::kColorId_NotificationDefaultAccentColor)
- : notification.accent_color());
+ notification.accent_color().value_or(GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_NotificationDefaultAccentColor)));
if (masked_small_icon.IsEmpty()) {
header_row_->ClearAppIcon();
@@ -1152,23 +1119,21 @@ void NotificationViewMD::CreateOrUpdateActionButtonViews(
for (size_t i = 0; i < buttons.size(); ++i) {
ButtonInfo button_info = buttons[i];
+ base::string16 label = base::i18n::ToUpper(button_info.title);
if (new_buttons) {
- NotificationButtonMD* button = new NotificationButtonMD(
- this, button_info.title, button_info.placeholder);
- action_buttons_.push_back(button);
- action_buttons_row_->AddChildView(button);
+ action_buttons_.push_back(action_buttons_row_->AddChildView(
+ std::make_unique<NotificationMdTextButton>(this, label,
+ button_info.placeholder)));
+ // TODO(pkasting): BoxLayout should invalidate automatically when a child
+ // is added, at which point we can remove this call.
+ action_buttons_row_->InvalidateLayout();
} else {
- action_buttons_[i]->SetText(button_info.title);
+ action_buttons_[i]->SetText(label);
action_buttons_[i]->set_placeholder(button_info.placeholder);
- action_buttons_[i]->SchedulePaint();
- action_buttons_[i]->Layout();
}
// Change action button color to the accent color.
- action_buttons_[i]->SetEnabledTextColors(notification.accent_color() ==
- SK_ColorTRANSPARENT
- ? kActionButtonTextColor
- : notification.accent_color());
+ action_buttons_[i]->SetEnabledTextColors(notification.accent_color());
}
// Inherit mouse hover state when action button views reset.
@@ -1237,10 +1202,9 @@ void NotificationViewMD::CreateOrUpdateInlineSettingsViews(
settings_row_->AddChildView(dont_block_button_);
settings_row_->SetVisible(false);
- settings_done_button_ = new NotificationButtonMD(
+ settings_done_button_ = new NotificationMdTextButton(
this, l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_DONE),
base::nullopt);
- settings_done_button_->SetTextSubpixelRenderingEnabled(false);
auto* settings_button_row = new views::View;
auto settings_button_layout = std::make_unique<views::BoxLayout>(
@@ -1496,11 +1460,6 @@ std::vector<views::View*> NotificationViewMD::GetChildrenForLayerAdjustment()
settings_done_button_};
}
-std::unique_ptr<views::InkDropMask> NotificationViewMD::CreateInkDropMask()
- const {
- return nullptr;
-}
-
SkColor NotificationViewMD::GetInkDropBaseColor() const {
return GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_NotificationInlineSettingsBackground);
diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h
index bb92431650e..4bb3c2dbb4c 100644
--- a/chromium/ui/message_center/views/notification_view_md.h
+++ b/chromium/ui/message_center/views/notification_view_md.h
@@ -5,6 +5,8 @@
#ifndef UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
+#include <memory>
+#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
@@ -15,28 +17,57 @@
#include "ui/message_center/views/message_view.h"
#include "ui/views/animation/ink_drop_observer.h"
#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/metadata/metadata_header_macros.h"
namespace views {
class ImageButton;
class Label;
-class LabelButton;
class ProgressBar;
class RadioButton;
class Textfield;
-}
+} // namespace views
namespace message_center {
class NotificationHeaderView;
class ProportionalImageView;
+// NotificationMdTextButton extends MdText button to allow for placeholder text
+// as well as capitalizing the given label string.
+class MESSAGE_CENTER_EXPORT NotificationMdTextButton
+ : public views::MdTextButton {
+ public:
+ METADATA_HEADER(NotificationMdTextButton);
+
+ NotificationMdTextButton(views::ButtonListener* listener,
+ const base::string16& label,
+ const base::Optional<base::string16>& placeholder);
+ ~NotificationMdTextButton() override;
+
+ // views::MdTextButton:
+ void UpdateBackgroundColor() override;
+
+ const base::Optional<base::string16>& placeholder() const {
+ return placeholder_;
+ }
+ void set_placeholder(base::Optional<base::string16> placeholder) {
+ placeholder_ = std::move(placeholder);
+ }
+ SkColor enabled_color_for_testing() const {
+ return label()->GetEnabledColor();
+ }
+
+ private:
+ base::Optional<base::string16> placeholder_;
+};
+
// CompactTitleMessageView shows notification title and message in a single
// line. This view is used for NOTIFICATION_TYPE_PROGRESS.
class CompactTitleMessageView : public views::View {
public:
- explicit CompactTitleMessageView();
+ CompactTitleMessageView();
~CompactTitleMessageView() override;
const char* GetClassName() const override;
@@ -48,10 +79,10 @@ class CompactTitleMessageView : public views::View {
void set_message(const base::string16& message);
private:
- DISALLOW_COPY_AND_ASSIGN(CompactTitleMessageView);
-
views::Label* title_ = nullptr;
views::Label* message_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(CompactTitleMessageView);
};
class LargeImageView : public views::View {
@@ -73,42 +104,6 @@ class LargeImageView : public views::View {
DISALLOW_COPY_AND_ASSIGN(LargeImageView);
};
-// This class is needed in addition to LabelButton mainly becuase we want to set
-// visible_opacity of InkDropHighlight.
-// This button capitalizes the given label string.
-class NotificationButtonMD : public views::LabelButton {
- public:
- // |is_inline_reply| is true when the notification action takes text as the
- // return value i.e. the notification action is inline reply.
- // The input field would be shown when the button is clicked.
- // |placeholder| is placeholder text shown on the input field. Only used when
- // |is_inline_reply| is true.
- NotificationButtonMD(views::ButtonListener* listener,
- const base::string16& label,
- const base::Optional<base::string16>& placeholder);
- ~NotificationButtonMD() override;
-
- void SetText(const base::string16& text) override;
- const char* GetClassName() const override;
-
- std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
- const override;
-
- SkColor enabled_color_for_testing() { return label()->GetEnabledColor(); }
-
- const base::Optional<base::string16>& placeholder() const {
- return placeholder_;
- }
- void set_placeholder(const base::Optional<base::string16>& placeholder) {
- placeholder_ = placeholder;
- }
-
- private:
- base::Optional<base::string16> placeholder_;
-
- DISALLOW_COPY_AND_ASSIGN(NotificationButtonMD);
-};
-
class NotificationInputDelegate {
public:
virtual void OnNotificationInputSubmit(size_t index,
@@ -120,7 +115,7 @@ class NotificationInputContainerMD : public views::InkDropHostView,
public views::ButtonListener,
public views::TextfieldController {
public:
- NotificationInputContainerMD(NotificationInputDelegate* delegate);
+ explicit NotificationInputContainerMD(NotificationInputDelegate* delegate);
~NotificationInputContainerMD() override;
void AnimateBackground(const ui::Event& event);
@@ -187,7 +182,6 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
void PreferredSizeChanged() override;
std::unique_ptr<views::InkDrop> CreateInkDrop() override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
- std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
SkColor GetInkDropBaseColor() const override;
void UpdateWithNotification(const Notification& notification) override;
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
@@ -310,7 +304,7 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
views::Label* status_view_ = nullptr;
ProportionalImageView* icon_view_ = nullptr;
views::View* image_container_view_ = nullptr;
- std::vector<NotificationButtonMD*> action_buttons_;
+ std::vector<NotificationMdTextButton*> action_buttons_;
std::vector<views::View*> item_views_;
views::ProgressBar* progress_bar_view_ = nullptr;
CompactTitleMessageView* compact_title_message_view_ = nullptr;
@@ -324,7 +318,7 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
// Views for inline settings.
views::RadioButton* block_all_button_ = nullptr;
views::RadioButton* dont_block_button_ = nullptr;
- views::LabelButton* settings_done_button_ = nullptr;
+ NotificationMdTextButton* settings_done_button_ = nullptr;
// Owned by views properties. Guaranteed to be not null for the lifetime of
// |this| because views properties are the last thing cleaned up.
diff --git a/chromium/ui/message_center/views/notification_view_md_unittest.cc b/chromium/ui/message_center/views/notification_view_md_unittest.cc
index 77311c9aafa..f4663af293b 100644
--- a/chromium/ui/message_center/views/notification_view_md_unittest.cc
+++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc
@@ -15,6 +15,7 @@
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_unittest_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
@@ -486,7 +487,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
EXPECT_TRUE(notification_view()->actions_row_->GetVisible());
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
// Now construct a mouse move event 1 pixel inside the boundary of the action
// button.
@@ -498,12 +499,12 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
generator.MoveMouseTo(cursor_location);
EXPECT_EQ(views::Button::STATE_HOVERED,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
notification_view()->CreateOrUpdateViews(*notification);
EXPECT_EQ(views::Button::STATE_HOVERED,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
// Now construct a mouse move event 1 pixel outside the boundary of the
// widget.
@@ -513,7 +514,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
generator.MoveMouseTo(cursor_location);
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
}
TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
@@ -528,9 +529,9 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
EXPECT_TRUE(notification_view()->actions_row_->GetVisible());
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[1]->state());
+ notification_view()->action_buttons_[1]->GetState());
// Now construct a mouse move event 1 pixel inside the boundary of the action
// button.
@@ -542,15 +543,15 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
generator.MoveMouseTo(cursor_location);
EXPECT_EQ(views::Button::STATE_HOVERED,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[1]->state());
+ notification_view()->action_buttons_[1]->GetState());
notification->set_buttons(CreateButtons(1));
UpdateNotificationViews(*notification);
EXPECT_EQ(views::Button::STATE_HOVERED,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
EXPECT_EQ(1u, notification_view()->action_buttons_.size());
// Now construct a mouse move event 1 pixel outside the boundary of the
@@ -561,7 +562,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
generator.MoveMouseTo(cursor_location);
EXPECT_EQ(views::Button::STATE_NORMAL,
- notification_view()->action_buttons_[0]->state());
+ notification_view()->action_buttons_[0]->GetState());
}
TEST_F(NotificationViewMDTest, TestActionButtonClick) {
@@ -757,7 +758,7 @@ TEST_F(NotificationViewMDTest, TestInlineReplyActivateWithKeyPress) {
// Synthetic scroll events are not supported on Mac in the views
// test framework.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_SlideOut DISABLED_SlideOut
#else
#define MAYBE_SlideOut SlideOut
@@ -784,7 +785,7 @@ TEST_F(NotificationViewMDTest, MAYBE_SlideOut) {
EXPECT_TRUE(IsRemovedAfterIdle(kDefaultNotificationId));
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_SlideOutNested DISABLED_SlideOutNested
#else
#define MAYBE_SlideOutNested SlideOutNested
@@ -810,7 +811,7 @@ TEST_F(NotificationViewMDTest, MAYBE_SlideOutNested) {
EXPECT_TRUE(IsRemovedAfterIdle(kDefaultNotificationId));
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_DisableSlideForcibly DISABLED_DisableSlideForcibly
#else
#define MAYBE_DisableSlideForcibly DisableSlideForcibly
@@ -988,7 +989,19 @@ TEST_F(NotificationViewMDTest, TestAccentColor) {
notification_view()->ToggleExpanded();
EXPECT_TRUE(notification_view()->actions_row_->GetVisible());
- // By default, header does not have accent color.
+ auto app_icon_color_matches = [&](SkColor color) {
+ SkBitmap expected =
+ notification->GenerateMaskedSmallIcon(kSmallImageSizeMD, color)
+ .AsBitmap();
+ SkBitmap actual = *notification_view()
+ ->header_row_->app_icon_view_for_testing()
+ ->GetImage()
+ .bitmap();
+ return gfx::test::AreBitmapsEqual(expected, actual);
+ };
+
+ // By default, header does not have accent color (default grey), and
+ // buttons have default accent color.
EXPECT_FALSE(
notification_view()->header_row_->accent_color_for_testing().has_value());
EXPECT_EQ(
@@ -997,6 +1010,9 @@ TEST_F(NotificationViewMDTest, TestAccentColor) {
EXPECT_EQ(
kActionButtonTextColor,
notification_view()->action_buttons_[1]->enabled_color_for_testing());
+ EXPECT_TRUE(app_icon_color_matches(
+ notification_view()->GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_NotificationDefaultAccentColor)));
// If custom accent color is set, the header and the buttons should have the
// same accent color.
@@ -1012,6 +1028,7 @@ TEST_F(NotificationViewMDTest, TestAccentColor) {
EXPECT_EQ(
kCustomAccentColor,
notification_view()->action_buttons_[1]->enabled_color_for_testing());
+ EXPECT_TRUE(app_icon_color_matches(kCustomAccentColor));
}
TEST_F(NotificationViewMDTest, UseImageAsIcon) {
diff --git a/chromium/ui/message_center/views/relative_time_formatter.cc b/chromium/ui/message_center/views/relative_time_formatter.cc
index 14ece7eb0e6..6053884bf5f 100644
--- a/chromium/ui/message_center/views/relative_time_formatter.cc
+++ b/chromium/ui/message_center/views/relative_time_formatter.cc
@@ -4,6 +4,7 @@
#include "ui/message_center/views/relative_time_formatter.h"
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
@@ -69,7 +70,7 @@ void GetRelativeTimeStringAndNextUpdateTime(TimeDelta delta,
}
int string_id = past ? format.past : format.future;
- int count = static_cast<int>(absolute / format.range);
+ int count = base::ClampFloor(absolute / format.range);
TimeDelta delay = past
? format.range * (count + 1)
: TimeDelta::FromMilliseconds(1) - format.range * count;
diff --git a/chromium/ui/native_theme/BUILD.gn b/chromium/ui/native_theme/BUILD.gn
index 08bf35542c3..a96e591e6d7 100644
--- a/chromium/ui/native_theme/BUILD.gn
+++ b/chromium/ui/native_theme/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -12,7 +11,7 @@ import("//testing/test.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("native_theme") {
+component("native_theme") {
sources = [
"caption_style.cc",
"caption_style.h",
@@ -79,7 +78,7 @@ jumbo_component("native_theme") {
]
if (is_mac) {
- libs = [
+ frameworks = [
"CoreGraphics.framework",
"AppKit.framework",
"MediaAccessibility.framework",
@@ -88,7 +87,7 @@ jumbo_component("native_theme") {
}
if (is_win) {
- jumbo_component("native_theme_browser") {
+ component("native_theme_browser") {
defines = [ "NATIVE_THEME_IMPLEMENTATION" ]
# These files cannot work in the renderer on Windows.
@@ -109,11 +108,11 @@ if (is_win) {
libs = [ "uxtheme.lib" ]
}
} else {
- jumbo_source_set("native_theme_browser") {
+ source_set("native_theme_browser") {
}
}
-jumbo_source_set("test_support") {
+source_set("test_support") {
testonly = true
deps = [
diff --git a/chromium/ui/native_theme/caption_style.cc b/chromium/ui/native_theme/caption_style.cc
index 2b8ee10adc3..f0a223cbcc1 100644
--- a/chromium/ui/native_theme/caption_style.cc
+++ b/chromium/ui/native_theme/caption_style.cc
@@ -29,7 +29,7 @@ base::Optional<CaptionStyle> CaptionStyle::FromSpec(const std::string& spec) {
return style;
}
-#if !defined(OS_WIN) && !defined(OS_MACOSX)
+#if !defined(OS_WIN) && !defined(OS_APPLE)
base::Optional<CaptionStyle> CaptionStyle::FromSystemSettings() {
return base::nullopt;
}
diff --git a/chromium/ui/native_theme/caption_style_win.cc b/chromium/ui/native_theme/caption_style_win.cc
index 566de5e13a4..b147e53f039 100644
--- a/chromium/ui/native_theme/caption_style_win.cc
+++ b/chromium/ui/native_theme/caption_style_win.cc
@@ -11,12 +11,12 @@
#include "base/check_op.h"
#include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
#include "base/win/core_winrt_util.h"
#include "base/win/windows_version.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/color_utils.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
namespace CC = ABI::Windows::Media::ClosedCaptioning;
@@ -122,9 +122,9 @@ SkAlpha GetCaptionOpacity(CC::ClosedCaptionOpacity caption_opacity) {
case CC::ClosedCaptionOpacity_ZeroPercent:
return SK_AlphaTRANSPARENT;
case CC::ClosedCaptionOpacity_TwentyFivePercent:
- return gfx::ToRoundedInt(SK_AlphaOPAQUE * 0.25);
+ return base::ClampRound<SkAlpha>(SK_AlphaOPAQUE * 0.25);
case CC::ClosedCaptionOpacity_SeventyFivePercent:
- return gfx::ToRoundedInt(SK_AlphaOPAQUE * 0.75);
+ return base::ClampRound<SkAlpha>(SK_AlphaOPAQUE * 0.75);
case CC::ClosedCaptionOpacity_OneHundredPercent:
case CC::ClosedCaptionOpacity_Default:
default:
diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc
index e0f4ef443d3..c0c37674e99 100644
--- a/chromium/ui/native_theme/common_theme.cc
+++ b/chromium/ui/native_theme/common_theme.cc
@@ -163,6 +163,7 @@ base::Optional<SkColor> GetDarkSchemeColor(NativeTheme::ColorId color_id) {
// TabbedPane
case NativeTheme::kColorId_TabTitleColorActive:
+ case NativeTheme::kColorId_TabSelectedBorderColor:
return gfx::kGoogleBlue300;
case NativeTheme::kColorId_TabTitleColorInactive:
return gfx::kGoogleGrey500;
@@ -285,7 +286,8 @@ SkColor GetDefaultColor(NativeTheme::ColorId color_id,
return color_utils::BlendForMinContrast(gfx::kGoogleGrey600, bg, fg)
.color;
}
- case NativeTheme::kColorId_ProminentButtonDisabledColor: {
+ case NativeTheme::kColorId_ProminentButtonDisabledColor:
+ case NativeTheme::kColorId_DisabledButtonBorderColor: {
const SkColor bg = base_theme->GetSystemColor(
NativeTheme::kColorId_ButtonColor, color_scheme);
return color_utils::BlendForMinContrast(bg, bg, base::nullopt, 1.2f)
@@ -427,6 +429,10 @@ SkColor GetDefaultColor(NativeTheme::ColorId color_id,
return SkColorSetRGB(0xf5, 0xf5, 0xf5);
case NativeTheme::kColorId_NotificationEmptyPlaceholderIconColor:
return SkColorSetA(SK_ColorWHITE, 0x60);
+ case NativeTheme::kColorId_NotificationEmptyPlaceholderTextColor:
+ return SkColorSetA(SK_ColorWHITE, gfx::kDisabledControlAlpha);
+ case NativeTheme::kColorId_NotificationInkDropBase:
+ return gfx::kGoogleBlue600;
#if defined(OS_CHROMEOS)
case NativeTheme::kColorId_NotificationButtonBackground:
return SkColorSetA(SK_ColorWHITE, 0.9 * 0xff);
@@ -458,6 +464,7 @@ SkColor GetDefaultColor(NativeTheme::ColorId color_id,
// TabbedPane
case NativeTheme::kColorId_TabTitleColorActive:
+ case NativeTheme::kColorId_TabSelectedBorderColor:
return gfx::kGoogleBlue600;
case NativeTheme::kColorId_TabTitleColorInactive:
return gfx::kGoogleGrey700;
diff --git a/chromium/ui/native_theme/native_theme.cc b/chromium/ui/native_theme/native_theme.cc
index 150f4455d49..432c107708d 100644
--- a/chromium/ui/native_theme/native_theme.cc
+++ b/chromium/ui/native_theme/native_theme.cc
@@ -41,6 +41,7 @@ NativeThemeColorIdToColorIdMap() {
kColorBubbleFooterBackground},
{NTCID::kColorId_ButtonColor, kColorButtonBackground},
{NTCID::kColorId_ButtonBorderColor, kColorButtonBorder},
+ {NTCID::kColorId_DisabledButtonBorderColor, kColorButtonDisabledBorder},
{NTCID::kColorId_ButtonDisabledColor,
kColorButtonDisabledForeground},
{NTCID::kColorId_ButtonEnabledColor, kColorButtonForeground},
@@ -95,9 +96,10 @@ NativeThemeColorIdToColorIdMap() {
{NTCID::kColorId_MenuSeparatorColor, kColorMenuSeparator},
{NTCID::kColorId_TabBottomBorder, kColorTabContentSeparator},
{NTCID::kColorId_TabTitleColorInactive, kColorTabForeground},
+ {NTCID::kColorId_TabSelectedBorderColor, kColorTabSelectedBorder},
{NTCID::kColorId_TabTitleColorActive, kColorTabSelectedForeground},
{NTCID::kColorId_TableBackground, kColorTableBackground},
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
{NTCID::kColorId_TableBackgroundAlternate,
kColorTableBackgroundAlternate},
#endif
@@ -164,7 +166,7 @@ NativeTheme::ExtraParams::ExtraParams(const ExtraParams& other) {
memcpy(this, &other, sizeof(*this));
}
-#if !defined(OS_WIN) && !defined(OS_MACOSX)
+#if !defined(OS_WIN) && !defined(OS_APPLE)
// static
bool NativeTheme::SystemDarkModeSupported() {
return false;
@@ -201,6 +203,10 @@ SkColor NativeTheme::GetSystemButtonPressedColor(SkColor base_color) const {
return base_color;
}
+SkColor NativeTheme::FocusRingColorForBaseColor(SkColor base_color) const {
+ return base_color;
+}
+
float NativeTheme::GetBorderRadiusForPart(Part part,
float width,
float height,
diff --git a/chromium/ui/native_theme/native_theme.h b/chromium/ui/native_theme/native_theme.h
index f4e9cc14b0b..e2ca7dd1983 100644
--- a/chromium/ui/native_theme/native_theme.h
+++ b/chromium/ui/native_theme/native_theme.h
@@ -249,7 +249,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
ScrollbarOverlayColorTheme scrollbar_theme;
};
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
enum ScrollbarOrientation {
// Vertical scrollbar on the right side of content.
kVerticalOnRight,
@@ -310,7 +310,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
MenuBackgroundExtraParams menu_background;
ProgressBarExtraParams progress_bar;
ScrollbarArrowExtraParams scrollbar_arrow;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
ScrollbarExtraParams scrollbar_extra;
#endif
ScrollbarTrackExtraParams scrollbar_track;
@@ -471,6 +471,9 @@ class NATIVE_THEME_EXPORT NativeTheme {
// pressed states.
virtual SkColor GetSystemButtonPressedColor(SkColor base_color) const;
+ // Assign the focus-ring-appropriate alpha value to the provided base_color.
+ virtual SkColor FocusRingColorForBaseColor(SkColor base_color) const;
+
protected:
explicit NativeTheme(bool should_only_use_dark_colors);
virtual ~NativeTheme();
diff --git a/chromium/ui/native_theme/native_theme_aura.cc b/chromium/ui/native_theme/native_theme_aura.cc
index ff92427546f..eb68579e8cf 100644
--- a/chromium/ui/native_theme/native_theme_aura.cc
+++ b/chromium/ui/native_theme/native_theme_aura.cc
@@ -47,7 +47,7 @@ const SkScalar kScrollRadius =
////////////////////////////////////////////////////////////////////////////////
// NativeTheme:
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// static
NativeTheme* NativeTheme::GetInstanceForWeb() {
return NativeThemeAura::web_instance();
@@ -65,7 +65,7 @@ NativeTheme* NativeTheme::GetInstanceForDarkUI() {
return s_native_theme.get();
}
#endif // OS_WIN
-#endif // !OS_MACOSX
+#endif // !OS_APPLE
////////////////////////////////////////////////////////////////////////////////
// NativeThemeAura:
@@ -100,6 +100,17 @@ NativeThemeAura* NativeThemeAura::web_instance() {
return s_native_theme_for_web.get();
}
+SkColor NativeThemeAura::FocusRingColorForBaseColor(SkColor base_color) const {
+#if defined(OS_APPLE)
+ DCHECK(features::IsFormControlsRefreshEnabled());
+ // On Mac OSX, the system Accent Color setting is darkened a bit
+ // for better contrast.
+ return SkColorSetA(base_color, 166);
+#else
+ return base_color;
+#endif // OS_APPLE
+}
+
void NativeThemeAura::PaintMenuPopupBackground(
cc::PaintCanvas* canvas,
const gfx::Size& size,
diff --git a/chromium/ui/native_theme/native_theme_aura.h b/chromium/ui/native_theme/native_theme_aura.h
index 1b537cc2255..fd2f9dc846c 100644
--- a/chromium/ui/native_theme/native_theme_aura.h
+++ b/chromium/ui/native_theme/native_theme_aura.h
@@ -24,6 +24,9 @@ class NATIVE_THEME_EXPORT NativeThemeAura : public NativeThemeBase {
static NativeThemeAura* web_instance();
+ // Overridden from NativeTheme:
+ SkColor FocusRingColorForBaseColor(SkColor base_color) const override;
+
// NativeThemeBase:
void PaintMenuPopupBackground(
cc::PaintCanvas* canvas,
diff --git a/chromium/ui/native_theme/native_theme_base.cc b/chromium/ui/native_theme/native_theme_base.cc
index 57bd8bf9905..a69346950fa 100644
--- a/chromium/ui/native_theme/native_theme_base.cc
+++ b/chromium/ui/native_theme/native_theme_base.cc
@@ -720,15 +720,17 @@ SkRect NativeThemeBase::PaintCheckboxRadioCommon(
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->drawRoundRect(background_rect, border_radius, border_radius, flags);
- // Draw the border.
- if (!(is_checkbox && button.checked)) {
+ // For checkbox the border is drawn only when it is unchecked or
+ // indeterminate. For radio the border is always drawn.
+ if (!(is_checkbox && button.checked && !button.indeterminate)) {
// Shrink half border width so the final pixels of the border will be
// within the rectangle.
const auto border_rect =
skrect.makeInset(kBorderWidth / 2, kBorderWidth / 2);
SkColor border_color =
- button.checked ? ControlsAccentColorForState(state, color_scheme)
- : ControlsBorderColorForState(state, color_scheme);
+ (button.checked && !button.indeterminate)
+ ? ControlsAccentColorForState(state, color_scheme)
+ : ControlsBorderColorForState(state, color_scheme);
flags.setColor(border_color);
flags.setStyle(cc::PaintFlags::kStroke_Style);
flags.setStrokeWidth(kBorderWidth);
@@ -1645,45 +1647,47 @@ SkColor NativeThemeBase::GetControlColor(ControlColorId color_id,
SkColor NativeThemeBase::GetDarkModeControlColor(
ControlColorId color_id) const {
- switch (color_id) {
+ switch (color_id) {
case kAccent:
- return SkColorSetRGB(0xC3, 0xC3, 0xC3);
+ return SkColorSetRGB(0x99, 0xC8, 0xFF);
case kHoveredAccent:
- return SkColorSetRGB(0xD8, 0xD8, 0xD8);
+ return SkColorSetRGB(0xD1, 0xE6, 0xFF);
case kPressedAccent:
- return SkColorSetRGB(0xB9, 0xB9, 0xB9);
+ return SkColorSetRGB(0x61, 0xA9, 0xFF);
case kDisabledAccent:
- return SkColorSetARGB(0x4D, 0xC3, 0xC3, 0xC3);
+ return SkColorSetRGB(0x75, 0x75, 0x75);
case kProgressValue:
return SkColorSetRGB(0x63, 0xAD, 0xE5);
case kFill:
+ return SkColorSetRGB(0x3B, 0x3B, 0x3B);
case kLightenLayer:
case kAutoCompleteBackground:
case kBackground:
return SkColorSetRGB(0x3B, 0x3B, 0x3B);
case kBorder:
+ return SkColorSetRGB(0x85, 0x85, 0x85);
case kSlider:
- return SkColorSetRGB(0xC3, 0xC3, 0xC3);
+ return SkColorSetRGB(0x99, 0xC8, 0xFF);
case kHoveredSlider:
- return SkColorSetRGB(0xD8, 0xD8, 0xD8);
+ return SkColorSetRGB(0xD1, 0xE6, 0xFF);
case kPressedSlider:
- return SkColorSetRGB(0xB9, 0xB9, 0xB9);
+ return SkColorSetRGB(0x61, 0xA9, 0xFF);
case kDisabledSlider:
- return SkColorSetRGB(0x70, 0x70, 0x70);
+ return SkColorSetRGB(0x75, 0x75, 0x75);
case kDisabledBackground:
- return SkColorSetARGB(0x4D, 0x3B, 0x3B, 0x3B);
+ return SkColorSetRGB(0x3B, 0x3B, 0x3B);
case kHoveredBorder:
- return SkColorSetRGB(0xEA, 0xEA, 0xEA);
- case kPressedBorder:
return SkColorSetRGB(0xAC, 0xAC, 0xAC);
+ case kPressedBorder:
+ return SkColorSetRGB(0x6E, 0x6E, 0x6E);
case kDisabledBorder:
- return SkColorSetARGB(0x4D ,0xC3, 0xC3, 0xC3);
+ return SkColorSetRGB(0x62, 0x62, 0x62);
case kHoveredFill:
- return SkColorSetRGB(0x54, 0x54, 0x54);
+ return SkColorSetRGB(0x3B, 0x3B, 0x3B);
case kPressedFill:
- return SkColorSetRGB(0x45, 0x45, 0x45);
+ return SkColorSetRGB(0x3B, 0x3B, 0x3B);
case kDisabledFill:
- return SkColorSetARGB(0x4D, 0x3B, 0x3B, 0x3B);
+ return SkColorSetRGB(0x36, 0x36, 0x36);
case kScrollbarArrowBackground:
return SkColorSetRGB(0x42, 0x42, 0x42);
case kScrollbarArrowBackgroundHovered:
@@ -1702,7 +1706,7 @@ SkColor NativeThemeBase::GetDarkModeControlColor(
case kScrollbarThumbPressed:
case kScrollbarThumb:
return SK_ColorWHITE;
- }
+ }
NOTREACHED();
return gfx::kPlaceholderColor;
}
diff --git a/chromium/ui/native_theme/native_theme_color_id.h b/chromium/ui/native_theme/native_theme_color_id.h
index 60fb0ea1010..dde0f977c67 100644
--- a/chromium/ui/native_theme/native_theme_color_id.h
+++ b/chromium/ui/native_theme/native_theme_color_id.h
@@ -22,6 +22,7 @@
/* Button */ \
OP(kColorId_ButtonColor), \
OP(kColorId_ButtonBorderColor), \
+ OP(kColorId_DisabledButtonBorderColor), \
OP(kColorId_ButtonCheckedColor), \
OP(kColorId_ButtonUncheckedColor), \
OP(kColorId_ButtonEnabledColor), \
@@ -91,7 +92,9 @@
OP(kColorId_NotificationLargeImageBackground), \
OP(kColorId_NotificationPlaceholderIconColor), \
OP(kColorId_NotificationEmptyPlaceholderIconColor), \
+ OP(kColorId_NotificationEmptyPlaceholderTextColor), \
OP(kColorId_NotificationDefaultAccentColor), \
+ OP(kColorId_NotificationInkDropBase), \
/* Slider */ \
OP(kColorId_SliderThumbDefault), \
OP(kColorId_SliderTroughDefault), \
@@ -109,6 +112,7 @@
OP(kColorId_TabBottomBorder), \
OP(kColorId_TabHighlightBackground), \
OP(kColorId_TabHighlightFocusedBackground), \
+ OP(kColorId_TabSelectedBorderColor), \
/* Textfield */ \
OP(kColorId_TextfieldDefaultColor), \
OP(kColorId_TextfieldDefaultBackground), \
diff --git a/chromium/ui/native_theme/native_theme_mac.h b/chromium/ui/native_theme/native_theme_mac.h
index fbaa33f4c9a..fb95b14032d 100644
--- a/chromium/ui/native_theme/native_theme_mac.h
+++ b/chromium/ui/native_theme/native_theme_mac.h
@@ -160,8 +160,6 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase {
std::unique_ptr<NativeTheme::ColorSchemeNativeThemeObserver>
color_scheme_observer_;
- bool should_only_use_dark_colors_;
-
DISALLOW_COPY_AND_ASSIGN(NativeThemeMac);
};
diff --git a/chromium/ui/native_theme/native_theme_mac.mm b/chromium/ui/native_theme/native_theme_mac.mm
index 1fda9fe5d26..2ec658c0050 100644
--- a/chromium/ui/native_theme/native_theme_mac.mm
+++ b/chromium/ui/native_theme/native_theme_mac.mm
@@ -168,8 +168,7 @@ SkColor NativeThemeMac::GetSystemColor(ColorId color_id,
// once NativeTheme.cc handles kColorProviderReirection and
// kPlatformHighContrast both being on.
if ((base::FeatureList::IsEnabled(features::kColorProviderRedirection) &&
- color_scheme != ColorScheme::kPlatformHighContrast) ||
- should_only_use_dark_colors_)
+ color_scheme != ColorScheme::kPlatformHighContrast))
return NativeTheme::GetSystemColor(color_id, color_scheme);
if (UsesHighContrastColors()) {
@@ -577,8 +576,7 @@ void NativeThemeMac::PaintMenuItemBackground(
NativeThemeMac::NativeThemeMac(bool configure_web_instance,
bool should_only_use_dark_colors)
- : NativeThemeBase(should_only_use_dark_colors),
- should_only_use_dark_colors_(should_only_use_dark_colors) {
+ : NativeThemeBase(should_only_use_dark_colors) {
if (!should_only_use_dark_colors)
InitializeDarkModeStateAndObserver();
diff --git a/chromium/ui/native_theme/themed_vector_icon.cc b/chromium/ui/native_theme/themed_vector_icon.cc
index bf3b0e65d9f..d4374ca97c3 100644
--- a/chromium/ui/native_theme/themed_vector_icon.cc
+++ b/chromium/ui/native_theme/themed_vector_icon.cc
@@ -43,20 +43,19 @@ ThemedVectorIcon::ThemedVectorIcon(ThemedVectorIcon&&) = default;
ThemedVectorIcon& ThemedVectorIcon::operator=(ThemedVectorIcon&&) = default;
-const gfx::ImageSkia ThemedVectorIcon::GetImageSkia(
- const NativeTheme* theme) const {
+gfx::ImageSkia ThemedVectorIcon::GetImageSkia(const NativeTheme* theme) const {
DCHECK(!empty());
return icon_size_ > 0 ? CreateVectorIcon(*icon_, icon_size_, GetColor(theme))
: CreateVectorIcon(*icon_, GetColor(theme));
}
-const gfx::ImageSkia ThemedVectorIcon::GetImageSkia(const NativeTheme* theme,
- int icon_size) const {
+gfx::ImageSkia ThemedVectorIcon::GetImageSkia(const NativeTheme* theme,
+ int icon_size) const {
DCHECK(!empty());
return CreateVectorIcon(*icon_, icon_size, GetColor(theme));
}
-const gfx::ImageSkia ThemedVectorIcon::GetImageSkia(SkColor color) const {
+gfx::ImageSkia ThemedVectorIcon::GetImageSkia(SkColor color) const {
DCHECK(!empty());
return icon_size_ > 0 ? CreateVectorIcon(*icon_, icon_size_, color)
: CreateVectorIcon(*icon_, color);
diff --git a/chromium/ui/native_theme/themed_vector_icon.h b/chromium/ui/native_theme/themed_vector_icon.h
index bf929d6af94..33df4298838 100644
--- a/chromium/ui/native_theme/themed_vector_icon.h
+++ b/chromium/ui/native_theme/themed_vector_icon.h
@@ -40,10 +40,9 @@ class NATIVE_THEME_EXPORT ThemedVectorIcon {
void clear() { icon_ = nullptr; }
bool empty() const { return !icon_; }
- const gfx::ImageSkia GetImageSkia(const NativeTheme* theme) const;
- const gfx::ImageSkia GetImageSkia(const NativeTheme* theme,
- int icon_size) const;
- const gfx::ImageSkia GetImageSkia(SkColor color) const;
+ gfx::ImageSkia GetImageSkia(const NativeTheme* theme) const;
+ gfx::ImageSkia GetImageSkia(const NativeTheme* theme, int icon_size) const;
+ gfx::ImageSkia GetImageSkia(SkColor color) const;
private:
SkColor GetColor(const NativeTheme* theme) const;
diff --git a/chromium/ui/ozone/BUILD.gn b/chromium/ui/ozone/BUILD.gn
index e3eb1e17596..296c06ab333 100644
--- a/chromium/ui/ozone/BUILD.gn
+++ b/chromium/ui/ozone/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/buildflag_header.gni")
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//gpu/vulkan/features.gni")
import("//testing/test.gni")
@@ -64,7 +63,7 @@ platform_list_h_file = "$target_gen_dir/platform_list.h"
platform_list_txt_file = "$target_gen_dir/platform_list.txt"
constructor_list_cc_file = "$target_gen_dir/constructor_list.cc"
-jumbo_component("ozone_base") {
+component("ozone_base") {
sources = [
"public/gl_ozone.h",
"public/gpu_platform_support_host.cc",
@@ -129,6 +128,7 @@ jumbo_component("ozone_base") {
visibility += [
# Everyone should depend on //ui/ozone instead except a handful of
# things that would otherwise create a cycle.
+ "//chromeos/system:system",
"//ui/events/ozone/*",
"//ui/ozone/common/*",
"//ui/ozone/public/mojom",
@@ -199,7 +199,7 @@ source_set("platform") {
visibility += ozone_external_platform_visibility
}
-jumbo_component("ozone") {
+component("ozone") {
visibility = []
visibility = [ "*" ]
public_deps = [
@@ -209,7 +209,7 @@ jumbo_component("ozone") {
]
}
-jumbo_source_set("test_support_internal") {
+source_set("test_support_internal") {
testonly = true
sources = [
@@ -232,7 +232,14 @@ jumbo_source_set("test_support_internal") {
public_deps = [ "//base/test:test_support" ]
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
+ visibility = []
+ visibility = [
+ ":*",
+ "platform/wayland:wayland_unittests",
+ "platform/x11:x11_unittests",
+ ]
+
testonly = true
public_deps = [ ":test_support_internal" ]
}
diff --git a/chromium/ui/ozone/demo/demo_window.h b/chromium/ui/ozone/demo/demo_window.h
index e0e7871098a..907107b1070 100644
--- a/chromium/ui/ozone/demo/demo_window.h
+++ b/chromium/ui/ozone/demo/demo_window.h
@@ -43,6 +43,7 @@ class DemoWindow : public PlatformWindowDelegate {
void OnWindowStateChanged(PlatformWindowState new_state) override;
void OnLostCapture() override;
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
+ void OnWillDestroyAcceleratedWidget() override {}
void OnAcceleratedWidgetDestroyed() override;
void OnActivationChanged(bool active) override;
void OnMouseEnter() override;
diff --git a/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc b/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc
index c5be0bb4632..15a9ae39586 100644
--- a/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc
+++ b/chromium/ui/ozone/demo/skia/skia_gl_renderer.cc
@@ -73,7 +73,7 @@ bool SkiaGlRenderer::Initialize() {
// TODO(csmartdalton): enable internal multisampling after the related Skia
// rolls are in.
options.fInternalMultisampleCount = 0;
- gr_context_ = GrContext::MakeGL(std::move(native_interface), options);
+ gr_context_ = GrDirectContext::MakeGL(std::move(native_interface), options);
DCHECK(gr_context_);
PostRenderFrameTask(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK));
@@ -101,8 +101,7 @@ void SkiaGlRenderer::RenderFrame() {
if (use_ddl_) {
StartDDLRenderThreadIfNecessary(sk_surface_.get());
- auto ddl = GetDDL();
- sk_surface_->draw(ddl.get());
+ sk_surface_->draw(GetDDL());
} else {
Draw(sk_surface_->getCanvas(), NextFraction());
}
@@ -210,7 +209,7 @@ void SkiaGlRenderer::StopDDLRenderThread() {
ddls_.pop();
}
-std::unique_ptr<SkDeferredDisplayList> SkiaGlRenderer::GetDDL() {
+sk_sp<SkDeferredDisplayList> SkiaGlRenderer::GetDDL() {
base::AutoLock auto_lock_(lock_);
DCHECK(surface_charaterization_.isValid());
// Wait until DDL is generated by DDL render thread.
@@ -234,7 +233,7 @@ void SkiaGlRenderer::Run() {
break;
DCHECK_LT(ddls_.size(), kMaxPendingDDLS);
SkDeferredDisplayListRecorder recorder(surface_charaterization_);
- std::unique_ptr<SkDeferredDisplayList> ddl;
+ sk_sp<SkDeferredDisplayList> ddl;
{
base::AutoUnlock auto_unlock(lock_);
Draw(recorder.getCanvas(), NextFraction());
diff --git a/chromium/ui/ozone/demo/skia/skia_gl_renderer.h b/chromium/ui/ozone/demo/skia/skia_gl_renderer.h
index 08195eebf87..e49948d0c30 100644
--- a/chromium/ui/ozone/demo/skia/skia_gl_renderer.h
+++ b/chromium/ui/ozone/demo/skia/skia_gl_renderer.h
@@ -16,7 +16,7 @@
#include "base/threading/simple_thread.h"
#include "third_party/skia/include/core/SkDeferredDisplayListRecorder.h"
#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/swap_result.h"
#include "ui/ozone/demo/renderer_base.h"
@@ -51,14 +51,14 @@ class SkiaGlRenderer : public RendererBase,
void Draw(SkCanvas* canvas, float fraction);
void StartDDLRenderThreadIfNecessary(SkSurface* sk_surface);
void StopDDLRenderThread();
- std::unique_ptr<SkDeferredDisplayList> GetDDL();
+ sk_sp<SkDeferredDisplayList> GetDDL();
std::unique_ptr<PlatformWindowSurface> window_surface_;
scoped_refptr<gl::GLSurface> gl_surface_;
scoped_refptr<gl::GLContext> gl_context_;
- sk_sp<GrContext> gr_context_;
+ sk_sp<GrDirectContext> gr_context_;
const bool use_ddl_;
private:
@@ -80,7 +80,7 @@ class SkiaGlRenderer : public RendererBase,
base::ConditionVariable condition_variable_;
SkSurfaceCharacterization surface_charaterization_;
- base::queue<std::unique_ptr<SkDeferredDisplayList>> ddls_;
+ base::queue<sk_sp<SkDeferredDisplayList>> ddls_;
base::WeakPtrFactory<SkiaGlRenderer> weak_ptr_factory_{this};
diff --git a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
index ec083066e53..c6b759253ce 100644
--- a/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
+++ b/chromium/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
@@ -239,8 +239,7 @@ void SurfacelessSkiaGlRenderer::RenderFrame() {
SkSurface* sk_surface = buffers_[back_buffer_]->sk_surface();
if (use_ddl_) {
StartDDLRenderThreadIfNecessary(sk_surface);
- auto ddl = GetDDL();
- sk_surface->draw(ddl.get());
+ sk_surface->draw(GetDDL());
} else {
Draw(sk_surface->getCanvas(), NextFraction());
}
diff --git a/chromium/ui/ozone/demo/software_renderer.cc b/chromium/ui/ozone/demo/software_renderer.cc
index 3214fa4b3e9..30cd9127c8d 100644
--- a/chromium/ui/ozone/demo/software_renderer.cc
+++ b/chromium/ui/ozone/demo/software_renderer.cc
@@ -33,14 +33,12 @@ SoftwareRenderer::SoftwareRenderer(
vsync_period_(
base::TimeDelta::FromMilliseconds(kFrameDelayMilliseconds)) {}
-SoftwareRenderer::~SoftwareRenderer() {}
+SoftwareRenderer::~SoftwareRenderer() = default;
bool SoftwareRenderer::Initialize() {
- software_surface_ =
- ui::OzonePlatform::GetInstance()
- ->GetSurfaceFactoryOzone()
- ->CreateCanvasForWidget(widget_,
- base::ThreadTaskRunnerHandle::Get().get());
+ software_surface_ = ui::OzonePlatform::GetInstance()
+ ->GetSurfaceFactoryOzone()
+ ->CreateCanvasForWidget(widget_);
if (!software_surface_) {
LOG(ERROR) << "Failed to create software surface";
return false;
diff --git a/chromium/ui/ozone/demo/vulkan_renderer.cc b/chromium/ui/ozone/demo/vulkan_renderer.cc
index 04235e74ee3..4e1a1fc3d1f 100644
--- a/chromium/ui/ozone/demo/vulkan_renderer.cc
+++ b/chromium/ui/ozone/demo/vulkan_renderer.cc
@@ -17,7 +17,6 @@
#include "gpu/vulkan/vulkan_command_buffer.h"
#include "gpu/vulkan/vulkan_command_pool.h"
#include "gpu/vulkan/vulkan_device_queue.h"
-#include "gpu/vulkan/vulkan_fence_helper.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_surface.h"
@@ -239,34 +238,35 @@ void VulkanRenderer::RenderFrame() {
{
gpu::ScopedSingleUseCommandBufferRecorder recorder(command_buffer);
- VkImageLayout old_layout = scoped_write.image_layout();
- VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- VkImageMemoryBarrier image_memory_barrier = {
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = GetAccessMask(old_layout),
- .dstAccessMask = GetAccessMask(layout),
- .oldLayout = old_layout,
- .newLayout = layout,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = scoped_write.image(),
- .subresourceRange =
- {
- .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
- .baseMipLevel = 0,
- .levelCount = 1,
- .baseArrayLayer = 0,
- .layerCount = 1,
- },
- };
- vkCmdPipelineBarrier(
- recorder.handle(), GetPipelineStageFlags(old_layout),
- GetPipelineStageFlags(layout), 0 /* dependencyFlags */,
- 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
- 0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
- 1, &image_memory_barrier);
- scoped_write.set_image_layout(layout);
+ {
+ VkImageLayout old_layout = scoped_write.image_layout();
+ VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ VkImageMemoryBarrier image_memory_barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = GetAccessMask(old_layout),
+ .dstAccessMask = GetAccessMask(layout),
+ .oldLayout = old_layout,
+ .newLayout = layout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = scoped_write.image(),
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ vkCmdPipelineBarrier(
+ recorder.handle(), GetPipelineStageFlags(old_layout),
+ GetPipelineStageFlags(layout), 0 /* dependencyFlags */,
+ 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
+ 0 /* bufferMemoryBarrierCount */,
+ nullptr /* pBufferMemoryBarriers */, 1, &image_memory_barrier);
+ }
VkRenderPassBeginInfo begin_info = {
/* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
@@ -293,12 +293,42 @@ void VulkanRenderer::RenderFrame() {
VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(recorder.handle());
+
+ // Transfer image layout back to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR for
+ // presenting.
+ {
+ VkImageLayout old_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ VkImageLayout layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ VkImageMemoryBarrier image_memory_barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = GetAccessMask(old_layout),
+ .dstAccessMask = GetAccessMask(layout),
+ .oldLayout = old_layout,
+ .newLayout = layout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = scoped_write.image(),
+ .subresourceRange =
+ {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ vkCmdPipelineBarrier(
+ recorder.handle(), GetPipelineStageFlags(old_layout),
+ GetPipelineStageFlags(layout), 0 /* dependencyFlags */,
+ 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
+ 0 /* bufferMemoryBarrierCount */,
+ nullptr /* pBufferMemoryBarriers */, 1, &image_memory_barrier);
+ }
}
- VkSemaphore begin_semaphore = scoped_write.TakeBeginSemaphore();
- VkSemaphore end_semaphore = scoped_write.GetEndSemaphore();
+ VkSemaphore begin_semaphore = scoped_write.begin_semaphore();
+ VkSemaphore end_semaphore = scoped_write.end_semaphore();
CHECK(command_buffer.Submit(1, &begin_semaphore, 1, &end_semaphore));
- device_queue_->GetFenceHelper()->EnqueueSemaphoreCleanupForSubmittedWork(
- begin_semaphore);
}
vulkan_surface_->SwapBuffers();
diff --git a/chromium/ui/ozone/demo/window_manager.cc b/chromium/ui/ozone/demo/window_manager.cc
index 181539c4673..611d6bff646 100644
--- a/chromium/ui/ozone/demo/window_manager.cc
+++ b/chromium/ui/ozone/demo/window_manager.cc
@@ -69,13 +69,13 @@ void WindowManager::OnConfigurationChanged() {
}
is_configuring_ = true;
- delegate_->GetDisplays(base::BindOnce(&WindowManager::OnDisplaysAquired,
+ delegate_->GetDisplays(base::BindOnce(&WindowManager::OnDisplaysAcquired,
base::Unretained(this)));
}
void WindowManager::OnDisplaySnapshotsInvalidated() {}
-void WindowManager::OnDisplaysAquired(
+void WindowManager::OnDisplaysAcquired(
const std::vector<display::DisplaySnapshot*>& displays) {
windows_.clear();
@@ -87,10 +87,14 @@ void WindowManager::OnDisplaysAquired(
continue;
}
+ display::DisplayConfigurationParams display_config_params(
+ display->display_id(), origin, display->native_mode());
+ std::vector<display::DisplayConfigurationParams> config_request;
+ config_request.push_back(std::move(display_config_params));
delegate_->Configure(
- *display, display->native_mode(), origin,
+ config_request,
base::BindOnce(&WindowManager::OnDisplayConfigured,
- base::Unretained(this),
+ base::Unretained(this), display->display_id(),
gfx::Rect(origin, display->native_mode()->size())));
origin.Offset(display->native_mode()->size().width(), 0);
}
@@ -104,8 +108,13 @@ void WindowManager::OnDisplaysAquired(
}
}
-void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) {
- if (success) {
+void WindowManager::OnDisplayConfigured(
+ const int64_t display_id,
+ const gfx::Rect& bounds,
+ const base::flat_map<int64_t, bool>& statuses) {
+ DCHECK_EQ(statuses.size(), 1UL);
+
+ if (statuses.at(display_id)) {
std::unique_ptr<DemoWindow> window(
new DemoWindow(this, renderer_factory_.get(), bounds));
window->Start();
diff --git a/chromium/ui/ozone/demo/window_manager.h b/chromium/ui/ozone/demo/window_manager.h
index a31b01d47bb..0415b01bf39 100644
--- a/chromium/ui/ozone/demo/window_manager.h
+++ b/chromium/ui/ozone/demo/window_manager.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "ui/display/types/native_display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/demo/renderer_factory.h"
@@ -36,9 +37,11 @@ class WindowManager : public display::NativeDisplayObserver {
void RemoveWindow(DemoWindow* window);
private:
- void OnDisplaysAquired(
+ void OnDisplaysAcquired(
const std::vector<display::DisplaySnapshot*>& displays);
- void OnDisplayConfigured(const gfx::Rect& bounds, bool success);
+ void OnDisplayConfigured(const int64_t display_id,
+ const gfx::Rect& bounds,
+ const base::flat_map<int64_t, bool>& statuses);
// display::NativeDisplayDelegate:
void OnConfigurationChanged() override;
diff --git a/chromium/ui/ozone/ozone.gni b/chromium/ui/ozone/ozone.gni
index 05d59e73fad..951a5b8f425 100644
--- a/chromium/ui/ozone/ozone.gni
+++ b/chromium/ui/ozone/ozone.gni
@@ -76,7 +76,6 @@ declare_args() {
ozone_platform = "x11"
ozone_platform_wayland = true
ozone_platform_x11 = true
- ozone_platform_gbm = true
} else if (is_win) {
ozone_platform = "windows"
ozone_platform_windows = true
diff --git a/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc b/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
index 36ae5fac4d5..93ed64dafb0 100644
--- a/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
+++ b/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc
@@ -89,8 +89,8 @@ void GLOzoneEglCast::TerminateDisplay() {
scoped_refptr<gl::GLSurface> GLOzoneEglCast::CreateViewGLSurface(
gfx::AcceleratedWidget widget) {
// Verify requested widget dimensions match our current display size.
- DCHECK_EQ(widget >> 16, display_size_.width());
- DCHECK_EQ(widget & 0xffff, display_size_.height());
+ DCHECK_EQ(static_cast<int>(widget >> 16), display_size_.width());
+ DCHECK_EQ(static_cast<int>(widget & 0xffff), display_size_.height());
return gl::InitializeGLSurface(new GLSurfaceCast(widget, this));
}
diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc
index 46928b82e97..cd2587800e8 100644
--- a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc
+++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc
@@ -113,8 +113,7 @@ GLOzone* SurfaceFactoryCast::GetGLOzone(gl::GLImplementation implementation) {
}
std::unique_ptr<SurfaceOzoneCanvas> SurfaceFactoryCast::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ gfx::AcceleratedWidget widget) {
// Software canvas support only in headless mode
if (egl_implementation_)
return nullptr;
diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.h b/chromium/ui/ozone/platform/cast/surface_factory_cast.h
index a7d0ef9b151..ea3ad0ff7d0 100644
--- a/chromium/ui/ozone/platform/cast/surface_factory_cast.h
+++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.h
@@ -32,8 +32,7 @@ class SurfaceFactoryCast : public SurfaceFactoryOzone {
std::vector<gl::GLImplementation> GetAllowedGLImplementations() override;
GLOzone* GetGLOzone(gl::GLImplementation implementation) override;
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
scoped_refptr<gfx::NativePixmap> CreateNativePixmap(
gfx::AcceleratedWidget widget,
VkDevice vk_device,
diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc
index 656330d42bf..a03a4a94935 100644
--- a/chromium/ui/ozone/platform/drm/common/drm_util.cc
+++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc
@@ -469,6 +469,7 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot(
!!edid_blob);
std::vector<uint8_t> edid;
if (edid_blob) {
+ DCHECK(edid_blob->length);
edid.assign(static_cast<uint8_t*>(edid_blob->data),
static_cast<uint8_t*>(edid_blob->data) + edid_blob->length);
@@ -496,6 +497,14 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot(
display::DisplaySnapshot::DisplayModeList modes =
ExtractDisplayModes(info, active_pixel_size, &current_mode, &native_mode);
+ // TODO(https://crbug.com/1105919): Needed for investigating an issue where
+ // non-supporting devices broadcast privacy screen support on certain
+ // displays.
+ VLOG(1) << "DisplaySnapshot created: display_id=" << display_id
+ << " type=" << type << " current_mode="
+ << (current_mode ? current_mode->ToString() : "nullptr")
+ << " privacy_screen_state=" << privacy_screen_state;
+
return std::make_unique<display::DisplaySnapshot>(
display_id, origin, physical_size, type, is_aspect_preserving_scaling,
has_overscan, privacy_screen_state, has_color_correction_matrix,
@@ -532,4 +541,15 @@ int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) {
}
}
+uint64_t GetEnumValueForName(int fd, int property_id, const char* str) {
+ ScopedDrmPropertyPtr res(drmModeGetProperty(fd, property_id));
+ for (int i = 0; i < res->count_enums; ++i) {
+ if (strcmp(res->enums[i].name, str) == 0) {
+ return res->enums[i].value;
+ }
+ }
+ NOTREACHED();
+ return 0;
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.h b/chromium/ui/ozone/platform/drm/common/drm_util.h
index 7fb2cebb77e..c8fc1b71124 100644
--- a/chromium/ui/ozone/platform/drm/common/drm_util.h
+++ b/chromium/ui/ozone/platform/drm/common/drm_util.h
@@ -98,6 +98,8 @@ float ModeRefreshRate(const drmModeModeInfo& mode);
bool ModeIsInterlaced(const drmModeModeInfo& mode);
+uint64_t GetEnumValueForName(int fd, int property_id, const char* str);
+
} // namespace ui
#endif // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc
index 943816dddf9..cc5e8c5f213 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -16,8 +16,8 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/free_deleter.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_for_io.h"
+#include "base/task/current_thread.h"
#include "base/task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
@@ -206,19 +206,19 @@ class DrmDevice::IOWatcher : public base::MessagePumpLibevent::FdWatcher {
private:
void Register() {
- DCHECK(base::MessageLoopCurrentForIO::IsSet());
- base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
+ DCHECK(base::CurrentIOThread::IsSet());
+ base::CurrentIOThread::Get()->WatchFileDescriptor(
fd_, true, base::MessagePumpForIO::WATCH_READ, &controller_, this);
}
void Unregister() {
- DCHECK(base::MessageLoopCurrentForIO::IsSet());
+ DCHECK(base::CurrentIOThread::IsSet());
controller_.StopWatchingFileDescriptor();
}
// base::MessagePumpLibevent::FdWatcher overrides:
void OnFileCanReadWithoutBlocking(int fd) override {
- DCHECK(base::MessageLoopCurrentForIO::IsSet());
+ DCHECK(base::CurrentIOThread::IsSet());
TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
if (!ProcessDrmEvent(
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc
index 0866e1989ed..8530924e44a 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -85,12 +85,13 @@ std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) {
return modes;
}
-void FillLinearValues(std::vector<display::GammaRampRGBEntry>* table,
- size_t table_size,
- float max_value) {
+void FillPowerFunctionValues(std::vector<display::GammaRampRGBEntry>* table,
+ size_t table_size,
+ float max_value,
+ float exponent) {
for (size_t i = 0; i < table_size; i++) {
- const uint16_t v =
- max_value * std::numeric_limits<uint16_t>::max() * i / (table_size - 1);
+ const uint16_t v = max_value * std::numeric_limits<uint16_t>::max() *
+ pow((static_cast<float>(i) + 1) / table_size, exponent);
struct display::GammaRampRGBEntry gamma_entry = {v, v, v};
table->push_back(gamma_entry);
}
@@ -104,10 +105,10 @@ DrmDisplay::DrmDisplay(ScreenManager* screen_manager,
drm_(drm),
current_color_space_(gfx::ColorSpace::CreateSRGB()) {}
-DrmDisplay::~DrmDisplay() {
-}
+DrmDisplay::~DrmDisplay() = default;
uint32_t DrmDisplay::connector() const {
+ DCHECK(connector_);
return connector_->connector_id;
}
@@ -117,12 +118,13 @@ std::unique_ptr<display::DisplaySnapshot> DrmDisplay::Update(
std::unique_ptr<display::DisplaySnapshot> params = CreateDisplaySnapshot(
info, drm_->get_fd(), drm_->device_path(), device_index, origin_);
crtc_ = info->crtc()->crtc_id;
- // TODO(dcastagna): consider taking ownership of |info->connector()|
+ // TODO(crbug.com/1119499): consider taking ownership of |info->connector()|
connector_ = ScopedDrmConnectorPtr(
drm_->GetConnector(info->connector()->connector_id));
if (!connector_) {
PLOG(ERROR) << "Failed to get connector "
<< info->connector()->connector_id;
+ return nullptr;
}
display_id_ = params->display_id();
@@ -228,7 +230,8 @@ void DrmDisplay::SetGammaCorrection(
// When both |degamma_lut| and |gamma_lut| are empty they are interpreted as
// "linear/pass-thru" [1]. If the display |is_hdr_capable_| we have to make
// sure the |current_color_space_| is considered properly.
- // [1] https://www.kernel.org/doc/html/v4.19/gpu/drm-kms.html#color-management-properties
+ // [1]
+ // https://www.kernel.org/doc/html/v4.19/gpu/drm-kms.html#color-management-properties
if (degamma_lut.empty() && gamma_lut.empty() && is_hdr_capable_)
SetColorSpace(current_color_space_);
else
@@ -271,12 +274,16 @@ void DrmDisplay::SetColorSpace(const gfx::ColorSpace& color_space) {
if (current_color_space_.IsHDR())
return CommitGammaCorrection(degamma, gamma);
- // TODO(mcasas) This should be the same value as in DisplayChangeObservers's
- // FillDisplayColorSpaces, move to a common place.
- constexpr float kHDRLevel = 2.0;
+ // TODO(mcasas) This should be the inverse value of DisplayChangeObservers's
+ // FillDisplayColorSpaces's kHDRLevel, move to a common place.
+ // TODO(b/165822222): adjust this level based on the display brightness.
+ constexpr float kSDRLevel = 0.85;
// TODO(mcasas): Retrieve this from the |drm_| HardwareDisplayPlaneManager.
- constexpr size_t kNumGammaSamples = 16ul;
- FillLinearValues(&gamma, kNumGammaSamples, 1.0 / kHDRLevel);
+ constexpr size_t kNumGammaSamples = 64ul;
+ // Only using kSDRLevel of the available values shifts the contrast ratio, we
+ // restore it via a smaller local gamma correction using this exponent.
+ constexpr float kExponent = 1.2;
+ FillPowerFunctionValues(&gamma, kNumGammaSamples, kSDRLevel, kExponent);
CommitGammaCorrection(degamma, gamma);
}
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
index 30e8c7ef8bc..c5aadaf7b11 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_display_unittest.cc
@@ -19,26 +19,21 @@
using ::testing::_;
using ::testing::SizeIs;
-// Verifies that the argument goes from 0 to the maximum uint16_t times |scale|.
-MATCHER_P(MatchesLinearRamp, scale, "") {
+// Verifies that the argument goes from 0 to the maximum uint16_t times |scale|
+// following a power function with |exponent|.
+MATCHER_P2(MatchesPowerFunction, scale, exponent, "") {
EXPECT_FALSE(arg.empty());
- EXPECT_EQ(arg.front().r, 0);
- EXPECT_EQ(arg.front().g, 0);
- EXPECT_EQ(arg.front().b, 0);
-
const uint16_t max_value = std::numeric_limits<uint16_t>::max() * scale;
- const auto middle_element = arg[arg.size() / 2];
- const uint16_t middle_value = max_value * (arg.size() / 2) / (arg.size() - 1);
- EXPECT_NEAR(middle_element.r, middle_value, 1);
- EXPECT_NEAR(middle_element.g, middle_value, 1);
- EXPECT_NEAR(middle_element.b, middle_value, 1);
-
- const uint16_t last_value = max_value;
- EXPECT_EQ(arg.back().r, last_value);
- EXPECT_EQ(arg.back().g, last_value);
- EXPECT_EQ(arg.back().b, last_value);
+ float i = 1.0;
+ for (const auto rgb_value : arg) {
+ const uint16_t expected_value = max_value * pow(i / arg.size(), exponent);
+ i++;
+ EXPECT_NEAR(rgb_value.r, expected_value, 1.0);
+ EXPECT_NEAR(rgb_value.g, expected_value, 1.0);
+ EXPECT_NEAR(rgb_value.b, expected_value, 1.0);
+ }
return true;
}
@@ -154,10 +149,11 @@ TEST_F(DrmDisplayTest, SetColorSpace) {
drm_display_.SetColorSpace(kHDRColorSpace);
const auto kSDRColorSpace = gfx::ColorSpace::CreateREC709();
- constexpr float kHDRLevel = 2.0;
- EXPECT_CALL(
- *plane_manager,
- SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(1.0 / kHDRLevel)));
+ constexpr float kSDRLevel = 0.85;
+ constexpr float kExponent = 1.2;
+ EXPECT_CALL(*plane_manager,
+ SetGammaCorrection(_, SizeIs(0),
+ MatchesPowerFunction(kSDRLevel, kExponent)));
drm_display_.SetColorSpace(kSDRColorSpace);
}
@@ -181,10 +177,11 @@ TEST_F(DrmDisplayTest, SetEmptyGammaCorrectionHDRDisplay) {
ON_CALL(*plane_manager, SetGammaCorrection(_, _, _))
.WillByDefault(::testing::Return(true));
- constexpr float kHDRLevel = 2.0;
- EXPECT_CALL(
- *plane_manager,
- SetGammaCorrection(_, SizeIs(0), MatchesLinearRamp(1.0 / kHDRLevel)));
+ constexpr float kSDRLevel = 0.85;
+ constexpr float kExponent = 1.2;
+ EXPECT_CALL(*plane_manager,
+ SetGammaCorrection(_, SizeIs(0),
+ MatchesPowerFunction(kSDRLevel, kExponent)));
drm_display_.SetGammaCorrection(std::vector<display::GammaRampRGBEntry>(),
std::vector<display::GammaRampRGBEntry>());
}
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index e7642e41351..544df3618e8 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -102,8 +102,14 @@ MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
} else {
displays_.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm));
}
- params_list.push_back(
- displays_.back()->Update(display_info.get(), device_index));
+
+ auto display_snapshot =
+ displays_.back()->Update(display_info.get(), device_index);
+ if (display_snapshot) {
+ params_list.push_back(std::move(display_snapshot));
+ } else {
+ displays_.pop_back();
+ }
}
device_index++;
}
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
index c4de6cc393e..3877bce8b68 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -416,9 +416,8 @@ TEST_F(DrmOverlayValidatorTest,
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
@@ -462,9 +461,8 @@ TEST_F(DrmOverlayValidatorTest,
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
@@ -505,9 +503,8 @@ TEST_F(DrmOverlayValidatorTest,
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
@@ -548,9 +545,8 @@ TEST_F(DrmOverlayValidatorTest, OptimalFormatXRGB_MirroredControllers) {
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
@@ -585,9 +581,8 @@ TEST_F(DrmOverlayValidatorTest,
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
@@ -621,9 +616,8 @@ TEST_F(DrmOverlayValidatorTest,
InitDrmStatesAndControllers(crtc_states);
ui::HardwareDisplayController* controller = window_->GetController();
- controller->AddCrtc(
- std::unique_ptr<ui::CrtcController>(new ui::CrtcController(
- drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1)));
+ controller->AddCrtc(std::make_unique<ui::CrtcController>(
+ drm_.get(), kCrtcIdBase + 1, kConnectorIdBase + 1));
ui::DrmOverlayPlane plane1(CreateBuffer(), nullptr);
EXPECT_TRUE(controller->Modeset(plane1, kDefaultMode));
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc
index 13b3dd7a141..789edc91f68 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -325,22 +325,23 @@ void DrmThread::RefreshNativeDisplays(
std::move(callback).Run(display_manager_->GetDisplays());
}
-void DrmThread::ConfigureNativeDisplay(
- const display::DisplayConfigurationParams& display_config_params,
- base::OnceCallback<void(int64_t, bool)> callback) {
- TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplay");
-
- if (display_config_params.mode) {
- std::move(callback).Run(
- display_config_params.id,
- display_manager_->ConfigureDisplay(display_config_params.id,
- *display_config_params.mode.value(),
- display_config_params.origin));
- } else {
- std::move(callback).Run(
- display_config_params.id,
- display_manager_->DisableDisplay(display_config_params.id));
+void DrmThread::ConfigureNativeDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ base::OnceCallback<void(const base::flat_map<int64_t, bool>&)> callback) {
+ TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplays");
+
+ base::flat_map<int64_t, bool> statuses;
+ for (const auto& config : config_requests) {
+ bool status = false;
+ if (config.mode) {
+ status = display_manager_->ConfigureDisplay(
+ config.id, *config.mode.value(), config.origin);
+ } else {
+ status = display_manager_->DisableDisplay(config.id);
+ }
+ statuses.insert(std::make_pair(config.id, status));
}
+ std::move(callback).Run(statuses);
}
void DrmThread::TakeDisplayControl(base::OnceCallback<void(bool)> callback) {
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h
index 71457f24c99..a744f482bd1 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -147,9 +147,9 @@ class DrmThread : public base::Thread,
base::OnceCallback<void(MovableDisplaySnapshots)> callback) override;
void AddGraphicsDevice(const base::FilePath& path, base::File file) override;
void RemoveGraphicsDevice(const base::FilePath& path) override;
- void ConfigureNativeDisplay(
- const display::DisplayConfigurationParams& display_config_params,
- base::OnceCallback<void(int64_t, bool)> callback) override;
+ void ConfigureNativeDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ ConfigureNativeDisplaysCallback callback) override;
void GetHDCPState(int64_t display_id,
base::OnceCallback<void(int64_t, bool, display::HDCPState)>
callback) override;
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
index 685cd8fcea8..fa04ef64c35 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc
@@ -175,10 +175,23 @@ void DrmThreadProxy::AddDrmDeviceReceiver(
DCHECK(drm_thread_.task_runner()) << "DrmThreadProxy::AddDrmDeviceReceiver "
"drm_thread_ task runner missing";
- drm_thread_.task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&DrmThread::AddDrmDeviceReceiver,
- base::Unretained(&drm_thread_), std::move(receiver)));
+ if (drm_thread_.task_runner()->BelongsToCurrentThread()) {
+ drm_thread_.AddDrmDeviceReceiver(std::move(receiver));
+ } else {
+ drm_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&DrmThread::AddDrmDeviceReceiver,
+ base::Unretained(&drm_thread_), std::move(receiver)));
+ }
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+DrmThreadProxy::GetDrmThreadTaskRunner() {
+ return drm_thread_.task_runner();
+}
+
+bool DrmThreadProxy::WaitUntilDrmThreadStarted() {
+ return drm_thread_.WaitUntilThreadStarted();
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
index 303a7eb9cbe..5b55beee457 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h
@@ -83,6 +83,9 @@ class DrmThreadProxy {
void AddDrmDeviceReceiver(
mojo::PendingReceiver<ozone::mojom::DrmDevice> receiver);
+ bool WaitUntilDrmThreadStarted();
+ scoped_refptr<base::SingleThreadTaskRunner> GetDrmThreadTaskRunner();
+
private:
DrmThread drm_thread_;
diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
index 824992ab157..ffb8ffb9359 100644
--- a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc
@@ -34,8 +34,8 @@
namespace {
// Mode of size 6x4.
-const drmModeModeInfo kDefaultMode =
- {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0, {'\0'}};
const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
const uint32_t kDefaultCrtc = 1;
@@ -68,7 +68,7 @@ SkBitmap AllocateBitmap(const gfx::Size& size) {
class DrmWindowTest : public testing::Test {
public:
- DrmWindowTest() {}
+ DrmWindowTest() = default;
void SetUp() override;
void TearDown() override;
diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 5953baf4b28..9a220c3ecdf 100644
--- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -117,6 +117,7 @@ class GLOzoneEGLGbm : public GLOzoneEGL {
}
std::vector<EGLDeviceEXT> devices(DRM_MAX_MINOR, EGL_NO_DEVICE_EXT);
+ EGLDeviceEXT virgl_device = EGL_NO_DEVICE_EXT;
EGLDeviceEXT amdgpu_device = EGL_NO_DEVICE_EXT;
EGLDeviceEXT i915_device = EGL_NO_DEVICE_EXT;
EGLint num_devices = 0;
@@ -128,12 +129,20 @@ class GLOzoneEGLGbm : public GLOzoneEGL {
eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
if (!filename) // Not a DRM device.
continue;
+ if (IsDriverName(filename, "virtio_gpu"))
+ virgl_device = device;
if (IsDriverName(filename, "amdgpu"))
amdgpu_device = device;
if (IsDriverName(filename, "i915"))
i915_device = device;
}
+ if (virgl_device != EGL_NO_DEVICE_EXT) {
+ native_display_ = gl::EGLDisplayPlatform(
+ reinterpret_cast<EGLNativeDisplayType>(virgl_device),
+ EGL_PLATFORM_DEVICE_EXT);
+ }
+
if (amdgpu_device != EGL_NO_DEVICE_EXT) {
native_display_ = gl::EGLDisplayPlatform(
reinterpret_cast<EGLNativeDisplayType>(amdgpu_device),
@@ -341,8 +350,7 @@ std::unique_ptr<OverlaySurface> GbmSurfaceFactory::CreateOverlaySurface(
}
std::unique_ptr<SurfaceOzoneCanvas> GbmSurfaceFactory::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ gfx::AcceleratedWidget widget) {
DCHECK(thread_checker_.CalledOnValidThread());
LOG(ERROR) << "Software rendering mode is not supported with GBM platform";
return nullptr;
diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
index ab3089b7242..4ac05739713 100644
--- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
+++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
@@ -54,8 +54,7 @@ class GbmSurfaceFactory : public SurfaceFactoryOzone {
std::unique_ptr<OverlaySurface> CreateOverlaySurface(
gfx::AcceleratedWidget window) override;
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
scoped_refptr<gfx::NativePixmap> CreateNativePixmap(
gfx::AcceleratedWidget widget,
VkDevice vk_device,
diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 29b76be970e..a868bb5175b 100644
--- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -290,7 +290,7 @@ void GbmSurfaceless::OnSubmission(gfx::SwapResult result,
void GbmSurfaceless::OnPresentation(const gfx::PresentationFeedback& feedback) {
gfx::PresentationFeedback feedback_copy = feedback;
- if (submitted_frame_gpu_fence_) {
+ if (submitted_frame_gpu_fence_ && !feedback.failed()) {
feedback_copy.ready_timestamp =
submitted_frame_gpu_fence_->GetMaxTimestamp();
}
diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc
index 6505127b024..272a86d8166 100644
--- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.cc
@@ -8,6 +8,7 @@
#include <drm_mode.h>
#include "base/logging.h"
+#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
@@ -92,6 +93,14 @@ bool HardwareDisplayPlane::Initialize(DrmDevice* drm) {
if (properties_.type.id)
type_ = GetPlaneType(properties_.type.value);
+ if (properties_.plane_color_encoding.id) {
+ color_encoding_bt601_ =
+ GetEnumValueForName(drm->get_fd(), properties_.plane_color_encoding.id,
+ "ITU-R BT.601 YCbCr");
+ color_range_limited_ = GetEnumValueForName(
+ drm->get_fd(), properties_.plane_color_range.id, "YCbCr limited range");
+ }
+
VLOG(3) << "Initialized plane=" << id_ << " crtc_mask=" << std::hex << "0x"
<< crtc_mask_ << std::dec
<< " supported_formats_count=" << supported_formats_.size()
@@ -168,6 +177,10 @@ void HardwareDisplayPlane::InitializeProperties(DrmDevice* drm) {
GetDrmPropertyForName(drm, props.get(), "IN_FENCE_FD",
&properties_.in_fence_fd);
GetDrmPropertyForName(drm, props.get(), "PLANE_CTM", &properties_.plane_ctm);
+ GetDrmPropertyForName(drm, props.get(), "COLOR_ENCODING",
+ &properties_.plane_color_encoding);
+ GetDrmPropertyForName(drm, props.get(), "COLOR_RANGE",
+ &properties_.plane_color_range);
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h
index 0f85f1bdc58..ea614863ba4 100644
--- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h
+++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h
@@ -68,6 +68,8 @@ class HardwareDisplayPlane {
DrmDevice::Property in_formats;
DrmDevice::Property in_fence_fd;
DrmDevice::Property plane_ctm;
+ DrmDevice::Property plane_color_encoding;
+ DrmDevice::Property plane_color_range;
};
const uint32_t id_;
@@ -82,6 +84,9 @@ class HardwareDisplayPlane {
std::vector<uint32_t> supported_formats_;
std::vector<drm_format_modifier> supported_format_modifiers_;
+ uint64_t color_encoding_bt601_;
+ uint64_t color_range_limited_;
+
private:
void InitializeProperties(DrmDevice* drm);
diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
index 1899daf4e7c..7b50c3c8fea 100644
--- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
@@ -64,6 +64,12 @@ bool HardwareDisplayPlaneAtomic::Initialize(DrmDevice* drm) {
properties_.src_w.id && properties_.src_h.id;
LOG_IF(ERROR, !ret) << "Failed to find all required properties for plane="
<< id_;
+
+ ret &= (properties_.plane_color_encoding.id == 0) ==
+ (properties_.plane_color_range.id == 0);
+ LOG_IF(ERROR, !ret) << "Inconsistent color management properties for plane="
+ << id_;
+
return ret;
}
@@ -119,6 +125,16 @@ bool HardwareDisplayPlaneAtomic::SetPlaneData(
AddPropertyIfValid(property_set, id_, properties_.in_fence_fd);
}
+ if (properties_.plane_color_encoding.id) {
+ properties_.plane_color_encoding.value = color_encoding_bt601_;
+ properties_.plane_color_range.value = color_range_limited_;
+ plane_set_succeeded =
+ plane_set_succeeded &&
+ AddPropertyIfValid(property_set, id_,
+ properties_.plane_color_encoding) &&
+ AddPropertyIfValid(property_set, id_, properties_.plane_color_range);
+ }
+
if (!plane_set_succeeded) {
LOG(ERROR) << "Failed to set plane data";
return false;
diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
index 577cfa7d540..2cbc6a6bc32 100644
--- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
+++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc
@@ -44,8 +44,8 @@ drmModeModeInfo Mode(uint16_t hdisplay, uint16_t vdisplay) {
class ScreenManagerTest : public testing::Test {
public:
- ScreenManagerTest() {}
- ~ScreenManagerTest() override {}
+ ScreenManagerTest() = default;
+ ~ScreenManagerTest() override = default;
gfx::Rect GetPrimaryBounds() const {
return gfx::Rect(0, 0, kDefaultMode.hdisplay, kDefaultMode.vdisplay);
diff --git a/chromium/ui/ozone/platform/drm/host/drm_device_connector.cc b/chromium/ui/ozone/platform/drm/host/drm_device_connector.cc
index e546a2294cc..71b27383aa4 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/chromium/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "base/bind.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "ui/ozone/platform/drm/host/host_drm_device.h"
diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc
index 3da0ad8e912..23c44ce2e5a 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc
+++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc
@@ -11,7 +11,6 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
@@ -36,37 +35,6 @@ void DrmDisplayHost::UpdateDisplaySnapshot(
snapshot_ = std::move(params);
}
-void DrmDisplayHost::Configure(const display::DisplayMode* mode,
- const gfx::Point& origin,
- display::ConfigureCallback callback) {
- if (is_dummy_) {
- std::move(callback).Run(true);
- return;
- }
-
- configure_callback_ = std::move(callback);
- bool status = false;
-
- display::DisplayConfigurationParams display_config_params(
- snapshot_->display_id(), origin, mode);
- status = sender_->GpuConfigureNativeDisplay(display_config_params);
-
- if (!status)
- OnDisplayConfigured(false);
-}
-
-void DrmDisplayHost::OnDisplayConfigured(bool status) {
- if (!configure_callback_.is_null()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(configure_callback_), status));
- } else {
- LOG(ERROR) << "Got unexpected event for display "
- << snapshot_->display_id();
- }
-
- configure_callback_.Reset();
-}
-
void DrmDisplayHost::GetHDCPState(display::GetHDCPStateCallback callback) {
get_hdcp_callback_ = std::move(callback);
if (!sender_->GpuGetHDCPState(snapshot_->display_id()))
@@ -134,8 +102,6 @@ void DrmDisplayHost::OnGpuThreadReady() {
void DrmDisplayHost::OnGpuThreadRetired() {}
void DrmDisplayHost::ClearCallbacks() {
- if (!configure_callback_.is_null())
- OnDisplayConfigured(false);
if (!get_hdcp_callback_.is_null())
OnHDCPStateReceived(false, display::HDCP_STATE_UNDESIRED);
if (!set_hdcp_callback_.is_null())
diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.h b/chromium/ui/ozone/platform/drm/host/drm_display_host.h
index bca23f727b9..5bb6a114ee3 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_display_host.h
+++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/native_display_delegate.h"
#include "ui/ozone/platform/drm/host/gpu_thread_observer.h"
@@ -28,11 +29,9 @@ class DrmDisplayHost : public GpuThreadObserver {
~DrmDisplayHost() override;
display::DisplaySnapshot* snapshot() const { return snapshot_.get(); }
+ bool is_dummy() const { return is_dummy_; }
void UpdateDisplaySnapshot(std::unique_ptr<display::DisplaySnapshot> params);
- void Configure(const display::DisplayMode* mode,
- const gfx::Point& origin,
- display::ConfigureCallback callback);
void GetHDCPState(display::GetHDCPStateCallback callback);
void SetHDCPState(display::HDCPState state,
display::SetHDCPStateCallback callback);
@@ -44,7 +43,6 @@ class DrmDisplayHost : public GpuThreadObserver {
// Called when the IPC from the GPU process arrives to answer the above
// commands.
- void OnDisplayConfigured(bool status);
void OnHDCPStateReceived(bool status, display::HDCPState state);
void OnHDCPStateUpdated(bool status);
@@ -65,7 +63,6 @@ class DrmDisplayHost : public GpuThreadObserver {
// synchronous and succeed.
bool is_dummy_;
- display::ConfigureCallback configure_callback_;
display::GetHDCPStateCallback get_hdcp_callback_;
display::SetHDCPStateCallback set_hdcp_callback_;
diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index aa4298703a7..e3429cbce13 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -39,7 +39,9 @@ typedef base::OnceCallback<void(const base::FilePath&,
const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d";
const char* kDisplayActionString[] = {
- "ADD", "REMOVE", "CHANGE",
+ "ADD",
+ "REMOVE",
+ "CHANGE",
};
// Find sysfs device path for the given device path.
@@ -232,6 +234,23 @@ void DrmDisplayHostManager::UpdateDisplays(
}
}
+void DrmDisplayHostManager::ConfigureDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) {
+ base::flat_map<int64_t, bool> dummy_statuses;
+ bool is_any_dummy = false;
+ for (auto& config : config_requests) {
+ is_any_dummy |= GetDisplay(config.id)->is_dummy();
+ dummy_statuses.insert(std::make_pair(config.id, true));
+ }
+ if (is_any_dummy) {
+ std::move(callback).Run(dummy_statuses);
+ return;
+ }
+
+ proxy_->GpuConfigureNativeDisplays(config_requests, std::move(callback));
+}
+
void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) {
if (event.device_type() != DeviceEvent::DISPLAY)
return;
@@ -393,16 +412,6 @@ void DrmDisplayHostManager::GpuHasUpdatedNativeDisplays(
}
}
-void DrmDisplayHostManager::GpuConfiguredDisplay(int64_t display_id,
- bool status) {
- DrmDisplayHost* display = GetDisplay(display_id);
- if (display) {
- display->OnDisplayConfigured(status);
- } else {
- LOG(ERROR) << "Couldn't find display with id=" << display_id;
- }
-}
-
void DrmDisplayHostManager::GpuReceivedHDCPState(int64_t display_id,
bool status,
display::HDCPState state) {
diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h
index 5622804e08a..bddc9b5beac 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h
+++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h
@@ -51,6 +51,9 @@ class DrmDisplayHostManager : public DeviceEventObserver, GpuThreadObserver {
void TakeDisplayControl(display::DisplayControlCallback callback);
void RelinquishDisplayControl(display::DisplayControlCallback callback);
void UpdateDisplays(display::GetDisplaysCallback callback);
+ void ConfigureDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback);
// DeviceEventObserver overrides:
void OnDeviceEvent(const DeviceEvent& event) override;
@@ -63,7 +66,6 @@ class DrmDisplayHostManager : public DeviceEventObserver, GpuThreadObserver {
// Communication-free implementations of actions performed in response to
// messages from the GPU thread.
void GpuHasUpdatedNativeDisplays(MovableDisplaySnapshots displays);
- void GpuConfiguredDisplay(int64_t display_id, bool status);
void GpuReceivedHDCPState(int64_t display_id,
bool status,
display::HDCPState state);
diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
index c5465582fb1..c239f99e51d 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
+++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -15,8 +15,7 @@ namespace ui {
DrmNativeDisplayDelegate::DrmNativeDisplayDelegate(
DrmDisplayHostManager* display_manager)
- : display_manager_(display_manager) {
-}
+ : display_manager_(display_manager) {}
DrmNativeDisplayDelegate::~DrmNativeDisplayDelegate() {
display_manager_->RemoveDelegate(this);
@@ -51,12 +50,10 @@ void DrmNativeDisplayDelegate::GetDisplays(
display_manager_->UpdateDisplays(std::move(callback));
}
-void DrmNativeDisplayDelegate::Configure(const display::DisplaySnapshot& output,
- const display::DisplayMode* mode,
- const gfx::Point& origin,
- display::ConfigureCallback callback) {
- DrmDisplayHost* display = display_manager_->GetDisplay(output.display_id());
- display->Configure(mode, origin, std::move(callback));
+void DrmNativeDisplayDelegate::Configure(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) {
+ display_manager_->ConfigureDisplays(config_requests, std::move(callback));
}
void DrmNativeDisplayDelegate::GetHDCPState(
diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h
index 249e032e73a..fd141f1fdc6 100644
--- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h
+++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h
@@ -29,10 +29,9 @@ class DrmNativeDisplayDelegate : public display::NativeDisplayDelegate {
void RelinquishDisplayControl(
display::DisplayControlCallback callback) override;
void GetDisplays(display::GetDisplaysCallback callback) override;
- void Configure(const display::DisplaySnapshot& output,
- const display::DisplayMode* mode,
- const gfx::Point& origin,
- display::ConfigureCallback callback) override;
+ void Configure(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) override;
void GetHDCPState(const display::DisplaySnapshot& output,
display::GetHDCPStateCallback callback) override;
void SetHDCPState(const display::DisplaySnapshot& output,
diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h
index 8b9b8a39eea..c782923ddf1 100644
--- a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h
+++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h
@@ -9,6 +9,7 @@
#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/gamma_ramp_rgb_entry.h"
+#include "ui/display/types/native_display_delegate.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
@@ -21,7 +22,7 @@ class GpuThreadObserver;
// to use either a GPU process or thread for their implementation.
class GpuThreadAdapter {
public:
- virtual ~GpuThreadAdapter() {}
+ virtual ~GpuThreadAdapter() = default;
virtual bool IsConnected() = 0;
virtual void AddGpuThreadObserver(GpuThreadObserver* observer) = 0;
@@ -43,8 +44,9 @@ class GpuThreadAdapter {
virtual bool GpuRemoveGraphicsDevice(const base::FilePath& path) = 0;
// Services needed by DrmDisplayHost
- virtual bool GpuConfigureNativeDisplay(
- const display::DisplayConfigurationParams& display_config_params) = 0;
+ virtual void GpuConfigureNativeDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) = 0;
virtual bool GpuGetHDCPState(int64_t display_id) = 0;
virtual bool GpuSetHDCPState(int64_t display_id,
display::HDCPState state) = 0;
diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc
index 58bd76117d4..960df0d3905 100644
--- a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -128,19 +128,19 @@ bool HostDrmDevice::GpuRefreshNativeDisplays() {
return true;
}
-bool HostDrmDevice::GpuConfigureNativeDisplay(
- const display::DisplayConfigurationParams& display_config_params) {
+void HostDrmDevice::GpuConfigureNativeDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
- if (!IsConnected())
- return false;
-
- auto callback =
- base::BindOnce(&HostDrmDevice::GpuConfigureNativeDisplayCallback, this);
-
- drm_device_->ConfigureNativeDisplay(display_config_params,
- std::move(callback));
-
- return true;
+ if (IsConnected()) {
+ drm_device_->ConfigureNativeDisplays(config_requests, std::move(callback));
+ } else {
+ // If not connected, report failure to config.
+ base::flat_map<int64_t, bool> dummy_statuses;
+ for (const auto& config : config_requests)
+ dummy_statuses.insert(std::make_pair(config.id, false));
+ std::move(callback).Run(dummy_statuses);
+ }
}
bool HostDrmDevice::GpuTakeDisplayControl() {
@@ -251,12 +251,6 @@ bool HostDrmDevice::GpuSetPrivacyScreen(int64_t display_id, bool enabled) {
return true;
}
-void HostDrmDevice::GpuConfigureNativeDisplayCallback(int64_t display_id,
- bool success) const {
- DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
- display_manager_->GpuConfiguredDisplay(display_id, success);
-}
-
void HostDrmDevice::GpuRefreshNativeDisplaysCallback(
MovableDisplaySnapshots displays) const {
DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.h b/chromium/ui/ozone/platform/drm/host/host_drm_device.h
index a21505193c6..2484d706c9e 100644
--- a/chromium/ui/ozone/platform/drm/host/host_drm_device.h
+++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.h
@@ -65,8 +65,9 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>,
bool GpuRemoveGraphicsDevice(const base::FilePath& path) override;
// Services needed by DrmDisplayHost
- bool GpuConfigureNativeDisplay(const display::DisplayConfigurationParams&
- display_config_params) override;
+ void GpuConfigureNativeDisplays(
+ const std::vector<display::DisplayConfigurationParams>& config_requests,
+ display::ConfigureCallback callback) override;
bool GpuGetHDCPState(int64_t display_id) override;
bool GpuSetHDCPState(int64_t display_id, display::HDCPState state) override;
bool GpuSetColorMatrix(int64_t display_id,
@@ -92,9 +93,6 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>,
void OnDrmServiceStarted();
- void GpuConfigureNativeDisplayCallback(int64_t display_id,
- bool success) const;
-
void GpuRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const;
void GpuTakeDisplayControlCallback(bool success) const;
void GpuRelinquishDisplayControlCallback(bool success) const;
diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc
index 9e0cc71662e..4e81e147590 100644
--- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -101,26 +101,50 @@ class OzonePlatformGbm : public OzonePlatform {
// In multi-process mode, this function must be executed in Viz as it sets up
// the callbacks needed for Mojo receivers. In single process mode, it may be
// called on any thread. It must follow one of |InitializeUI| or
- // |InitializeGPU|. While the caller may choose to invoke this method before
- // entering the sandbox, the actual interface adding has to happen on the DRM
- // Device thread and so will be deferred until the DRM thread is running.
+ // |InitializeGPU|.
void AddInterfaces(mojo::BinderMap* binders) override {
- binders->Add<ozone::mojom::DrmDevice>(
- base::BindRepeating(&OzonePlatformGbm::CreateDrmDeviceReceiver,
- weak_factory_.GetWeakPtr()),
- base::ThreadTaskRunnerHandle::Get());
+ if (single_process()) {
+ // This logic in multi-process mode causes deadlock to happen, where
+ // |gpu_task_runner_| blocks on drm_thread while drm_thread has not
+ // received DrmDevice mojo endpoint. Hence, the caller should invoke this
+ // method after drm_thread is started.
+ binders->Add<ozone::mojom::DrmDevice>(
+ base::BindRepeating(
+ &OzonePlatformGbm::CreateDrmDeviceReceiverOnGpuThread,
+ weak_factory_.GetWeakPtr()),
+ gpu_task_runner_);
+ } else {
+ // In multi-process mode DRM thread is started right after sandbox entry,
+ // |AddInterfaces| is invoked from VizMainImpl so DRM thread must have
+ // been started. |WaitUntilDrmThreadStarted| is not expected to do a real
+ // wait but helps assuming that the task runner exists.
+ drm_thread_proxy_->WaitUntilDrmThreadStarted();
+ // There's no need for binder callback to bounce on |gpu_task_runner_|.
+ // Binder callbacks should directly run on DRM thread.
+ binders->Add<ozone::mojom::DrmDevice>(
+ base::BindRepeating(
+ &OzonePlatformGbm::CreateDrmDeviceReceiverOnDrmThread,
+ weak_factory_.GetWeakPtr()),
+ drm_thread_proxy_->GetDrmThreadTaskRunner());
+ }
}
- // Runs on the thread where AddInterfaces was invoked. But the endpoint is
- // always bound on the DRM thread.
- void CreateDrmDeviceReceiver(
+ // Runs on the gpu thread. But the endpoint is always bound on the DRM thread.
+ void CreateDrmDeviceReceiverOnGpuThread(
mojo::PendingReceiver<ozone::mojom::DrmDevice> receiver) {
+ CHECK(single_process());
if (drm_thread_started_)
drm_thread_proxy_->AddDrmDeviceReceiver(std::move(receiver));
else
pending_gpu_adapter_receivers_.push_back(std::move(receiver));
}
+ void CreateDrmDeviceReceiverOnDrmThread(
+ mojo::PendingReceiver<ozone::mojom::DrmDevice> receiver) {
+ CHECK(!single_process());
+ drm_thread_proxy_->AddDrmDeviceReceiver(std::move(receiver));
+ }
+
// Runs on the thread that invoked |AddInterfaces| to drain the queue of
// receiver requests that could not be satisfied until the DRM thread is
// available (i.e. if waiting until the sandbox has been entered.)
diff --git a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc
index 9d77b6c3f6a..4ecbd2c7900 100644
--- a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -234,9 +234,7 @@ GLOzone* HeadlessSurfaceFactory::GetGLOzone(
}
std::unique_ptr<SurfaceOzoneCanvas>
-HeadlessSurfaceFactory::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+HeadlessSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget) {
return std::make_unique<FileSurface>(GetPathForWidget(base_path_, widget));
}
diff --git a/chromium/ui/ozone/platform/headless/headless_surface_factory.h b/chromium/ui/ozone/platform/headless/headless_surface_factory.h
index abde7d4d8ab..e11c83eecf4 100644
--- a/chromium/ui/ozone/platform/headless/headless_surface_factory.h
+++ b/chromium/ui/ozone/platform/headless/headless_surface_factory.h
@@ -24,8 +24,7 @@ class HeadlessSurfaceFactory : public SurfaceFactoryOzone {
std::vector<gl::GLImplementation> GetAllowedGLImplementations() override;
GLOzone* GetGLOzone(gl::GLImplementation implementation) override;
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
scoped_refptr<gfx::NativePixmap> CreateNativePixmap(
gfx::AcceleratedWidget widget,
VkDevice vk_device,
diff --git a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc
index 707948351e0..01107a50f94 100644
--- a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc
+++ b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc
@@ -138,6 +138,7 @@ OzonePlatform* CreateOzonePlatformHeadless() {
base::FilePath location;
if (cmd->HasSwitch(switches::kOzoneDumpFile))
location = cmd->GetSwitchValuePath(switches::kOzoneDumpFile);
+ cmd->AppendSwitch(switches::kDisableRunningAsSystemCompositor);
return new OzonePlatformHeadless(location);
}
diff --git a/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc b/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc
index a05febd52d9..6f65b910af1 100644
--- a/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc
+++ b/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc
@@ -8,6 +8,7 @@
#include <lib/zx/vmo.h>
#include <vector>
+#include "base/bits.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/system/sys_info.h"
#include "ui/gfx/buffer_format_util.h"
@@ -47,7 +48,7 @@ class ClientNativePixmapFuchsia : public gfx::ClientNativePixmap {
// Round mapping size to align with the page size.
size_t page_size = base::SysInfo::VMAllocationGranularity();
- mapping_size_ = (mapping_size_ + page_size - 1) & ~(page_size - 1);
+ mapping_size_ = base::bits::Align(mapping_size_, page_size);
zx_status_t status =
zx::vmar::root_self()->map(0, handle_.planes[0].vmo, 0, mapping_size_,
diff --git a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc
index fcd5d1ccf32..ec639a62722 100644
--- a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc
+++ b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc
@@ -11,9 +11,9 @@
#include "base/check.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_type.h"
#include "base/notreached.h"
+#include "base/task/current_thread.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
@@ -44,11 +44,11 @@ namespace ui {
namespace {
constexpr OzonePlatform::PlatformProperties kScenicPlatformProperties{
- /*needs_view_token=*/true,
- /*custom_frame_pref_default=*/false,
- /*use_system_title_bar=*/false,
- /*message_pump_type_for_gpu=*/base::MessagePumpType::IO,
- /*supports_vulkan_swap_chain=*/true,
+ .needs_view_token = true,
+ .custom_frame_pref_default = false,
+ .use_system_title_bar = false,
+ .message_pump_type_for_gpu = base::MessagePumpType::IO,
+ .supports_vulkan_swap_chain = true,
};
class ScenicPlatformEventSource : public ui::PlatformEventSource {
@@ -61,9 +61,8 @@ class ScenicPlatformEventSource : public ui::PlatformEventSource {
};
// OzonePlatform for Scenic.
-class OzonePlatformScenic
- : public OzonePlatform,
- public base::MessageLoopCurrent::DestructionObserver {
+class OzonePlatformScenic : public OzonePlatform,
+ public base::CurrentThread::DestructionObserver {
public:
OzonePlatformScenic() = default;
~OzonePlatformScenic() override = default;
@@ -188,7 +187,7 @@ class OzonePlatformScenic
surface_factory_->Initialize(std::move(gpu_host_remote));
bound_in_main_process_ = true;
- base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
+ base::CurrentThread::Get()->AddDestructionObserver(this);
}
void ShutdownInMainProcess() {
@@ -199,7 +198,7 @@ class OzonePlatformScenic
bound_in_main_process_ = false;
}
- // base::MessageLoopCurrent::DestructionObserver implementation.
+ // base::CurrentThread::DestructionObserver implementation.
void WillDestroyCurrentMessageLoop() override { ShutdownInMainProcess(); }
std::unique_ptr<ScenicWindowManager> window_manager_;
diff --git a/chromium/ui/ozone/platform/scenic/scenic_gpu_host.cc b/chromium/ui/ozone/platform/scenic/scenic_gpu_host.cc
index 878f1ca85e1..c01c57f38b4 100644
--- a/chromium/ui/ozone/platform/scenic/scenic_gpu_host.cc
+++ b/chromium/ui/ozone/platform/scenic/scenic_gpu_host.cc
@@ -11,7 +11,6 @@
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "ui/ozone/platform/scenic/scenic_window.h"
#include "ui/ozone/platform/scenic/scenic_window_manager.h"
diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc
index 7719bac9d88..0b5749ee013 100644
--- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc
+++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc
@@ -207,8 +207,7 @@ ScenicSurfaceFactory::CreatePlatformWindowSurface(
}
std::unique_ptr<SurfaceOzoneCanvas> ScenicSurfaceFactory::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ gfx::AcceleratedWidget widget) {
ScenicSurface* surface = GetSurface(widget);
return std::make_unique<ScenicWindowCanvas>(surface);
}
diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h
index 945d72429de..0a2e11de524 100644
--- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h
+++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h
@@ -47,8 +47,7 @@ class ScenicSurfaceFactory : public SurfaceFactoryOzone {
std::unique_ptr<PlatformWindowSurface> CreatePlatformWindowSurface(
gfx::AcceleratedWidget widget) override;
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
scoped_refptr<gfx::NativePixmap> CreateNativePixmap(
gfx::AcceleratedWidget widget,
VkDevice vk_device,
diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
index d320987b58e..300e188712e 100644
--- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
+++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
@@ -129,11 +129,13 @@ SysmemBufferCollection::SysmemBufferCollection(gfx::SysmemBufferCollectionId id)
bool SysmemBufferCollection::Initialize(
fuchsia::sysmem::Allocator_Sync* allocator,
+ zx::channel token_handle,
gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
VkDevice vk_device,
- size_t num_buffers) {
+ size_t min_buffer_count,
+ bool force_protected) {
DCHECK(IsNativePixmapConfigSupported(format, usage));
DCHECK(!collection_);
DCHECK(!vk_buffer_collection_);
@@ -143,51 +145,41 @@ bool SysmemBufferCollection::Initialize(
if (vk_device == VK_NULL_HANDLE)
return false;
- min_size_ = size;
+ if (size.IsEmpty()) {
+ // Buffer collection that doesn't have explicit size is expected to be
+ // shared with other participants, who will determine the actual image size.
+ DCHECK(token_handle);
+
+ // Set nominal size of 1x1, which will be used only for
+ // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the
+ // allocated buffers is determined by constraints set by other sysmem
+ // clients for the same collection. Size of the Vulkan image is determined
+ // by the values passed to CreateVkImage().
+ min_size_ = gfx::Size(1, 1);
+ } else {
+ min_size_ = size;
+ }
+
format_ = format;
usage_ = usage;
vk_device_ = vk_device;
+ is_protected_ = force_protected;
fuchsia::sysmem::BufferCollectionTokenSyncPtr collection_token;
- zx_status_t status =
- allocator->AllocateSharedCollection(collection_token.NewRequest());
- if (status != ZX_OK) {
- ZX_DLOG(ERROR, status)
- << "fuchsia.sysmem.Allocator.AllocateSharedCollection()";
- return false;
+ if (token_handle) {
+ collection_token.Bind(std::move(token_handle));
+ } else {
+ zx_status_t status =
+ allocator->AllocateSharedCollection(collection_token.NewRequest());
+ if (status != ZX_OK) {
+ ZX_DLOG(ERROR, status)
+ << "fuchsia.sysmem.Allocator.AllocateSharedCollection()";
+ return false;
+ }
}
return InitializeInternal(allocator, std::move(collection_token),
- num_buffers);
-}
-
-bool SysmemBufferCollection::Initialize(
- fuchsia::sysmem::Allocator_Sync* allocator,
- VkDevice vk_device,
- zx::channel token_handle,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- bool force_protected) {
- DCHECK(!collection_);
- DCHECK(!vk_buffer_collection_);
-
- // Set nominal size of 1x1, which will be used only for
- // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the allocated
- // buffers is determined by constraints set by other sysmem clients for the
- // same collection. Size of the Vulkan image is determined by the valus passed
- // to CreateVkImage().
- min_size_ = gfx::Size(1, 1);
-
- vk_device_ = vk_device;
- format_ = format;
- usage_ = usage;
- is_protected_ = force_protected;
-
- fuchsia::sysmem::BufferCollectionTokenSyncPtr token;
- token.Bind(std::move(token_handle));
-
- return InitializeInternal(allocator, std::move(token),
- /*buffers_for_camping=*/0);
+ min_buffer_count);
}
scoped_refptr<gfx::NativePixmap> SysmemBufferCollection::CreateNativePixmap(
diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h
index 34628685327..38f9e12e1bf 100644
--- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h
+++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_collection.h
@@ -39,18 +39,18 @@ class SysmemBufferCollection
SysmemBufferCollection();
explicit SysmemBufferCollection(gfx::SysmemBufferCollectionId id);
+ // Initializes the buffer collection and registers it with Vulkan using the
+ // specified |vk_device|. If |token_handle| is null then a new collection
+ // collection is created. |size| may be empty. In that case |token_handle|
+ // must not be null and the image size is determined by the other sysmem
+ // participants.
bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator,
+ zx::channel token_handle,
gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
VkDevice vk_device,
- size_t num_buffers);
-
- bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator,
- VkDevice vk_device,
- zx::channel token,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
+ size_t min_buffer_count,
bool force_protected);
// Must not be called more than once.
diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
index b1bdfb23b55..8138ded3e55 100644
--- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
+++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
@@ -37,10 +37,11 @@ scoped_refptr<SysmemBufferCollection> SysmemBufferManager::CreateCollection(
gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
- size_t num_buffers) {
+ size_t min_buffer_count) {
auto result = base::MakeRefCounted<SysmemBufferCollection>();
- if (!result->Initialize(allocator_.get(), size, format, usage, vk_device,
- num_buffers)) {
+ if (!result->Initialize(allocator_.get(), /*token_channel=*/zx::channel(),
+ size, format, usage, vk_device, min_buffer_count,
+ /*force_protected=*/false)) {
return nullptr;
}
RegisterCollection(result.get());
@@ -52,12 +53,15 @@ SysmemBufferManager::ImportSysmemBufferCollection(
VkDevice vk_device,
gfx::SysmemBufferCollectionId id,
zx::channel token,
+ gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
+ size_t min_buffer_count,
bool force_protected) {
auto result = base::MakeRefCounted<SysmemBufferCollection>(id);
- if (!result->Initialize(allocator_.get(), vk_device, std::move(token), format,
- usage, force_protected)) {
+ if (!result->Initialize(allocator_.get(), std::move(token), size, format,
+ usage, vk_device, min_buffer_count,
+ force_protected)) {
return nullptr;
}
RegisterCollection(result.get());
diff --git a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h
index 9bc6fe9bd35..6845d8d8df7 100644
--- a/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h
+++ b/chromium/ui/ozone/platform/scenic/sysmem_buffer_manager.h
@@ -47,8 +47,10 @@ class SysmemBufferManager {
VkDevice vk_device,
gfx::SysmemBufferCollectionId id,
zx::channel token,
+ gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
+ size_t min_buffer_count,
bool force_protected);
scoped_refptr<SysmemBufferCollection> GetCollectionById(
diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
index 9152a959c73..6f3d23999e5 100644
--- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
+++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -269,7 +269,7 @@ VulkanImplementationScenic::CreateImageFromGpuMemoryHandle(
auto image = gpu::VulkanImage::Create(
device_queue, vk_image, vk_device_memory, size, vk_image_info.format,
vk_image_info.tiling, vk_device_size, 0 /* memory_type_index */,
- ycbcr_info, vk_image_info.flags);
+ ycbcr_info, vk_image_info.usage, vk_image_info.flags);
if (image->format() != vk_format) {
DLOG(ERROR) << "Unexpected format " << vk_format << " vs "
@@ -300,14 +300,21 @@ VulkanImplementationScenic::RegisterSysmemBufferCollection(
gfx::SysmemBufferCollectionId id,
zx::channel token,
gfx::BufferFormat format,
- gfx::BufferUsage usage) {
+ gfx::BufferUsage usage,
+ gfx::Size size,
+ size_t min_buffer_count) {
// SCANOUT images must be protected in protected mode.
bool force_protected =
usage == gfx::BufferUsage::SCANOUT && enforce_protected_memory();
+ auto buffer_collection = sysmem_buffer_manager_->ImportSysmemBufferCollection(
+ device, id, std::move(token), size, format, usage, min_buffer_count,
+ force_protected);
+ if (!buffer_collection)
+ return nullptr;
+
return std::make_unique<SysmemBufferCollectionImpl>(
- sysmem_buffer_manager_->ImportSysmemBufferCollection(
- device, id, std::move(token), format, usage, force_protected));
+ std::move(buffer_collection));
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
index cbc7e0d111b..d92869e733d 100644
--- a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
+++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
@@ -58,7 +58,9 @@ class VulkanImplementationScenic : public gpu::VulkanImplementation {
gfx::SysmemBufferCollectionId id,
zx::channel token,
gfx::BufferFormat format,
- gfx::BufferUsage usage) override;
+ gfx::BufferUsage usage,
+ gfx::Size size,
+ size_t min_buffer_count) override;
private:
ScenicSurfaceFactory* const scenic_surface_factory_;
diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn
index e0d30f2575c..6b87d5ca7c6 100644
--- a/chromium/ui/ozone/platform/wayland/BUILD.gn
+++ b/chromium/ui/ozone/platform/wayland/BUILD.gn
@@ -10,10 +10,6 @@ import("//gpu/vulkan/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//ui/ozone/platform/wayland/wayland.gni")
-pkg_config("wayland-egl") {
- packages = [ "wayland-egl" ]
-}
-
source_set("wayland") {
sources = [
"client_native_pixmap_factory_wayland.cc",
@@ -34,6 +30,10 @@ source_set("wayland") {
"gpu/wayland_buffer_manager_gpu.h",
"gpu/wayland_canvas_surface.cc",
"gpu/wayland_canvas_surface.h",
+ "gpu/wayland_overlay_candidates.cc",
+ "gpu/wayland_overlay_candidates.h",
+ "gpu/wayland_overlay_manager.cc",
+ "gpu/wayland_overlay_manager.h",
"gpu/wayland_surface_factory.cc",
"gpu/wayland_surface_factory.h",
"gpu/wayland_surface_gpu.h",
@@ -49,6 +49,8 @@ source_set("wayland") {
"host/shell_popup_wrapper.h",
"host/shell_surface_wrapper.cc",
"host/shell_surface_wrapper.h",
+ "host/wayland_auxiliary_window.cc",
+ "host/wayland_auxiliary_window.h",
"host/wayland_buffer_manager_connector.cc",
"host/wayland_buffer_manager_connector.h",
"host/wayland_buffer_manager_host.cc",
@@ -138,11 +140,13 @@ source_set("wayland") {
deps = [
"//base",
+ "//build:lacros_buildflags",
"//build/config/linux/libdrm",
+ "//components/exo/wayland/protocol:aura_shell_protocol",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
"//skia",
- "//third_party/wayland:wayland_client",
+ "//third_party/wayland:wayland_egl",
"//third_party/wayland-protocols:gtk_primary_selection_protocol",
"//third_party/wayland-protocols:keyboard_extension_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
@@ -170,9 +174,21 @@ source_set("wayland") {
"//ui/ozone/common",
"//ui/ozone/public/mojom/wayland:wayland_mojom",
"//ui/platform_window",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
]
+ # TODO(msisov): we should do the following once support for Trusty and Jessie
+ # are dropped:
+ # 1. Remove libs=["wayland-client"] and
+ # add deps+=["//third_party/wayland:wayland_client"]
+ # 2. Set use_system_libwayland=true for is_desktop_linux.
+ libs = []
+ if (!is_chromeos) {
+ libs = [ "wayland-client" ]
+ } else {
+ deps += [ "//third_party/wayland:wayland_client" ]
+ }
+
if (is_linux && !is_chromeos) {
deps += [ "//ui/base/ime/linux" ]
}
@@ -220,17 +236,13 @@ source_set("wayland") {
deps += [ "//gpu/vulkan" ]
}
- configs += [
- ":wayland-egl",
- "//third_party/khronos:khronos_headers",
- ]
+ configs += [ "//third_party/khronos:khronos_headers" ]
}
source_set("test_support") {
testonly = true
sources = [
- "test/constants.h",
"test/global_object.cc",
"test/global_object.h",
"test/mock_buffer.cc",
@@ -309,10 +321,8 @@ source_set("test_support") {
source_set("wayland_unittests") {
testonly = true
- assert(use_wayland_gbm)
-
sources = [
- "gpu/wayland_surface_factory_unittest.cc",
+ "gpu/wayland_overlay_manager_unittest.cc",
"host/wayland_connection_unittest.cc",
"host/wayland_data_device_unittest.cc",
"host/wayland_data_drag_controller_unittest.cc",
@@ -327,13 +337,11 @@ source_set("wayland_unittests") {
"host/wayland_window_unittest.cc",
"test/wayland_test.cc",
"test/wayland_test.h",
- "wayland_buffer_manager_unittest.cc",
]
deps = [
":test_support",
":wayland",
- "//build/config/linux/libdrm",
"//testing/gmock",
"//testing/gtest",
"//third_party/wayland:wayland_server",
@@ -346,12 +354,10 @@ source_set("wayland_unittests") {
"//ui/base/cursor",
"//ui/base/ime/linux",
"//ui/events/ozone/layout",
- "//ui/gfx/linux:drm",
- "//ui/gfx/linux:gbm",
"//ui/gfx/linux:test_support",
"//ui/ozone:platform",
"//ui/ozone:test_support",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
]
import("//ui/base/ui_features.gni")
@@ -359,10 +365,20 @@ source_set("wayland_unittests") {
deps += [ "//ui/events/keycodes:xkb" ]
}
- defines = [
- "WL_HIDE_DEPRECATED",
- "WAYLAND_GBM",
- ]
+ defines = [ "WL_HIDE_DEPRECATED" ]
+
+ if (use_wayland_gbm) {
+ sources += [
+ "gpu/wayland_surface_factory_unittest.cc",
+ "wayland_buffer_manager_unittest.cc",
+ ]
+ deps += [
+ "//build/config/linux/libdrm",
+ "//ui/gfx/linux:drm",
+ "//ui/gfx/linux:gbm",
+ ]
+ defines += [ "WAYLAND_GBM" ]
+ }
}
fuzzer_test("wayland_buffer_fuzzer") {
diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.cc b/chromium/ui/ozone/platform/wayland/common/data_util.cc
index 55e594a07e3..90773668cde 100644
--- a/chromium/ui/ozone/platform/wayland/common/data_util.cc
+++ b/chromium/ui/ozone/platform/wayland/common/data_util.cc
@@ -46,34 +46,32 @@ int MimeTypeToFormat(const std::string& mime_type) {
// Converts raw data to either narrow or wide string.
template <typename StringType>
-StringType BytesTo(const PlatformClipboard::Data& bytes) {
- if (bytes.size() % sizeof(typename StringType::value_type) != 0U) {
+StringType BytesTo(PlatformClipboard::Data bytes) {
+ using ValueType = typename StringType::value_type;
+ if (bytes->size() % sizeof(ValueType) != 0U) {
// This is suspicious.
LOG(WARNING)
<< "Data is possibly truncated, or a wrong conversion is requested.";
}
- StringType result;
- result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]),
- bytes.size() / sizeof(typename StringType::value_type));
+ StringType result(bytes->front_as<ValueType>(),
+ bytes->size() / sizeof(ValueType));
return result;
}
-void AddString(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
+void AddString(PlatformClipboard::Data data, OSExchangeData* os_exchange_data) {
DCHECK(os_exchange_data);
- if (data.empty())
+ if (data->data().empty())
return;
os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data)));
}
-void AddHtml(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
+void AddHtml(PlatformClipboard::Data data, OSExchangeData* os_exchange_data) {
DCHECK(os_exchange_data);
- if (data.empty())
+ if (data->data().empty())
return;
os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)),
@@ -85,8 +83,7 @@ void AddHtml(const PlatformClipboard::Data& data,
// 2. Non-comment lines shall be URIs (URNs or URLs).
// 3. Lines are terminated with a CRLF pair.
// 4. URL encoding is used.
-void AddFiles(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
+void AddFiles(PlatformClipboard::Data data, OSExchangeData* os_exchange_data) {
DCHECK(os_exchange_data);
std::string data_as_string = BytesTo<std::string>(data);
@@ -124,11 +121,10 @@ void AddFiles(const PlatformClipboard::Data& data,
// two lines separated with newline, where the first line is the URL and
// the second one is page title. The unpleasant feature of text/x-moz-url is
// that the URL has UTF-16 encoding.
-void AddUrl(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
+void AddUrl(PlatformClipboard::Data data, OSExchangeData* os_exchange_data) {
DCHECK(os_exchange_data);
- if (data.empty())
+ if (data->data().empty())
return;
base::string16 data_as_string16 = BytesTo<base::string16>(data);
@@ -163,9 +159,10 @@ bool ContainsMimeType(const OSExchangeData& exchange_data,
return exchange_data.HasAnyFormat(MimeTypeToFormat(mime_type), {});
}
-void AddToOSExchangeData(const PlatformClipboard::Data& data,
+void AddToOSExchangeData(PlatformClipboard::Data data,
const std::string& mime_type,
OSExchangeData* exchange_data) {
+ DCHECK(data);
DCHECK(IsMimeTypeSupported(mime_type));
DCHECK(exchange_data);
int format = MimeTypeToFormat(mime_type);
diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.h b/chromium/ui/ozone/platform/wayland/common/data_util.h
index d6c2d10e7f5..39c03080d76 100644
--- a/chromium/ui/ozone/platform/wayland/common/data_util.h
+++ b/chromium/ui/ozone/platform/wayland/common/data_util.h
@@ -24,7 +24,7 @@ bool ContainsMimeType(const ui::OSExchangeData& exchange_data,
// Add clipboard |data| content with |mime_type| format to the |exchange_data|.
// |mime_type| is assumed to be supported (See IsMimeTypeSupported for more).
-void AddToOSExchangeData(const ui::PlatformClipboard::Data& data,
+void AddToOSExchangeData(ui::PlatformClipboard::Data data,
const std::string& mime_type,
ui::OSExchangeData* exchange_data);
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
index 9f9efeae72e..a71f25d42b7 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include <aura-shell-client-protocol.h>
#include <gtk-primary-selection-client-protocol.h>
#include <keyboard-extension-unstable-v1-client-protocol.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
@@ -190,6 +191,15 @@ const wl_interface* ObjectTraits<xdg_positioner>::interface =
void (*ObjectTraits<xdg_positioner>::deleter)(xdg_positioner*) =
&xdg_positioner_destroy;
+const wl_interface* ObjectTraits<zaura_shell>::interface =
+ &zaura_shell_interface;
+void (*ObjectTraits<zaura_shell>::deleter)(zaura_shell*) = &zaura_shell_destroy;
+
+const wl_interface* ObjectTraits<zaura_surface>::interface =
+ &zaura_surface_interface;
+void (*ObjectTraits<zaura_surface>::deleter)(zaura_surface*) =
+ &zaura_surface_destroy;
+
const wl_interface* ObjectTraits<zcr_keyboard_extension_v1>::interface =
&zcr_keyboard_extension_v1_interface;
void (*ObjectTraits<zcr_keyboard_extension_v1>::deleter)(
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.h b/chromium/ui/ozone/platform/wayland/common/wayland_object.h
index 5c639f2db81..faabaed2de3 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.h
@@ -39,6 +39,8 @@ struct xdg_surface;
struct xdg_toplevel;
struct xdg_popup;
struct xdg_positioner;
+struct zaura_shell;
+struct zaura_surface;
struct zcr_keyboard_extension_v1;
struct zcr_extended_keyboard_v1;
struct zwp_linux_dmabuf_v1;
@@ -248,6 +250,18 @@ struct ObjectTraits<xdg_positioner> {
};
template <>
+struct ObjectTraits<zaura_shell> {
+ static const wl_interface* interface;
+ static void (*deleter)(zaura_shell*);
+};
+
+template <>
+struct ObjectTraits<zaura_surface> {
+ static const wl_interface* interface;
+ static void (*deleter)(zaura_surface*);
+};
+
+template <>
struct ObjectTraits<zcr_keyboard_extension_v1> {
static const wl_interface* interface;
static void (*deleter)(zcr_keyboard_extension_v1*);
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc
index 14593b1c614..3b0b8829a95 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_util.cc
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.cc
@@ -10,6 +10,8 @@
#include "ui/base/hit_test.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace wl {
@@ -160,4 +162,26 @@ bool IsMenuType(ui::PlatformWindowType type) {
type == ui::PlatformWindowType::kPopup;
}
+ui::WaylandWindow* RootWindowFromWlSurface(wl_surface* surface) {
+ if (!surface)
+ return nullptr;
+ auto* wayland_surface = static_cast<ui::WaylandSurface*>(
+ wl_proxy_get_user_data(reinterpret_cast<wl_proxy*>(surface)));
+ if (!wayland_surface)
+ return nullptr;
+ return wayland_surface->root_window();
+}
+
+gfx::Rect TranslateWindowBoundsToParentDIP(ui::WaylandWindow* window,
+ ui::WaylandWindow* parent_window) {
+ DCHECK(window);
+ DCHECK(parent_window);
+ DCHECK_EQ(window->buffer_scale(), parent_window->buffer_scale());
+ DCHECK_EQ(window->ui_scale(), parent_window->ui_scale());
+ return gfx::ScaleToRoundedRect(
+ wl::TranslateBoundsToParentCoordinates(window->GetBounds(),
+ parent_window->GetBounds()),
+ 1.0 / window->buffer_scale());
+}
+
} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_util.h b/chromium/ui/ozone/platform/wayland/common/wayland_util.h
index 3db36965402..582a3c7b0ba 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_util.h
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_util.h
@@ -22,6 +22,7 @@ class SkBitmap;
namespace ui {
class WaylandConnection;
class WaylandShmBuffer;
+class WaylandWindow;
} // namespace ui
namespace gfx {
@@ -62,6 +63,16 @@ gfx::Rect TranslateBoundsToTopLevelCoordinates(const gfx::Rect& child_bounds,
// Says if the type is kPopup or kMenu.
bool IsMenuType(ui::PlatformWindowType type);
+// Returns the root WaylandWindow for the given wl_surface.
+ui::WaylandWindow* RootWindowFromWlSurface(wl_surface* surface);
+
+// Returns bounds of the given window, adjusted to its subsurface. We need to
+// adjust bounds because WaylandWindow::GetBounds() returns absolute bounds in
+// pixels, but wl_subsurface works with bounds relative to the parent surface
+// and in DIP.
+gfx::Rect TranslateWindowBoundsToParentDIP(ui::WaylandWindow* window,
+ ui::WaylandWindow* parent_window);
+
} // namespace wl
#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
index 734e8b2b699..8ee69e5b401 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -13,6 +13,7 @@
#include "ui/gfx/gpu_fence.h"
#include "ui/ozone/common/egl_util.h"
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
+#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h"
namespace ui {
@@ -114,7 +115,7 @@ void GbmSurfacelessWayland::SwapBuffersAsync(
if (!use_egl_fence_sync_ || !frame->schedule_planes_succeeded) {
frame->ready = true;
- SubmitFrame();
+ MaybeSubmitFrames();
return;
}
@@ -216,33 +217,40 @@ void GbmSurfacelessWayland::PendingFrame::Flush() {
overlay.Flush();
}
-void GbmSurfacelessWayland::SubmitFrame() {
- DCHECK(!unsubmitted_frames_.empty());
-
- if (unsubmitted_frames_.front()->ready && !submitted_frame_) {
- submitted_frame_ = std::move(unsubmitted_frames_.front());
+void GbmSurfacelessWayland::MaybeSubmitFrames() {
+ while (!unsubmitted_frames_.empty() && unsubmitted_frames_.front()->ready) {
+ auto submitted_frame = std::move(unsubmitted_frames_.front());
unsubmitted_frames_.erase(unsubmitted_frames_.begin());
- if (!submitted_frame_->schedule_planes_succeeded) {
+ if (!submitted_frame->schedule_planes_succeeded) {
last_swap_buffers_result_ = false;
- std::move(submitted_frame_->completion_callback)
+ std::move(submitted_frame->completion_callback)
.Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
// Notify the caller, the buffer is never presented on a screen.
- std::move(submitted_frame_->presentation_callback)
+ std::move(submitted_frame->presentation_callback)
.Run(gfx::PresentationFeedback::Failure());
- submitted_frame_.reset();
+ submitted_frame.reset();
return;
}
- DCHECK_EQ(submitted_frame_->planes.size(), 1u);
- submitted_frame_->buffer_id = submitted_frame_->planes.back().buffer_id;
- buffer_manager_->CommitBuffer(widget_,
- submitted_frame_->planes.back().buffer_id,
- submitted_frame_->damage_region_);
+ std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlay_configs;
+ for (const auto& plane : submitted_frame->planes) {
+ overlay_configs.push_back(
+ ui::ozone::mojom::WaylandOverlayConfig::From(plane.plane));
+ overlay_configs.back()->buffer_id = plane.buffer_id;
+ if (plane.plane.z_order == 0) {
+ overlay_configs.back()->damage_region = submitted_frame->damage_region_;
+ submitted_frame->buffer_id = plane.buffer_id;
+ }
+ }
+ buffer_manager_->CommitOverlays(widget_, std::move(overlay_configs));
- submitted_frame_->planes.clear();
+ submitted_frame->unacked_submissions = submitted_frame->planes.size();
+ submitted_frame->unacked_presentations = submitted_frame->planes.size();
+ submitted_frame->planes.clear();
+ submitted_frames_.push_back(std::move(submitted_frame));
}
}
@@ -251,12 +259,12 @@ EGLSyncKHR GbmSurfacelessWayland::InsertFence(bool implicit) {
EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM,
EGL_NONE};
return eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR,
- implicit ? attrib_list : NULL);
+ implicit ? attrib_list : nullptr);
}
void GbmSurfacelessWayland::FenceRetired(PendingFrame* frame) {
frame->ready = true;
- SubmitFrame();
+ MaybeSubmitFrames();
}
void GbmSurfacelessWayland::SetNoGLFlushForTests() {
@@ -265,28 +273,61 @@ void GbmSurfacelessWayland::SetNoGLFlushForTests() {
void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id,
const gfx::SwapResult& swap_result) {
- submitted_frame_->overlays.clear();
+ // submitted_frames_ may temporarily have more than one buffer in it if
+ // buffers are released out of order by the Wayland server.
+ DCHECK(!submitted_frames_.empty());
+ if (--submitted_frames_.front()->unacked_submissions)
+ return;
- DCHECK_EQ(submitted_frame_->buffer_id, buffer_id);
- std::move(submitted_frame_->completion_callback)
+ auto submitted_frame = std::move(submitted_frames_.front());
+ submitted_frames_.erase(submitted_frames_.begin());
+ submitted_frame->overlays.clear();
+
+ std::move(submitted_frame->completion_callback)
.Run(gfx::SwapCompletionResult(swap_result));
- pending_presentation_frames_.push_back(std::move(submitted_frame_));
+ pending_presentation_frames_.push_back(std::move(submitted_frame));
if (swap_result != gfx::SwapResult::SWAP_ACK) {
last_swap_buffers_result_ = false;
return;
}
- SubmitFrame();
+ MaybeSubmitFrames();
}
void GbmSurfacelessWayland::OnPresentation(
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) {
- DCHECK(!pending_presentation_frames_.empty());
+ // Items in |submitted_frames_| will not be moved to
+ // |pending_presentation_frames_| until |unacked_submissions| decrements to 0.
+ // Example:
+ // A SwapBuffers that submitted 2 buffers (buffer_1 and buffer_2) will push
+ // a submitted_frame expecting 2 submission feedbacks and 2 presentation
+ // feedbacks.
+ // If IPCs comes in the order of:
+ // buffer_1:submission > buffer_2:submission > buffer_1:presentation >
+ // buffer_2:presentation
+ // We are fine without below logic. However, this can happen:
+ // buffer_1:submission > buffer_1:presentation > buffer_2:submission >
+ // buffer_2:presentation
+ // In this case, we have to find the item in |submitted_frames_| and
+ // decrement |unacked_presentations| there.
+ // TODO(fangzhoug): This solution is sub-optimal and confusing. It increases
+ // the number of IPCs from browser to gpu. The barrier logic should be in the
+ // browser process.
+ if (pending_presentation_frames_.empty()) {
+ auto it = submitted_frames_.begin();
+ for (; !(*it)->unacked_presentations; ++it)
+ ;
+ --(*it)->unacked_presentations;
+ return;
+ }
+
auto* frame = pending_presentation_frames_.front().get();
- DCHECK_EQ(frame->buffer_id, buffer_id);
+ if (--frame->unacked_presentations)
+ return;
+
std::move(frame->presentation_callback).Run(feedback);
pending_presentation_frames_.erase(pending_presentation_frames_.begin());
}
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
index 137bea20432..13dbd84ab90 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
+++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -101,9 +101,15 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
bool schedule_planes_succeeded = false;
std::vector<PlaneData> planes;
+
+ // TODO(fangzhoug): This is a temporary solution to barrier swap/present
+ // acks of a frame that contains multiple buffer commits. Next step is to
+ // barrier in browser process to avoid extra IPC hops.
+ size_t unacked_submissions;
+ size_t unacked_presentations;
};
- void SubmitFrame();
+ void MaybeSubmitFrames();
EGLSyncKHR InsertFence(bool implicit);
void FenceRetired(PendingFrame* frame);
@@ -116,8 +122,8 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
// The native surface. Deleting this is allowed to free the EGLNativeWindow.
gfx::AcceleratedWidget widget_;
std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_;
+ std::vector<std::unique_ptr<PendingFrame>> submitted_frames_;
std::vector<std::unique_ptr<PendingFrame>> pending_presentation_frames_;
- std::unique_ptr<PendingFrame> submitted_frame_;
bool has_implicit_external_sync_;
bool last_swap_buffers_result_ = true;
bool use_egl_fence_sync_ = true;
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc
index 02ec5b8088e..e5cdaf12895 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_wayland.cc
@@ -21,8 +21,8 @@ void EGLWindowDeleter::operator()(wl_egl_window* egl_window) {
std::unique_ptr<wl_egl_window, EGLWindowDeleter> CreateWaylandEglWindow(
WaylandWindow* window) {
gfx::Size size = window->GetBounds().size();
- return std::unique_ptr<wl_egl_window, EGLWindowDeleter>(
- wl_egl_window_create(window->surface(), size.width(), size.height()));
+ return std::unique_ptr<wl_egl_window, EGLWindowDeleter>(wl_egl_window_create(
+ window->root_surface()->surface(), size.width(), size.height()));
}
GLSurfaceWayland::GLSurfaceWayland(WaylandEglWindowPtr egl_window)
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index 20e8736b057..42fd8192f00 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -7,10 +7,34 @@
#include <utility>
#include "base/bind.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/process/process.h"
+#include "base/task/current_thread.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
+#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h"
+#include "ui/ozone/public/overlay_plane.h"
+
+namespace mojo {
+// static
+ui::ozone::mojom::WaylandOverlayConfigPtr
+TypeConverter<ui::ozone::mojom::WaylandOverlayConfigPtr,
+ ui::OverlayPlane>::Convert(const ui::OverlayPlane& input) {
+ ui::ozone::mojom::WaylandOverlayConfigPtr wayland_overlay_config{
+ ui::ozone::mojom::WaylandOverlayConfig::New()};
+ wayland_overlay_config->z_order = input.z_order;
+ wayland_overlay_config->transform = input.plane_transform;
+ wayland_overlay_config->bounds_rect = input.display_bounds;
+ wayland_overlay_config->crop_rect = input.crop_rect;
+ wayland_overlay_config->enable_blend = input.enable_blend;
+ wayland_overlay_config->access_fence_handle =
+ !input.gpu_fence || input.gpu_fence->GetGpuFenceHandle().is_null()
+ ? base::Optional<gfx::GpuFenceHandle>()
+ : base::Optional<gfx::GpuFenceHandle>(
+ gfx::CloneHandleForIPC(input.gpu_fence->GetGpuFenceHandle()));
+
+ return wayland_overlay_config;
+}
+} // namespace mojo
namespace ui {
@@ -38,9 +62,11 @@ void WaylandBufferManagerGpu::Initialize(
void WaylandBufferManagerGpu::OnSubmission(gfx::AcceleratedWidget widget,
uint32_t buffer_id,
gfx::SwapResult swap_result) {
+ base::AutoLock scoped_lock(lock_);
DCHECK(io_thread_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(commit_thread_runners_.count(widget), 1u);
// Return back to the same thread where the commit request came from.
- commit_thread_runner_->PostTask(
+ commit_thread_runners_.find(widget)->second->PostTask(
FROM_HERE,
base::BindOnce(&WaylandBufferManagerGpu::SubmitSwapResultOnOriginThread,
base::Unretained(this), widget, buffer_id, swap_result));
@@ -50,22 +76,47 @@ void WaylandBufferManagerGpu::OnPresentation(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) {
+ base::AutoLock scoped_lock(lock_);
DCHECK(io_thread_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(commit_thread_runners_.count(widget), 1u);
// Return back to the same thread where the commit request came from.
- commit_thread_runner_->PostTask(
+ commit_thread_runners_.find(widget)->second->PostTask(
FROM_HERE,
- base::BindOnce(
- &WaylandBufferManagerGpu::SubmitPresentationtOnOriginThread,
- base::Unretained(this), widget, buffer_id, feedback));
+ base::BindOnce(&WaylandBufferManagerGpu::SubmitPresentationOnOriginThread,
+ base::Unretained(this), widget, buffer_id, feedback));
}
void WaylandBufferManagerGpu::RegisterSurface(gfx::AcceleratedWidget widget,
WaylandSurfaceGpu* surface) {
+ if (!io_thread_runner_) {
+ LOG(ERROR) << "WaylandBufferManagerGpu is not initialized. Can't register "
+ "a surface.";
+ return;
+ }
+
+ io_thread_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &WaylandBufferManagerGpu::SaveTaskRunnerForWidgetOnIOThread,
+ base::Unretained(this), widget, base::ThreadTaskRunnerHandle::Get()));
+
base::AutoLock scoped_lock(lock_);
widget_to_surface_map_.emplace(widget, surface);
}
void WaylandBufferManagerGpu::UnregisterSurface(gfx::AcceleratedWidget widget) {
+ if (!io_thread_runner_) {
+ LOG(ERROR) << "WaylandBufferManagerGpu is not initialized. Can't register "
+ "a surface.";
+ return;
+ }
+
+ io_thread_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &WaylandBufferManagerGpu::ForgetTaskRunnerForWidgetOnIOThread,
+ base::Unretained(this), widget));
+
base::AutoLock scoped_lock(lock_);
widget_to_surface_map_.erase(widget);
}
@@ -133,9 +184,6 @@ void WaylandBufferManagerGpu::CommitBuffer(gfx::AcceleratedWidget widget,
return;
}
- if (!commit_thread_runner_)
- commit_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
-
// Do the mojo call on the IO child thread.
io_thread_runner_->PostTask(
FROM_HERE,
@@ -143,6 +191,22 @@ void WaylandBufferManagerGpu::CommitBuffer(gfx::AcceleratedWidget widget,
base::Unretained(this), widget, buffer_id, damage_region));
}
+void WaylandBufferManagerGpu::CommitOverlays(
+ gfx::AcceleratedWidget widget,
+ std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays) {
+ if (!remote_host_) {
+ LOG(ERROR) << "Interface is not bound. Can't request "
+ "WaylandBufferManagerHost to create/commit/destroy buffers.";
+ return;
+ }
+
+ // Do the mojo call on the IO child thread.
+ io_thread_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaylandBufferManagerGpu::CommitOverlaysInternal,
+ base::Unretained(this), widget, std::move(overlays)));
+}
+
void WaylandBufferManagerGpu::DestroyBuffer(gfx::AcceleratedWidget widget,
uint32_t buffer_id) {
if (!remote_host_) {
@@ -210,6 +274,13 @@ void WaylandBufferManagerGpu::CommitBufferInternal(
remote_host_->CommitBuffer(widget, buffer_id, damage_region);
}
+void WaylandBufferManagerGpu::CommitOverlaysInternal(
+ gfx::AcceleratedWidget widget,
+ std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays) {
+ DCHECK(io_thread_runner_->BelongsToCurrentThread());
+ remote_host_->CommitOverlays(widget, std::move(overlays));
+}
+
void WaylandBufferManagerGpu::DestroyBufferInternal(
gfx::AcceleratedWidget widget,
uint32_t buffer_id) {
@@ -229,11 +300,25 @@ void WaylandBufferManagerGpu::BindHostInterface(
remote_host_->SetWaylandBufferManagerGpu(std::move(client_remote));
}
+void WaylandBufferManagerGpu::SaveTaskRunnerForWidgetOnIOThread(
+ gfx::AcceleratedWidget widget,
+ scoped_refptr<base::SingleThreadTaskRunner> origin_runner) {
+ DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
+ DCHECK(io_thread_runner_->BelongsToCurrentThread());
+ commit_thread_runners_.emplace(widget, origin_runner);
+}
+
+void WaylandBufferManagerGpu::ForgetTaskRunnerForWidgetOnIOThread(
+ gfx::AcceleratedWidget widget) {
+ DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
+ DCHECK(io_thread_runner_->BelongsToCurrentThread());
+ commit_thread_runners_.erase(widget);
+}
+
void WaylandBufferManagerGpu::SubmitSwapResultOnOriginThread(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
gfx::SwapResult swap_result) {
- DCHECK(commit_thread_runner_->BelongsToCurrentThread());
DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
auto* surface = GetSurface(widget);
// The surface might be destroyed by the time the swap result is provided.
@@ -241,11 +326,10 @@ void WaylandBufferManagerGpu::SubmitSwapResultOnOriginThread(
surface->OnSubmission(buffer_id, swap_result);
}
-void WaylandBufferManagerGpu::SubmitPresentationtOnOriginThread(
+void WaylandBufferManagerGpu::SubmitPresentationOnOriginThread(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) {
- DCHECK(commit_thread_runner_->BelongsToCurrentThread());
DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
auto* surface = GetSurface(widget);
// The surface might be destroyed by the time the presentation feedback is
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
index 819d64a9834..1f451202652 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -5,6 +5,7 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_BUFFER_MANAGER_GPU_H_
#define UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_BUFFER_MANAGER_GPU_H_
+#include <map>
#include <memory>
#include "base/macros.h"
@@ -14,6 +15,7 @@
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom.h"
@@ -32,6 +34,7 @@ namespace ui {
class WaylandConnection;
class WaylandSurfaceGpu;
class WaylandWindow;
+struct OverlayPlane;
// Forwards calls through an associated mojo connection to WaylandBufferManager
// on the browser process side.
@@ -104,6 +107,12 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu {
uint32_t buffer_id,
const gfx::Rect& damage_region);
+ // Send overlay configurations for a frame to a WaylandWindow identified by
+ // |widget|.
+ void CommitOverlays(
+ gfx::AcceleratedWidget widget,
+ std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays);
+
// Asks Wayland to destroy a wl_buffer.
void DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id);
@@ -142,17 +151,25 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu {
void CommitBufferInternal(gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::Rect& damage_region);
+ void CommitOverlaysInternal(
+ gfx::AcceleratedWidget widget,
+ std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays);
void DestroyBufferInternal(gfx::AcceleratedWidget widget, uint32_t buffer_id);
void BindHostInterface(
mojo::PendingRemote<ozone::mojom::WaylandBufferManagerHost> remote_host);
+ void SaveTaskRunnerForWidgetOnIOThread(
+ gfx::AcceleratedWidget widget,
+ scoped_refptr<base::SingleThreadTaskRunner> origin_runner);
+ void ForgetTaskRunnerForWidgetOnIOThread(gfx::AcceleratedWidget widget);
+
// Provides the WaylandSurfaceGpu, which backs the |widget|, with swap and
// presentation results.
void SubmitSwapResultOnOriginThread(gfx::AcceleratedWidget widget,
uint32_t buffer_id,
gfx::SwapResult swap_result);
- void SubmitPresentationtOnOriginThread(
+ void SubmitPresentationOnOriginThread(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback);
@@ -180,20 +197,24 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu {
base::flat_map<gfx::BufferFormat, std::vector<uint64_t>>
supported_buffer_formats_with_modifiers_;
- // This task runner can be used to pass messages back to the same thread,
- // where the commit buffer request came from. For example, swap requests come
- // from the GpuMainThread, but rerouted to the IOChildThread and then mojo
- // calls happen. However, when the manager receives mojo calls, it has to
+ // These task runners can be used to pass messages back to the same thread,
+ // where the commit buffer request came from. For example, swap requests can
+ // come from the GpuMainThread, but are rerouted to the IOChildThread and then
+ // mojo calls happen. However, when the manager receives mojo calls, it has to
// reroute calls back to the same thread where the calls came from to ensure
- // correct sequence.
- scoped_refptr<base::SingleThreadTaskRunner> commit_thread_runner_;
+ // correct sequence. Note that not all calls come from the GpuMainThread, e.g.
+ // WaylandCanvasSurface calls from the VizCompositorThread.
+ // This map must only be accessed from the IO thread.
+ base::small_map<std::map<gfx::AcceleratedWidget,
+ scoped_refptr<base::SingleThreadTaskRunner>>>
+ commit_thread_runners_;
// A task runner, which is initialized in a multi-process mode. It is used to
// ensure all the methods of this class are run on IOChildThread. This is
// needed to ensure mojo calls happen on a right sequence.
scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner_;
- // Protects access to |widget_to_surface_map_|.
+ // Protects access to |widget_to_surface_map_| and |commit_thread_runners_|.
base::Lock lock_;
// Keeps track of the next unique buffer ID.
@@ -204,4 +225,15 @@ class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu {
} // namespace ui
+// This is a specialization of mojo::TypeConverter and has to be in the mojo
+// namespace.
+namespace mojo {
+template <>
+struct TypeConverter<ui::ozone::mojom::WaylandOverlayConfigPtr,
+ ui::OverlayPlane> {
+ static ui::ozone::mojom::WaylandOverlayConfigPtr Convert(
+ const ui::OverlayPlane& input);
+};
+} // namespace mojo
+
#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_BUFFER_MANAGER_GPU_H_
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.cc
new file mode 100644
index 00000000000..1163133e4a9
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.cc
@@ -0,0 +1,24 @@
+// 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 "ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h"
+
+#include "ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace ui {
+
+WaylandOverlayCandidates::WaylandOverlayCandidates(
+ WaylandOverlayManager* manager,
+ gfx::AcceleratedWidget widget)
+ : overlay_manager_(manager), widget_(widget) {}
+
+WaylandOverlayCandidates::~WaylandOverlayCandidates() = default;
+
+void WaylandOverlayCandidates::CheckOverlaySupport(
+ std::vector<OverlaySurfaceCandidate>* candidates) {
+ overlay_manager_->CheckOverlaySupport(candidates, widget_);
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h
new file mode 100644
index 00000000000..37a66b636e5
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h
@@ -0,0 +1,39 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_CANDIDATES_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_CANDIDATES_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+
+namespace ui {
+class WaylandOverlayManager;
+class OverlaySurfaceCandidate;
+
+// OverlayCandidatesOzone implementation that delegates overlay decision to
+// WaylandOverlayManager.
+class WaylandOverlayCandidates : public OverlayCandidatesOzone {
+ public:
+ WaylandOverlayCandidates(WaylandOverlayManager* manager,
+ gfx::AcceleratedWidget widget);
+ WaylandOverlayCandidates(const WaylandOverlayCandidates&) = delete;
+ WaylandOverlayCandidates& operator=(const WaylandOverlayCandidates&) = delete;
+ ~WaylandOverlayCandidates() override;
+
+ // OverlayCandidatesOzone:
+ void CheckOverlaySupport(
+ std::vector<OverlaySurfaceCandidate>* candidates) override;
+
+ private:
+ WaylandOverlayManager* const overlay_manager_; // Not owned.
+ const gfx::AcceleratedWidget widget_;
+};
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_CANDIDATES_H_
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.cc
new file mode 100644
index 00000000000..089ca8b2cdd
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.cc
@@ -0,0 +1,56 @@
+// 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 "ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h"
+
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_overlay_candidates.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace ui {
+
+WaylandOverlayManager::WaylandOverlayManager() = default;
+WaylandOverlayManager::~WaylandOverlayManager() = default;
+
+std::unique_ptr<OverlayCandidatesOzone>
+WaylandOverlayManager::CreateOverlayCandidates(gfx::AcceleratedWidget widget) {
+ return std::make_unique<WaylandOverlayCandidates>(this, widget);
+}
+
+void WaylandOverlayManager::CheckOverlaySupport(
+ std::vector<OverlaySurfaceCandidate>* candidates,
+ gfx::AcceleratedWidget widget) {
+ for (auto& candidate : *candidates) {
+ bool can_handle = CanHandleCandidate(candidate, widget);
+
+ // CanHandleCandidate() should never return false if the candidate is
+ // the primary plane.
+ DCHECK(can_handle || candidate.plane_z_order != 0);
+
+ candidate.overlay_handled = can_handle;
+ }
+}
+
+bool WaylandOverlayManager::CanHandleCandidate(
+ const OverlaySurfaceCandidate& candidate,
+ gfx::AcceleratedWidget widget) const {
+ if (candidate.buffer_size.IsEmpty())
+ return false;
+
+ if (candidate.transform == gfx::OVERLAY_TRANSFORM_INVALID)
+ return false;
+
+ // Reject candidates that don't fall on a pixel boundary.
+ if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f))
+ return false;
+
+ if (candidate.is_clipped && !candidate.clip_rect.Contains(
+ gfx::ToNearestRect(candidate.display_rect))) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h
new file mode 100644
index 00000000000..5de78b3015f
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h
@@ -0,0 +1,44 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_MANAGER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_MANAGER_H_
+
+#include "base/macros.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+#include "ui/ozone/public/overlay_manager_ozone.h"
+
+namespace ui {
+class OverlaySurfaceCandidate;
+
+// Ozone Wayland extension of the OverlayManagerOzone interface. It verifies the
+// minimum validity of overlay candidates. Candidates' buffers are forwarded to
+// Wayland server as Wayland subsurfaces. Actual HW overlay promotion happens
+// later in Wayland Server.
+class WaylandOverlayManager : public OverlayManagerOzone {
+ public:
+ WaylandOverlayManager();
+ WaylandOverlayManager(const WaylandOverlayManager&) = delete;
+ WaylandOverlayManager& operator=(const WaylandOverlayManager&) = delete;
+ ~WaylandOverlayManager() override;
+
+ // OverlayManagerOzone:
+ std::unique_ptr<OverlayCandidatesOzone> CreateOverlayCandidates(
+ gfx::AcceleratedWidget w) override;
+
+ // Checks if overlay candidates can be displayed as overlays. Modifies
+ // |candidates| to indicate if they can.
+ void CheckOverlaySupport(std::vector<OverlaySurfaceCandidate>* candidates,
+ gfx::AcceleratedWidget widget);
+
+ protected:
+ // Perform basic validation to see if |candidate| is a valid request.
+ bool CanHandleCandidate(const OverlaySurfaceCandidate& candidate,
+ gfx::AcceleratedWidget widget) const;
+};
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_WAYLAND_OVERLAY_MANAGER_H_
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager_unittest.cc
new file mode 100644
index 00000000000..18c605d9f44
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_overlay_manager_unittest.cc
@@ -0,0 +1,77 @@
+// 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 "ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h"
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/public/overlay_surface_candidate.h"
+
+namespace ui {
+namespace {
+
+constexpr gfx::AcceleratedWidget kPrimaryWidget = 1;
+
+OverlaySurfaceCandidate CreateCandidate(const gfx::Rect& rect,
+ int plane_z_order) {
+ ui::OverlaySurfaceCandidate candidate;
+ candidate.transform = gfx::OVERLAY_TRANSFORM_NONE;
+ candidate.format = gfx::BufferFormat::YUV_420_BIPLANAR;
+ candidate.plane_z_order = plane_z_order;
+ candidate.buffer_size = rect.size();
+ candidate.display_rect = gfx::RectF(rect);
+ candidate.crop_rect = gfx::RectF(rect);
+ return candidate;
+}
+
+} // namespace
+
+TEST(WaylandOverlayManagerTest, MultipleOverlayCandidates) {
+ WaylandOverlayManager manager;
+
+ std::vector<OverlaySurfaceCandidate> candidates = {
+ CreateCandidate(gfx::Rect(10, 10, 20, 20), -2),
+ CreateCandidate(gfx::Rect(30, 30, 10, 10), -1),
+ CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
+ CreateCandidate(gfx::Rect(40, 40, 20, 20), 1),
+ CreateCandidate(gfx::Rect(60, 60, 20, 20), 2)};
+
+ // Submit a set of candidates that could potentially be displayed in an
+ // overlay. All candidates should be marked as handled.
+ manager.CheckOverlaySupport(&candidates, kPrimaryWidget);
+ EXPECT_TRUE(candidates[0].overlay_handled);
+ EXPECT_TRUE(candidates[1].overlay_handled);
+ EXPECT_TRUE(candidates[2].overlay_handled);
+ EXPECT_TRUE(candidates[3].overlay_handled);
+ EXPECT_TRUE(candidates[4].overlay_handled);
+}
+
+TEST(WaylandOverlayManagerTest, NonIntegerDisplayRect) {
+ WaylandOverlayManager manager;
+
+ // Candidates for output surface and single-on-top quad.
+ std::vector<OverlaySurfaceCandidate> candidates = {
+ CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
+ CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};
+
+ // Submit a set of candidates that could potentially be displayed in an
+ // overlay.
+ manager.CheckOverlaySupport(&candidates, kPrimaryWidget);
+ EXPECT_TRUE(candidates[0].overlay_handled);
+ EXPECT_TRUE(candidates[1].overlay_handled);
+
+ candidates[0].overlay_handled = false;
+ candidates[1].overlay_handled = false;
+
+ // Modify the display_rect for the second candidate so it's non-integer. We
+ // will never try to promote this to an overlay. This verifies we don't try to
+ // convert the non-integer RectF into a Rect which DCHECKs.
+ candidates[1].display_rect = gfx::RectF(9.4, 10.43, 20.11, 20.99);
+ manager.CheckOverlaySupport(&candidates, kPrimaryWidget);
+ EXPECT_TRUE(candidates[0].overlay_handled);
+ EXPECT_FALSE(candidates[1].overlay_handled);
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
index 76ba72b23eb..0f20a7c0c39 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
@@ -136,9 +136,7 @@ WaylandSurfaceFactory::WaylandSurfaceFactory(
WaylandSurfaceFactory::~WaylandSurfaceFactory() = default;
std::unique_ptr<SurfaceOzoneCanvas>
-WaylandSurfaceFactory::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+WaylandSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget) {
return std::make_unique<WaylandCanvasSurface>(buffer_manager_, widget);
}
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
index 5fb57e84b2f..1de11ed0104 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
@@ -35,8 +35,7 @@ class WaylandSurfaceFactory : public SurfaceFactoryOzone {
bool enforce_protected_memory) override;
#endif
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
scoped_refptr<gfx::NativePixmap> CreateNativePixmap(
gfx::AcceleratedWidget widget,
VkDevice vk_device,
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
index aa649253a68..ebec5b49347 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
@@ -185,8 +185,7 @@ class WaylandSurfaceFactoryTest : public WaylandTest {
protected:
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvas(
gfx::AcceleratedWidget widget) {
- auto canvas = surface_factory_->CreateCanvasForWidget(
- widget_, base::ThreadTaskRunnerHandle::Get().get());
+ auto canvas = surface_factory_->CreateCanvasForWidget(widget_);
base::RunLoop().RunUntilIdle();
return canvas;
@@ -234,7 +233,8 @@ TEST_P(WaylandSurfaceFactoryTest,
}
// Now, schedule 3 buffers for swap.
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget_);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
CallbacksHelper cbs_helper;
// Submit all the available buffers.
@@ -318,7 +318,7 @@ TEST_P(WaylandSurfaceFactoryTest,
// This will result in Wayland server releasing previously attached buffer for
// swap id=0u and calling OnSubmission for buffer with swap id=1u.
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -368,7 +368,7 @@ TEST_P(WaylandSurfaceFactoryTest,
// This will result in Wayland server releasing previously attached buffer for
// swap id=1u and calling OnSubmission for buffer with swap id=2u.
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
index fef56d4fcc8..e5017840921 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
@@ -43,6 +43,10 @@ bool GtkUiDelegateWayland::SetGdkWindowTransientFor(
return false;
}
+void GtkUiDelegateWayland::ClearTransientFor(gfx::AcceleratedWidget parent) {
+ NOTIMPLEMENTED_LOG_ONCE();
+}
+
void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) {
// TODO(crbug.com/1008755): Check if gtk_window_present_with_time is needed
// here as well, similarly to what is done in X11 impl.
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
index dd7baf93bf7..4f3f606c5ac 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
@@ -25,6 +25,7 @@ class GtkUiDelegateWayland : public GtkUiDelegate {
GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override;
bool SetGdkWindowTransientFor(GdkWindow* window,
gfx::AcceleratedWidget parent) override;
+ void ClearTransientFor(gfx::AcceleratedWidget parent) override;
void ShowGtkWindow(GtkWindow* window) override;
private:
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc
new file mode 100644
index 00000000000..c5e31fd5b9d
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc
@@ -0,0 +1,115 @@
+// 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 "ui/ozone/platform/wayland/host/wayland_auxiliary_window.h"
+
+#include "ui/ozone/platform/wayland/common/wayland_util.h"
+#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+
+namespace ui {
+
+WaylandAuxiliaryWindow::WaylandAuxiliaryWindow(PlatformWindowDelegate* delegate,
+ WaylandConnection* connection)
+ : WaylandWindow(delegate, connection) {}
+
+WaylandAuxiliaryWindow::~WaylandAuxiliaryWindow() = default;
+
+void WaylandAuxiliaryWindow::Show(bool inactive) {
+ if (subsurface_)
+ return;
+
+ CreateSubsurface();
+ UpdateBufferScale(false);
+}
+
+void WaylandAuxiliaryWindow::Hide() {
+ if (!subsurface_)
+ return;
+
+ subsurface_.reset();
+
+ // Detach buffer from surface in order to completely shutdown menus and
+ // tooltips, and release resources.
+ connection()->buffer_manager_host()->ResetSurfaceContents(root_surface());
+}
+
+bool WaylandAuxiliaryWindow::IsVisible() const {
+ return !!subsurface_;
+}
+
+void WaylandAuxiliaryWindow::SetBounds(const gfx::Rect& bounds) {
+ auto old_bounds = GetBounds();
+ WaylandWindow::SetBounds(bounds);
+
+ if (old_bounds == bounds || !parent_window())
+ return;
+
+ auto subsurface_bounds_dip =
+ wl::TranslateWindowBoundsToParentDIP(this, parent_window());
+ wl_subsurface_set_position(subsurface_.get(), subsurface_bounds_dip.x(),
+ subsurface_bounds_dip.y());
+ root_surface()->Commit();
+ connection()->ScheduleFlush();
+}
+
+void WaylandAuxiliaryWindow::CreateSubsurface() {
+ auto* parent = parent_window();
+ if (!parent) {
+ // wl_subsurface can be used for several purposes: tooltips and drag arrow
+ // windows. If we are in a drag process, use the entered window. Otherwise,
+ // it must be a tooltip.
+ if (connection()->IsDragInProgress()) {
+ parent = connection()->data_drag_controller()->entered_window();
+ set_parent_window(parent);
+ } else {
+ // If Aura does not not provide a reference parent window, needed by
+ // Wayland, we get the current focused window to place and show the
+ // tooltips.
+ parent =
+ connection()->wayland_window_manager()->GetCurrentFocusedWindow();
+ }
+ }
+
+ // Tooltip and drag arrow creation is an async operation. By the time Aura
+ // actually creates them, it is possible that the user has already moved the
+ // mouse/pointer out of the window that triggered the tooltip, or user is no
+ // longer in a drag/drop process. In this case, parent is nullptr.
+ if (!parent)
+ return;
+
+ subsurface_ = root_surface()->CreateSubsurface(parent->root_surface());
+
+ auto subsurface_bounds_dip =
+ wl::TranslateWindowBoundsToParentDIP(this, parent);
+
+ DCHECK(subsurface_);
+ // Convert position to DIP.
+ wl_subsurface_set_position(subsurface_.get(), subsurface_bounds_dip.x(),
+ subsurface_bounds_dip.y());
+ wl_subsurface_set_desync(subsurface_.get());
+ parent->root_surface()->Commit();
+ connection()->ScheduleFlush();
+
+ // Notify the observers the window has been configured. Please note that
+ // subsurface doesn't send ack configure events. Thus, notify the observers as
+ // soon as the subsurface is created.
+ connection()->wayland_window_manager()->NotifyWindowConfigured(this);
+}
+
+bool WaylandAuxiliaryWindow::OnInitialize(
+ PlatformWindowInitProperties properties) {
+ // If we do not have parent window provided, we must always use a focused
+ // window or a window that entered drag whenever the subsurface is created.
+ if (properties.parent_widget == gfx::kNullAcceleratedWidget) {
+ DCHECK(!parent_window());
+ return true;
+ }
+ set_parent_window(GetParentWindow(properties.parent_widget));
+ return true;
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h
new file mode 100644
index 00000000000..0db19755696
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_auxiliary_window.h
@@ -0,0 +1,39 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_AUXILIARY_WINDOW_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_AUXILIARY_WINDOW_H_
+
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+
+namespace ui {
+
+// A WaylandWindow implementation to show tooltips and arrow windows.
+class WaylandAuxiliaryWindow : public WaylandWindow {
+ public:
+ WaylandAuxiliaryWindow(PlatformWindowDelegate* delegate,
+ WaylandConnection* connection);
+ WaylandAuxiliaryWindow(const WaylandAuxiliaryWindow&) = delete;
+ WaylandAuxiliaryWindow& operator=(const WaylandAuxiliaryWindow&) = delete;
+ ~WaylandAuxiliaryWindow() override;
+
+ // PlatformWindow overrides:
+ void Show(bool inactive) override;
+ void Hide() override;
+ bool IsVisible() const override;
+ void SetBounds(const gfx::Rect& bounds) override;
+
+ private:
+ // WaylandWindow overrides:
+ bool OnInitialize(PlatformWindowInitProperties properties) override;
+
+ // Creates (if necessary) and shows a subsurface window.
+ void CreateSubsurface();
+
+ wl::Object<wl_subsurface> subsurface_;
+};
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_AUXILIARY_WINDOW_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 8320574a540..c60c35e4ab4 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -8,14 +8,15 @@
#include <memory>
#include "base/i18n/number_formatting.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_drm.h"
#include "ui/ozone/platform/wayland/host/wayland_shm.h"
+#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
#include "ui/ozone/platform/wayland/host/wayland_surface.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
@@ -65,8 +66,6 @@ class WaylandBufferManagerHost::Surface {
~Surface() = default;
bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) {
- DCHECK(!pending_buffer_);
-
// The window has already been destroyed.
if (!wayland_surface_)
return true;
@@ -96,7 +95,7 @@ class WaylandBufferManagerHost::Surface {
if (buffer->attached && !buffer->wl_buffer)
return false;
- pending_buffer_ = buffer;
+ pending_buffers_.push_back(buffer);
MaybeProcessPendingBuffer();
return true;
}
@@ -109,35 +108,16 @@ class WaylandBufferManagerHost::Surface {
size_t DestroyBuffer(uint32_t buffer_id) {
auto* buffer = GetBuffer(buffer_id);
- // We ack the submission of a front buffer whenever a previous front back
- // becomes the back one and receives a buffer release callback. But the
- // following can happen:
- //
- // Imagine the order:
- // the front buffer is buffer 2, then
- // Commit[1]
- // Destroy[2]
- // Commit[3]
- // Release[1]
- // Ack[3] <= this results in a wrong order of the callbacks. In reality,
- // the buffer [1] must have been acked, because the buffer 2 was
- // released.
- // But if the buffer 2 is destroyed, the buffer release callback never
- // comes for that buffer. Thus, if there is a submitted buffer, notify
- // the client about successful swap.
- // If the window has already been destroyed, no need to complete the
- // submission.
- if (buffer && !buffer->released && submitted_buffer_ && wayland_surface_)
- CompleteSubmission();
-
- if (prev_submitted_buffer_ == buffer)
- prev_submitted_buffer_ = nullptr;
-
- if (pending_buffer_ == buffer)
- pending_buffer_ = nullptr;
-
- auto result = buffers_.erase(buffer_id);
- return result;
+
+ // Treat destroying a buffer as a release, and make sure to call any
+ // OnSubmission callbacks that would be sent as a result of that.
+ if (buffer) {
+ buffer->released = true;
+ MaybeProcessSubmittedBuffers();
+ base::Erase(pending_buffers_, buffer);
+ }
+
+ return buffers_.erase(buffer_id);
}
void AttachWlBuffer(uint32_t buffer_id, wl::Object<wl_buffer> new_buffer) {
@@ -154,8 +134,7 @@ class WaylandBufferManagerHost::Surface {
if (buffer->wl_buffer)
SetupBufferReleaseListener(buffer);
- if (pending_buffer_ == buffer)
- MaybeProcessPendingBuffer();
+ MaybeProcessPendingBuffer();
}
void ClearState() {
@@ -165,9 +144,8 @@ class WaylandBufferManagerHost::Surface {
ResetSurfaceContents();
- prev_submitted_buffer_ = nullptr;
- submitted_buffer_ = nullptr;
- pending_buffer_ = nullptr;
+ submitted_buffers_.clear();
+ pending_buffers_.clear();
connection_->ScheduleFlush();
}
@@ -176,16 +154,11 @@ class WaylandBufferManagerHost::Surface {
if (!wayland_surface_)
return;
- wl_surface_attach(wayland_surface_->surface(), nullptr, 0, 0);
- wl_surface_commit(wayland_surface_->surface());
-
- // We cannot reset |prev_submitted_buffer_| here as long as the surface
- // might have attached a new buffer and is about to receive a release
- // callback. Check more comments below where the variable is declared.
- contents_reset_ = true;
+ wayland_surface_->AttachBuffer(nullptr);
+ wayland_surface_->Commit();
// ResetSurfaceContents happens upon WaylandWindow::Hide call, which
- // destroyes xdg_surface, xdg_popup, etc. They are going to be reinitialized
+ // destroys xdg_surface, xdg_popup, etc. They are going to be reinitialized
// once WaylandWindow::Show is called. Thus, they will have to be configured
// once again before buffers can be attached.
configured_ = false;
@@ -200,10 +173,10 @@ class WaylandBufferManagerHost::Surface {
bool HasBuffers() const { return !buffers_.empty(); }
- void OnWindowRemoved() { wayland_surface_ = nullptr; }
- bool HasWindow() const { return !!wayland_surface_; }
+ void OnSurfaceRemoved() { wayland_surface_ = nullptr; }
+ bool HasSurface() const { return !!wayland_surface_; }
- void OnWindowConfigured() {
+ void OnSurfaceConfigured() {
if (configured_)
return;
@@ -226,50 +199,44 @@ class WaylandBufferManagerHost::Surface {
using PresentationFeedbackQueue = std::vector<FeedbackInfo>;
+ // Holds information about a submitted buffer.
+ struct SubmissionInfo {
+ // ID of the submitted buffer. Buffers may be destroyed after they have been
+ // submitted but before we send OnSubmission for them, e.g. if the same
+ // buffer is submitted twice in a row. Keep the ID so we send OnSubmission
+ // even if a buffer is destroyed.
+ uint32_t buffer_id;
+ // Whether this buffer has had OnSubmission sent for it.
+ bool acked;
+ };
+
bool CommitBufferInternal(WaylandBuffer* buffer) {
DCHECK(buffer && wayland_surface_);
- DCHECK(!pending_buffer_);
- DCHECK(!submitted_buffer_);
- submitted_buffer_ = buffer;
-
- // if the same buffer has been submitted again right after the client
+ // If the same buffer has been submitted again right after the client
// received OnSubmission for that buffer, just damage the buffer and
// commit the surface again.
- if (prev_submitted_buffer_ != submitted_buffer_) {
+ if (submitted_buffers_.empty() ||
+ submitted_buffers_.back().buffer_id != buffer->buffer_id) {
// Once the BufferRelease is called, the buffer will be released.
DCHECK(buffer->released);
buffer->released = false;
AttachBuffer(buffer);
}
+ // If the client submits the same buffer twice, we need to store it twice,
+ // because the client will expect two acks for it.
+ submitted_buffers_.push_back(
+ SubmissionInfo{buffer->buffer_id, /*acked=*/false});
+
DamageBuffer(buffer);
SetupFrameCallback();
SetupPresentationFeedback(buffer->buffer_id);
CommitSurface();
-
connection_->ScheduleFlush();
-
- // If the contents were reset, there is no buffer attached. It means we have
- // to behave the same way as if it was the very first frame. Check the
- // comment below where the |contents_reset_| is declared.
- if (contents_reset_) {
- prev_submitted_buffer_ = nullptr;
- contents_reset_ = false;
- }
-
- // If it was the very first frame, the surface has not had a back buffer
- // before, and Wayland won't release the front buffer until next buffer is
- // attached. Thus, notify about successful submission immediately.
- //
- // As said above, if the client submits the same buffer again, we must
- // notify the client about the submission immediately as Wayland compositor
- // is not going to send a release callback for a buffer committed more than
- // once.
- if (!prev_submitted_buffer_ || prev_submitted_buffer_ == submitted_buffer_)
- CompleteSubmission();
+ MaybeProcessSubmittedBuffers();
return true;
}
@@ -277,7 +244,7 @@ class WaylandBufferManagerHost::Surface {
void DamageBuffer(WaylandBuffer* buffer) {
DCHECK(wayland_surface_);
- gfx::Rect pending_damage_region = std::move(buffer->damage_region);
+ gfx::Rect pending_damage_region = buffer->damage_region;
// If the size of the damage region is empty, wl_surface_damage must be
// supplied with the actual size of the buffer, which is going to be
// committed.
@@ -285,48 +252,17 @@ class WaylandBufferManagerHost::Surface {
pending_damage_region.set_size(buffer->size);
DCHECK(!pending_damage_region.size().IsEmpty());
- if (connection_->compositor_version() >=
- WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
- // wl_surface_damage_buffer relies on compositor API version 4. See
- // https://bit.ly/2u00lv6 for details.
- // We don't need to apply any scaling because pending_damage_region is
- // already in buffer coordinates.
- wl_surface_damage_buffer(
- wayland_surface_->surface(), pending_damage_region.x(),
- pending_damage_region.y(), pending_damage_region.width(),
- pending_damage_region.height());
- } else {
- // The calculation for damage region relies on two assumptions:
- // 1) The buffer is always attached at surface location (0, 0)
- // 2) The API wl_surface::set_buffer_transform is not used.
- // It's possible to write logic that accounts for both cases above, but
- // it's currently unnecessary.
- //
- // Note: The damage region may not be an integer multiple of scale. To
- // keep the implementation simple, the x() and y() coordinates round down,
- // and the width() and height() calculations always add an extra pixel.
- int scale = wayland_surface_->buffer_scale();
- wl_surface_damage(wayland_surface_->surface(),
- pending_damage_region.x() / scale,
- pending_damage_region.y() / scale,
- pending_damage_region.width() / scale + 1,
- pending_damage_region.height() / scale + 1);
- }
+ wayland_surface_->Damage(pending_damage_region);
}
void AttachBuffer(WaylandBuffer* buffer) {
DCHECK(wayland_surface_ && configured_);
-
- // The logic in DamageBuffer currently relies on attachment coordinates of
- // (0, 0). If this changes, then the calculation in DamageBuffer will also
- // need to be updated.
- wl_surface_attach(wayland_surface_->surface(), buffer->wl_buffer.get(), 0,
- 0);
+ wayland_surface_->AttachBuffer(buffer->wl_buffer.get());
}
void CommitSurface() {
DCHECK(wayland_surface_);
- wl_surface_commit(wayland_surface_->surface());
+ wayland_surface_->Commit();
}
void SetupFrameCallback() {
@@ -390,42 +326,23 @@ class WaylandBufferManagerHost::Surface {
void OnRelease(struct wl_buffer* wl_buffer) {
DCHECK(wl_buffer);
+ // Releases may not necessarily come in order, so search the submitted
+ // buffers.
WaylandBuffer* buffer = nullptr;
- // The Wayland compositor may release the buffer immediately after it has
- // been submitted. Thus, check that wl_buffer belongs to either the
- // submitted buffer or the previously submitted buffer.
- if (submitted_buffer_ && submitted_buffer_->wl_buffer.get() == wl_buffer) {
- buffer = submitted_buffer_;
- DCHECK(buffer->wl_buffer.get() == wl_buffer);
- } else {
- buffer = prev_submitted_buffer_;
- DCHECK(buffer && buffer->wl_buffer.get() == wl_buffer);
+ for (const auto& b : submitted_buffers_) {
+ auto* submitted_buffer = GetBuffer(b.buffer_id);
+ if (submitted_buffer && wl_buffer == submitted_buffer->wl_buffer.get()) {
+ buffer = submitted_buffer;
+ break;
+ }
}
-
+ DCHECK(buffer);
DCHECK(!buffer->released);
buffer->released = true;
- // It may happen that the client has attached only one buffer and then
- // destroyed the window. That means that we manually called OnRelease on
- // that very first buffer attach as long as the surface has not had any
- // buffers attached before. However, the |submitted_buffer_| can be null in
- // the OnRelease and hit the DCHECK when the client does not continue
- // attaching new buffers (only one has been attached) and destroyes the
- // surface. In this case, the Wayland compositor releases the buffer and the
- // DCHECK is hit, because we have already run the OnRelease call manually.
- // Please note that the |prev_submitted_buffer_| is the buffer we have
- // released manually, and when the Wayland compositor sends OnRelease, the
- // validation of the wl_buffers succeeds because of that previous manual
- // OnRelease call.
- if (!submitted_buffer_)
- return;
-
- // Although, the Wayland compositor has just released the previously
- // attached buffer, which became a back buffer, we have to notify the client
- // that next buffer has been attach and become the front one. Thus, mark the
- // back buffer as released to ensure the DCHECK is not hit, and notify about
- // successful submission of the front buffer.
- CompleteSubmission();
+ // A release means we may be able to send OnSubmission for previously
+ // submitted buffers.
+ MaybeProcessSubmittedBuffers();
}
// wl_buffer_listener
@@ -435,19 +352,49 @@ class WaylandBufferManagerHost::Surface {
self->OnRelease(wl_buffer);
}
- void CompleteSubmission() {
- DCHECK(submitted_buffer_);
- auto id = submitted_buffer_->buffer_id;
- prev_submitted_buffer_ = submitted_buffer_;
- submitted_buffer_ = nullptr;
-
+ void MaybeProcessSubmittedBuffers() {
if (!wayland_surface_)
return;
+ // We force an OnSubmission call for the very first buffer submitted,
+ // otherwise buffers are not acked in a quiescent state. We keep track of
+ // whether it has already been acked. A buffer may have already been acked
+ // if it is the first buffer submitted and it is destroyed before being
+ // explicitly released. In that case, don't send an OnSubmission.
+ if (submitted_buffers_.size() == 1u && !submitted_buffers_[0].acked)
+ ProcessOldestSubmittedBuffer();
+
+ // Buffers may be released out of order, but we need to provide the
+ // guarantee that OnSubmission will be called in order of buffer submission.
+ while (submitted_buffers_.size() >= 2) {
+ auto* buffer0 = GetBuffer(submitted_buffers_[0].buffer_id);
+ // Treat a buffer as released if it has been explicitly released or
+ // destroyed.
+ bool buffer0_released = !buffer0 || buffer0->released;
+ // We can send OnSubmission for the 2nd oldest buffer if the oldest buffer
+ // is released, or it's the same buffer.
+ if (!buffer0_released &&
+ submitted_buffers_[0].buffer_id != submitted_buffers_[1].buffer_id)
+ break;
+
+ DCHECK(submitted_buffers_[0].acked);
+ DCHECK(!submitted_buffers_[1].acked);
+ submitted_buffers_.erase(submitted_buffers_.begin());
+ ProcessOldestSubmittedBuffer();
+ }
+ }
+
+ void ProcessOldestSubmittedBuffer() {
+ DCHECK(wayland_surface_);
+ DCHECK(!submitted_buffers_.empty());
+
+ submitted_buffers_.front().acked = true;
+ auto buffer_id = submitted_buffers_.front().buffer_id;
+
// We can now complete the latest submission. We had to wait for this
// release because SwapCompletionCallback indicates to the client that the
// previous buffer is available for reuse.
- buffer_manager_->OnSubmission(wayland_surface_->GetRootWidget(), id,
+ buffer_manager_->OnSubmission(wayland_surface_->GetWidget(), buffer_id,
gfx::SwapResult::SWAP_ACK);
// If presentation feedback is not supported, use a fake feedback. This
@@ -455,18 +402,19 @@ class WaylandBufferManagerHost::Surface {
if (!connection_->presentation()) {
DCHECK(feedback_queue_.empty());
buffer_manager_->OnPresentation(
- wayland_surface_->GetWidget(), id,
+ wayland_surface_->GetWidget(), buffer_id,
gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(),
GetPresentationKindFlags(0)));
} else {
for (auto& info : feedback_queue_) {
- if (info.buffer_id == id && !info.submission_completed) {
+ if (info.buffer_id == buffer_id && !info.submission_completed) {
info.submission_completed = true;
ProcessPresentationFeedbacks();
return;
}
}
- NOTREACHED() << "Did not find matching feedback for buffer_id=" << id;
+ NOTREACHED() << "Did not find matching feedback for buffer_id="
+ << buffer_id;
}
}
@@ -561,9 +509,10 @@ class WaylandBufferManagerHost::Surface {
}
void MaybeProcessPendingBuffer() {
+ DCHECK_LE(pending_buffers_.size(), 6u);
// There is nothing to process if there is no pending buffer or the window
// has been destroyed.
- if (!pending_buffer_ || !wayland_surface_)
+ if (pending_buffers_.empty() || !wayland_surface_)
return;
// This request may come earlier than the Wayland compositor has imported a
@@ -580,19 +529,19 @@ class WaylandBufferManagerHost::Surface {
//
// The third case happens if the window hasn't been configured until a
// request to attach a buffer to its surface is sent.
- if (!pending_buffer_->wl_buffer || wl_frame_callback_ || !configured_)
+ auto* pending_buffer = pending_buffers_.front();
+ if (!pending_buffer->wl_buffer || wl_frame_callback_ || !configured_)
return;
- auto* buffer = pending_buffer_;
- pending_buffer_ = nullptr;
- CommitBufferInternal(buffer);
+ pending_buffers_.erase(pending_buffers_.begin());
+ CommitBufferInternal(pending_buffer);
}
// Widget this helper surface backs and has 1:1 relationship with the
// WaylandWindow.
- // Non-owned. The window this helper surface stores and submits buffers for.
- const WaylandSurface* wayland_surface_;
+ // Non-owned. The surface this helper stores and submits buffers for.
+ WaylandSurface* wayland_surface_;
// Non-owned pointer to the connection.
WaylandConnection* const connection_;
@@ -608,25 +557,17 @@ class WaylandBufferManagerHost::Surface {
// operation.
wl::Object<wl_callback> wl_frame_callback_;
- // A presentation feedback provided by the Wayland server once frame is
- // shown.
- PresentationFeedbackQueue feedback_queue_;
+ // Queue of buffers which are pending to be submitted (look the comment
+ // in the CommitBuffer method).
+ std::vector<WaylandBuffer*> pending_buffers_;
+
+ // Queue of buffers which have been submitted and are waiting to be
+ // acked (send OnSubmission)
+ std::vector<SubmissionInfo> submitted_buffers_;
- // A buffer, which is pending to be submitted (look the comment in the
- // CommitBuffer method).
- WaylandBuffer* pending_buffer_ = nullptr;
- // Current submitted buffer.
- WaylandBuffer* submitted_buffer_ = nullptr;
- // Previous submitted buffer.
- WaylandBuffer* prev_submitted_buffer_ = nullptr;
-
- // If WaylandWindow becomes hidden, it may need to attach a null buffer to the
- // surface it backed to avoid its contents shown on screen. However, it
- // means that the Wayland compositor no longer sends new buffer release events
- // as long as there has not been buffer attached and no submission callback is
- // sent. To avoid this, |contents_reset_| can be used as an identification of
- // a need to call submission callback manually.
- bool contents_reset_ = false;
+ // Queue of buffers which have been acked and are waiting to have
+ // OnPresentation sent.
+ PresentationFeedbackQueue feedback_queue_;
// If WaylandWindow has never been configured, do not try to attach
// buffers to its surface. Otherwise, Wayland server will drop the connection
@@ -653,25 +594,49 @@ WaylandBufferManagerHost::~WaylandBufferManagerHost() {
void WaylandBufferManagerHost::OnWindowAdded(WaylandWindow* window) {
DCHECK(window);
- surfaces_[window->GetWidget()] =
- std::make_unique<Surface>(window->wayland_surface(), connection_, this);
+ surfaces_[window->root_surface()] =
+ std::make_unique<Surface>(window->root_surface(), connection_, this);
}
void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) {
DCHECK(window);
- auto it = surfaces_.find(window->GetWidget());
+ auto it = surfaces_.find(window->root_surface());
DCHECK(it != surfaces_.end());
- if (it->second->HasBuffers())
- it->second->OnWindowRemoved();
- else
- surfaces_.erase(it);
+ if (it->second->HasBuffers()) {
+ it->second->OnSurfaceRemoved();
+ surface_graveyard_.emplace_back(std::move(it->second));
+ }
+ surfaces_.erase(it);
}
void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) {
DCHECK(window);
- auto it = surfaces_.find(window->GetWidget());
+ auto it = surfaces_.find(window->root_surface());
+ DCHECK(it != surfaces_.end());
+ it->second->OnSurfaceConfigured();
+}
+
+void WaylandBufferManagerHost::OnSubsurfaceAdded(
+ WaylandWindow* window,
+ WaylandSubsurface* subsurface) {
+ DCHECK(subsurface);
+ surfaces_[subsurface->wayland_surface()] = std::make_unique<Surface>(
+ subsurface->wayland_surface(), connection_, this);
+ // WaylandSubsurface is always configured.
+ surfaces_[subsurface->wayland_surface()]->OnSurfaceConfigured();
+}
+
+void WaylandBufferManagerHost::OnSubsurfaceRemoved(
+ WaylandWindow* window,
+ WaylandSubsurface* subsurface) {
+ DCHECK(subsurface);
+ auto it = surfaces_.find(subsurface->wayland_surface());
DCHECK(it != surfaces_.end());
- it->second->OnWindowConfigured();
+ if (it->second->HasBuffers()) {
+ it->second->OnSurfaceRemoved();
+ surface_graveyard_.emplace_back(std::move(it->second));
+ }
+ surfaces_.erase(it);
}
void WaylandBufferManagerHost::SetTerminateGpuCallback(
@@ -731,7 +696,7 @@ void WaylandBufferManagerHost::CreateDmabufBasedBuffer(
uint32_t format,
uint32_t planes_count,
uint32_t buffer_id) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(error_message_.empty());
TRACE_EVENT2("wayland", "WaylandBufferManagerHost::CreateDmabufBasedBuffer",
@@ -771,7 +736,7 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd,
uint64_t length,
const gfx::Size& size,
uint32_t buffer_id) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(error_message_.empty());
TRACE_EVENT1("wayland", "WaylandBufferManagerHost::CreateShmBasedBuffer",
@@ -792,10 +757,31 @@ void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::PlatformHandle shm_fd,
connection_->ScheduleFlush();
}
+bool WaylandBufferManagerHost::CommitBufferInternal(
+ WaylandSurface* wayland_surface,
+ uint32_t buffer_id,
+ const gfx::Rect& damage_region) {
+ DCHECK(base::CurrentUIThread::IsSet());
+
+ Surface* surface = GetSurface(wayland_surface);
+ if (!surface || !ValidateBufferIdFromGpu(buffer_id))
+ return false;
+
+ if (!surface->CommitBuffer(buffer_id, damage_region)) {
+ error_message_ =
+ base::StrCat({"Buffer with ", NumberToString(buffer_id),
+ " id does not exist or failed to be created."});
+ }
+
+ if (!error_message_.empty())
+ TerminateGpuProcess();
+ return true;
+}
+
void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::Rect& damage_region) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
TRACE_EVENT1("wayland", "WaylandBufferManagerHost::CommitBuffer", "Buffer id",
buffer_id);
@@ -804,25 +790,42 @@ void WaylandBufferManagerHost::CommitBuffer(gfx::AcceleratedWidget widget,
if (widget == gfx::kNullAcceleratedWidget) {
error_message_ = "Invalid widget.";
- } else if (ValidateBufferIdFromGpu(buffer_id)) {
- Surface* surface = GetSurface(widget);
- if (!surface)
+ TerminateGpuProcess();
+ } else {
+ auto* window = connection_->wayland_window_manager()->GetWindow(widget);
+ if (!window)
return;
-
- if (!surface->CommitBuffer(buffer_id, damage_region)) {
- error_message_ =
- base::StrCat({"Buffer with ", NumberToString(buffer_id),
- " id does not exist or failed to be created."});
- }
+ CommitBufferInternal(window->root_surface(), buffer_id, damage_region);
}
+}
- if (!error_message_.empty())
+void WaylandBufferManagerHost::CommitOverlays(
+ gfx::AcceleratedWidget widget,
+ std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) {
+ DCHECK(base::CurrentUIThread::IsSet());
+
+ TRACE_EVENT0("wayland", "WaylandBufferManagerHost::CommitOverlays");
+
+ DCHECK(error_message_.empty());
+
+ if (widget == gfx::kNullAcceleratedWidget) {
+ error_message_ = "Invalid widget.";
TerminateGpuProcess();
+ }
+ WaylandWindow* window =
+ connection_->wayland_window_manager()->GetWindow(widget);
+ // In tab dragging, window may have been destroyed when buffers reach here. We
+ // omit buffer commits and OnSubmission, because the corresponding buffer
+ // queue in gpu process should be destroyed soon.
+ if (!window)
+ return;
+
+ window->CommitOverlays(overlays);
}
void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget,
uint32_t buffer_id) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
TRACE_EVENT1("wayland", "WaylandBufferManagerHost::DestroyBuffer",
"Buffer id", buffer_id);
@@ -841,20 +844,45 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget,
// has been stored in the |anonymous_buffers_|.
// 2) if the |widget| is null, always search a buffer with the |buffer_id| in
// the |anonymous_buffers_|.
+ // 3) if the |widget| hints at a non-existing window, it's likely that the
+ // window has been destroyed. In that case, the surface containing the buffer
+ // is in the graveyard.
uint32_t destroyed_count = 0u;
- Surface* surface = GetSurface(widget);
- if (surface) {
- destroyed_count = surface->DestroyBuffer(buffer_id);
- if (!surface->HasBuffers() && !surface->HasWindow())
- surfaces_.erase(widget);
+ auto* window = connection_->wayland_window_manager()->GetWindow(widget);
+ if (window) {
+ // Case 1).
+ Surface* surface = GetSurface(window->root_surface());
+ if (surface) {
+ destroyed_count = surface->DestroyBuffer(buffer_id);
+ if (!surface->HasBuffers() && !surface->HasSurface())
+ surfaces_.erase(window->root_surface());
+ }
+ const auto& subsurfaces = window->wayland_subsurfaces();
+ for (const auto& it : subsurfaces) {
+ Surface* subsurface = GetSurface((*it).wayland_surface());
+ if (subsurface)
+ destroyed_count += subsurface->DestroyBuffer(buffer_id);
+ }
+ } else {
+ // Case 3)
+ auto it = surface_graveyard_.begin();
+ while (it != surface_graveyard_.end()) {
+ destroyed_count += (*it)->DestroyBuffer(buffer_id);
+ if (!(*it)->HasBuffers() && !(*it)->HasSurface()) {
+ surface_graveyard_.erase(it++);
+ } else {
+ ++it;
+ }
+ }
}
// Ensure that we can't destroy more than 1 buffer. This can be 0 as well
// if no buffers are destroyed.
DCHECK_LE(destroyed_count, 1u);
+ // Case 2)
if (destroyed_count == 1u || DestroyAnonymousBuffer(buffer_id))
return;
@@ -864,8 +892,8 @@ void WaylandBufferManagerHost::DestroyBuffer(gfx::AcceleratedWidget widget,
}
void WaylandBufferManagerHost::ResetSurfaceContents(
- gfx::AcceleratedWidget widget) {
- auto* surface = GetSurface(widget);
+ WaylandSurface* wayland_surface) {
+ auto* surface = GetSurface(wayland_surface);
DCHECK(surface);
surface->ResetSurfaceContents();
}
@@ -901,8 +929,8 @@ bool WaylandBufferManagerHost::CreateBuffer(const gfx::Size& size,
}
WaylandBufferManagerHost::Surface* WaylandBufferManagerHost::GetSurface(
- gfx::AcceleratedWidget widget) const {
- auto it = surfaces_.find(widget);
+ WaylandSurface* wayland_surface) const {
+ auto it = surfaces_.find(wayland_surface);
return it != surfaces_.end() ? it->second.get() : nullptr;
}
@@ -1013,7 +1041,7 @@ void WaylandBufferManagerHost::OnSubmission(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::SwapResult& swap_result) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(buffer_manager_gpu_associated_);
buffer_manager_gpu_associated_->OnSubmission(widget, buffer_id, swap_result);
@@ -1023,7 +1051,7 @@ void WaylandBufferManagerHost::OnPresentation(
gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
DCHECK(buffer_manager_gpu_associated_);
buffer_manager_gpu_associated_->OnPresentation(widget, buffer_id, feedback);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
index b467835f726..20a974e1a5c 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
@@ -29,7 +29,9 @@
namespace ui {
class WaylandConnection;
+class WaylandSubsurface;
class WaylandWindow;
+class WaylandSurface;
// This is an internal helper representation of a wayland buffer object, which
// the GPU process creates when CreateBuffer is called. It's used for
@@ -82,6 +84,10 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
void OnWindowAdded(WaylandWindow* window) override;
void OnWindowRemoved(WaylandWindow* window) override;
void OnWindowConfigured(WaylandWindow* window) override;
+ void OnSubsurfaceAdded(WaylandWindow* window,
+ WaylandSubsurface* subsurface) override;
+ void OnSubsurfaceRemoved(WaylandWindow* window,
+ WaylandSubsurface* subsurface) override;
void SetTerminateGpuCallback(
base::OnceCallback<void(std::string)> terminate_gpu_cb);
@@ -137,25 +143,39 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
void CommitBuffer(gfx::AcceleratedWidget widget,
uint32_t buffer_id,
const gfx::Rect& damage_region) override;
+ // Called by the GPU and asks to configure the surface/subsurfaces and attach
+ // wl_buffers to WaylandWindow with the specified |widget|. Calls OnSubmission
+ // and OnPresentation on successful swap and pixels presented.
+ void CommitOverlays(
+ gfx::AcceleratedWidget widget,
+ std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays) override;
+
+ // Called by the WaylandWindow and asks to attach a wl_buffer with a
+ // |buffer_id| to a WaylandSurface.
+ // Calls OnSubmission and OnPresentation on successful swap and pixels
+ // presented.
+ bool CommitBufferInternal(WaylandSurface* wayland_surface,
+ uint32_t buffer_id,
+ const gfx::Rect& damage_region);
// When a surface is hidden, the client may want to detach the buffer attached
- // to the surface backed by |widget| to ensure Wayland does not present those
- // contents and do not composite in a wrong way. Otherwise, users may see the
- // contents of a hidden surface on their screens.
- void ResetSurfaceContents(gfx::AcceleratedWidget widget);
+ // to the surface to ensure Wayland does not present those contents and do not
+ // composite in a wrong way. Otherwise, users may see the contents of a hidden
+ // surface on their screens.
+ void ResetSurfaceContents(WaylandSurface* wayland_surface);
// Returns the anonymously created WaylandBuffer.
std::unique_ptr<WaylandBuffer> PassAnonymousWlBuffer(uint32_t buffer_id);
private:
// This is an internal representation of a real surface, which holds a pointer
- // to WaylandWindow. Also, this object holds buffers, frame callbacks and
- // presentation callbacks for that window's surface.
+ // to WaylandSurface. Also, this object holds buffers, frame callbacks and
+ // presentation callbacks for that surface.
class Surface;
bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id);
- Surface* GetSurface(gfx::AcceleratedWidget widget) const;
+ Surface* GetSurface(WaylandSurface* wayland_surface) const;
// Validates data sent from GPU. If invalid, returns false and sets an error
// message to |error_message_|.
@@ -192,7 +212,14 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
bool DestroyAnonymousBuffer(uint32_t buffer_id);
- base::flat_map<gfx::AcceleratedWidget, std::unique_ptr<Surface>> surfaces_;
+ base::flat_map<WaylandSurface*, std::unique_ptr<Surface>> surfaces_;
+
+ // When a WaylandWindow/WaylandSubsurface is removed, its corresponding
+ // Surface may still have an un-released buffer and un-acked presentation.
+ // Thus, we keep removed surfaces in the graveyard. It's safe to delete them
+ // when all of the Surface's buffers are destroyed because buffer destruction
+ // is deferred till after buffers are released and presentations are acked.
+ std::list<std::unique_ptr<Surface>> surface_graveyard_;
// Set when invalid data is received from the GPU process.
std::string error_message_;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
index a0e848a216d..7e1cdf1aee4 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -109,7 +109,7 @@ class ClipboardImpl final : public Clipboard, public DataSource::Delegate {
if (it == data_.end() && mime_type == ui::kMimeTypeTextUtf8)
it = data_.find(ui::kMimeTypeText);
if (it != data_.end())
- contents->assign(it->second.begin(), it->second.end());
+ contents->assign(it->second->data().begin(), it->second->data().end());
}
// The device manager used to access data device and create data sources.
@@ -157,8 +157,10 @@ void WaylandClipboard::RequestClipboardData(
data_map_ = data_map;
read_clipboard_closure_ = std::move(callback);
auto* clipboard = GetClipboard(buffer);
- if (!clipboard || !clipboard->Read(mime_type))
- SetData({}, mime_type);
+ if (!clipboard || !clipboard->Read(mime_type)) {
+ SetData(scoped_refptr<base::RefCountedBytes>(new base::RefCountedBytes()),
+ mime_type);
+ }
}
bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) {
@@ -183,11 +185,16 @@ void WaylandClipboard::GetAvailableMimeTypes(
std::move(callback).Run(mime_types);
}
-void WaylandClipboard::SetData(const std::vector<uint8_t>& contents,
+bool WaylandClipboard::IsSelectionBufferAvailable() const {
+ return (connection_->primary_selection_device_manager() != nullptr);
+}
+
+void WaylandClipboard::SetData(PlatformClipboard::Data contents,
const std::string& mime_type) {
if (!data_map_)
return;
+ DCHECK(contents);
(*data_map_)[mime_type] = contents;
if (!read_clipboard_closure_.is_null()) {
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
index d4641406e0c..515c85ca6c3 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -53,11 +53,11 @@ class WaylandClipboard : public PlatformClipboard {
bool IsSelectionOwner(ClipboardBuffer buffer) override;
void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override;
+ bool IsSelectionBufferAvailable() const override;
// TODO(nickdiego): Get rid of these methods once DataDevice implementations
// are decoupled from WaylandClipboard.
- void SetData(const std::vector<uint8_t>& contents,
- const std::string& mime_type);
+ void SetData(PlatformClipboard::Data contents, const std::string& mime_type);
void UpdateSequenceNumber(ClipboardBuffer buffer);
private:
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
index 0246bc58695..febebefad2c 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -14,8 +14,8 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/strings/string_util.h"
+#include "base/task/current_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/gfx/geometry/point.h"
@@ -52,6 +52,7 @@ constexpr uint32_t kMaxXdgShellVersion = 1;
constexpr uint32_t kMaxDeviceManagerVersion = 3;
constexpr uint32_t kMaxWpPresentationVersion = 1;
constexpr uint32_t kMaxTextInputManagerVersion = 1;
+constexpr uint32_t kMinAuraShellVersion = 10;
constexpr uint32_t kMinWlDrmVersion = 2;
constexpr uint32_t kMinWlOutputVersion = 2;
} // namespace
@@ -117,7 +118,7 @@ void WaylandConnection::ScheduleFlush() {
// When we are in tests, the message loop is set later when the
// initialization of the OzonePlatform complete. Thus, just
// flush directly. This doesn't happen in normal run.
- if (!base::MessageLoopCurrentForUI::IsSet()) {
+ if (!base::CurrentUIThread::IsSet()) {
Flush();
} else if (!scheduled_flush_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -140,6 +141,12 @@ bool WaylandConnection::IsDragInProgress() const {
WaylandDataDragController::State::kIdle;
}
+wl::Object<wl_surface> WaylandConnection::CreateSurface() {
+ DCHECK(compositor_);
+ return wl::Object<wl_surface>(
+ wl_compositor_create_surface(compositor_.get()));
+}
+
void WaylandConnection::Flush() {
wl_display_flush(display_.get());
scheduled_flush_ = false;
@@ -157,26 +164,33 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat,
pointer_.reset();
cursor_.reset();
wayland_cursor_position_.reset();
- } else if (wl_pointer* pointer = wl_seat_get_pointer(seat)) {
- pointer_ = std::make_unique<WaylandPointer>(pointer, this, event_source());
- cursor_ = std::make_unique<WaylandCursor>(pointer_.get(), this);
- wayland_cursor_position_ = std::make_unique<WaylandCursorPosition>();
- } else {
- LOG(ERROR) << "Failed to get wl_pointer from seat";
+ } else if (!pointer_) {
+ if (wl_pointer* pointer = wl_seat_get_pointer(seat)) {
+ pointer_ =
+ std::make_unique<WaylandPointer>(pointer, this, event_source());
+ cursor_ = std::make_unique<WaylandCursor>(pointer_.get(), this);
+ wayland_cursor_position_ = std::make_unique<WaylandCursorPosition>();
+ } else {
+ LOG(ERROR) << "Failed to get wl_pointer from seat";
+ }
}
if (!has_keyboard) {
keyboard_.reset();
- } else if (!CreateKeyboard()) {
- LOG(ERROR) << "Failed to create WaylandKeyboard";
+ } else if (!keyboard_) {
+ if (!CreateKeyboard()) {
+ LOG(ERROR) << "Failed to create WaylandKeyboard";
+ }
}
if (!has_touch) {
touch_.reset();
- } else if (wl_touch* touch = wl_seat_get_touch(seat)) {
- touch_ = std::make_unique<WaylandTouch>(touch, this, event_source());
- } else {
- LOG(ERROR) << "Failed to get wl_touch from seat";
+ } else if (!touch_) {
+ if (wl_touch* touch = wl_seat_get_touch(seat)) {
+ touch_ = std::make_unique<WaylandTouch>(touch, this, event_source());
+ } else {
+ LOG(ERROR) << "Failed to get wl_touch from seat";
+ }
}
}
@@ -352,6 +366,15 @@ void WaylandConnection::Global(void* data,
auto wayland_drm = wl::Bind<struct wl_drm>(registry, name, version);
connection->drm_ =
std::make_unique<WaylandDrm>(wayland_drm.release(), connection);
+ } else if (!connection->aura_shell_ &&
+ (strcmp(interface, "zaura_shell") == 0) &&
+ version >= kMinAuraShellVersion) {
+ connection->aura_shell_ =
+ wl::Bind<struct zaura_shell>(registry, name, version);
+ if (!connection->aura_shell_) {
+ LOG(ERROR) << "Failed to bind zaura_shell";
+ return;
+ }
}
connection->ScheduleFlush();
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
index 5c5754dd2fa..6dfc5b7a560 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -54,6 +54,7 @@ class WaylandConnection {
wl_subcompositor* subcompositor() const { return subcompositor_.get(); }
xdg_wm_base* shell() const { return shell_.get(); }
zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); }
+ zaura_shell* aura_shell() const { return aura_shell_.get(); }
wl_seat* seat() const { return seat_.get(); }
wp_presentation* presentation() const { return presentation_.get(); }
zwp_text_input_manager_v1* text_input_manager_v1() const {
@@ -121,6 +122,9 @@ class WaylandConnection {
// Returns true when dragging is entered or started.
bool IsDragInProgress() const;
+ // Creates a new wl_surface.
+ wl::Object<wl_surface> CreateSurface();
+
private:
void Flush();
void UpdateInputDevices(wl_seat* seat, uint32_t capabilities);
@@ -162,6 +166,7 @@ class WaylandConnection {
wl::Object<wp_presentation> presentation_;
wl::Object<zcr_keyboard_extension_v1> keyboard_extension_v1_;
wl::Object<zwp_text_input_manager_v1> text_input_manager_v1_;
+ wl::Object<zaura_shell> aura_shell_;
// Event source instance. Must be declared before input objects so it
// outlives them so thus being able to properly handle their destruction.
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
index 7ffd900fcae..fa12bbb1e31 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
@@ -19,13 +19,9 @@ namespace ui {
WaylandCursor::WaylandCursor(WaylandPointer* pointer,
WaylandConnection* connection)
- : pointer_(pointer), connection_(connection) {
- DCHECK(connection);
- DCHECK(connection->compositor());
-
- pointer_surface_.reset(
- wl_compositor_create_surface(connection->compositor()));
-}
+ : pointer_(pointer),
+ connection_(connection),
+ pointer_surface_(connection->CreateSurface()) {}
WaylandCursor::~WaylandCursor() = default;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h
index 5f8b6364bd4..1f73050be9b 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.h
@@ -56,7 +56,7 @@ class WaylandCursor {
// Holds the buffers and their memory until the compositor releases them.
base::flat_map<wl_buffer*, WaylandShmBuffer> buffers_;
- wl::Object<wl_surface> pointer_surface_;
+ const wl::Object<wl_surface> pointer_surface_;
DISALLOW_COPY_AND_ASSIGN(WaylandCursor);
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
index 1edd356006b..f80738e5e34 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -43,8 +43,8 @@ void WaylandDataDevice::StartDrag(const WaylandDataSource& data_source,
drag_delegate_ = delegate;
wl_data_device_start_drag(data_device_.get(), data_source.data_source(),
- origin_window.surface(), icon_surface,
- connection()->serial());
+ origin_window.root_surface()->surface(),
+ icon_surface, connection()->serial());
drag_delegate_->DrawIcon();
connection()->ScheduleFlush();
}
@@ -81,12 +81,12 @@ void WaylandDataDevice::SetSelectionSource(WaylandDataSource* source) {
connection()->ScheduleFlush();
}
-void WaylandDataDevice::ReadDragDataFromFD(
- base::ScopedFD fd,
- base::OnceCallback<void(const PlatformClipboard::Data&)> callback) {
- PlatformClipboard::Data contents;
+void WaylandDataDevice::ReadDragDataFromFD(base::ScopedFD fd,
+ RequestDataCallback callback) {
+ std::vector<uint8_t> contents;
wl::ReadDataFromFD(std::move(fd), &contents);
- std::move(callback).Run(contents);
+ std::move(callback).Run(scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&contents)));
}
// static
@@ -109,7 +109,7 @@ void WaylandDataDevice::OnEnter(void* data,
wl_fixed_t x,
wl_fixed_t y,
wl_data_offer* offer) {
- WaylandWindow* window = WaylandWindow::FromSurface(surface);
+ WaylandWindow* window = wl::RootWindowFromWlSurface(surface);
if (!window) {
LOG(ERROR) << "Failed to get window.";
return;
@@ -149,15 +149,15 @@ void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) {
void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) {
auto* self = static_cast<WaylandDataDevice*>(data);
- if (self->drag_delegate_) {
+ if (self->drag_delegate_)
self->drag_delegate_->OnDragLeave();
- // When in a DND session initiated by an external application,
- // |drag_delegate_| is set at OnEnter, and must be reset here to avoid
- // potential use-after-free.
- if (!self->drag_delegate_->IsDragSource())
- self->drag_delegate_ = nullptr;
- }
+ // When in a DND session initiated by an external application,
+ // |drag_delegate_| is set at OnEnter, and must be reset here to avoid
+ // potential use-after-free. Above call to OnDragLeave() may result in
+ // |drag_delegate_| being reset, so it must be checked here as well.
+ if (self->drag_delegate_ && !self->drag_delegate_->IsDragSource())
+ self->drag_delegate_ = nullptr;
}
void WaylandDataDevice::OnSelection(void* data,
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
index 5512f4c4480..12b84cf9cb1 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
@@ -13,6 +13,7 @@
#include "base/callback.h"
#include "base/files/scoped_file.h"
+#include "base/gtest_prod_util.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_base.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
@@ -32,8 +33,7 @@ class WaylandWindow;
// such as copy-and-paste and drag-and-drop mechanisms.
class WaylandDataDevice : public WaylandDataDeviceBase {
public:
- using RequestDataCallback =
- base::OnceCallback<void(const PlatformClipboard::Data&)>;
+ using RequestDataCallback = base::OnceCallback<void(PlatformClipboard::Data)>;
// DragDelegate is responsible for handling drag and drop sessions.
class DragDelegate {
@@ -79,6 +79,8 @@ class WaylandDataDevice : public WaylandDataDeviceBase {
void SetSelectionSource(WaylandDataSource* source);
private:
+ FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDrag);
+
void ReadDragDataFromFD(base::ScopedFD fd, RequestDataCallback callback);
// wl_data_device_listener callbacks
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
index 3e020a31134..7df5d9ec709 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
@@ -56,7 +56,10 @@ void WaylandDataDeviceBase::ReadClipboardDataFromFD(
const std::string& mime_type) {
std::vector<uint8_t> contents;
wl::ReadDataFromFD(std::move(fd), &contents);
- connection_->clipboard()->SetData(contents, mime_type);
+ connection_->clipboard()->SetData(
+ scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&contents)),
+ mime_type);
}
void WaylandDataDeviceBase::RegisterDeferredReadCallback() {
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
index b4bedb59ac9..855982cf567 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
@@ -15,7 +15,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/events/base_event_utils.h"
-#include "ui/ozone/platform/wayland/test/constants.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_data_device.h"
#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
@@ -32,15 +31,14 @@ namespace ui {
namespace {
+constexpr char kSampleClipboardText[] = "This is a sample text for clipboard.";
+
template <typename StringType>
ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) {
- ui::PlatformClipboard::Data result;
- auto* begin =
- reinterpret_cast<typename ui::PlatformClipboard::Data::const_pointer>(
- data_string.data());
- result.assign(begin, begin + (data_string.size() *
- sizeof(typename StringType::value_type)));
- return result;
+ std::vector<uint8_t> data_vector;
+ data_vector.assign(data_string.begin(), data_string.end());
+ return scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&data_vector));
}
} // namespace
@@ -64,7 +62,7 @@ class MockClipboardClient {
~MockClipboardClient() = default;
// Fill the clipboard backing store with sample data.
- void SetData(const PlatformClipboard::Data& data,
+ void SetData(PlatformClipboard::Data data,
const std::string& mime_type,
PlatformClipboard::OfferDataClosure callback) {
data_types_[mime_type] = data;
@@ -115,23 +113,25 @@ class WaylandDataDeviceManagerTest : public WaylandTest {
TEST_P(WaylandDataDeviceManagerTest, WriteToClipboard) {
// The client writes data to the clipboard ...
- PlatformClipboard::Data data;
- data.assign(wl::kSampleClipboardText,
- wl::kSampleClipboardText + strlen(wl::kSampleClipboardText));
- clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8,
- base::BindOnce([]() {}));
+ std::vector<uint8_t> data_vector(
+ kSampleClipboardText,
+ kSampleClipboardText + strlen(kSampleClipboardText));
+ clipboard_client_->SetData(
+ scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&data_vector)),
+ {kMimeTypeTextUtf8}, base::BindOnce([]() {}));
Sync();
// ... and the server reads it.
base::RunLoop run_loop;
auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ [](base::RunLoop* loop, std::vector<uint8_t>&& data) {
std::string string_data(data.begin(), data.end());
- EXPECT_EQ(wl::kSampleClipboardText, string_data);
+ EXPECT_EQ(kSampleClipboardText, string_data);
loop->Quit();
},
&run_loop);
- data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8,
+ data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8,
std::move(callback));
run_loop.Run();
}
@@ -140,8 +140,8 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboard) {
// TODO(nickdiego): implement this in terms of an actual wl_surface that
// gets focused and compositor sends data_device data to it.
auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(wl::kTextMimeTypeUtf8,
- ToClipboardData(std::string(wl::kSampleClipboardText)));
+ data_offer->OnOffer(kMimeTypeTextUtf8,
+ ToClipboardData(std::string(kSampleClipboardText)));
data_device_manager_->data_device()->OnSelection(data_offer);
Sync();
@@ -150,10 +150,11 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboard) {
// expectation.
auto callback =
base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) {
- std::string string_data = std::string(data->begin(), data->end());
- EXPECT_EQ(wl::kSampleClipboardText, string_data);
+ auto& bytes = data->get()->data();
+ std::string string_data = std::string(bytes.begin(), bytes.end());
+ EXPECT_EQ(kSampleClipboardText, string_data);
});
- clipboard_client_->ReadData(wl::kTextMimeTypeUtf8, std::move(callback));
+ clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback));
Sync();
}
@@ -163,18 +164,22 @@ TEST_P(WaylandDataDeviceManagerTest, ReadFromClipboardWithoutOffer) {
// an empty string.
auto callback =
base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) {
- std::string string_data = std::string(data->begin(), data->end());
+ auto& bytes = data->get()->data();
+ std::string string_data = std::string(bytes.begin(), bytes.end());
EXPECT_EQ("", string_data);
});
- clipboard_client_->ReadData(wl::kTextMimeTypeUtf8, std::move(callback));
+ clipboard_client_->ReadData(kMimeTypeTextUtf8, std::move(callback));
}
TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) {
auto callback = base::BindOnce([]() {});
- PlatformClipboard::Data data;
- data.assign(wl::kSampleClipboardText,
- wl::kSampleClipboardText + strlen(wl::kSampleClipboardText));
- clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8, std::move(callback));
+ std::vector<uint8_t> data_vector(
+ kSampleClipboardText,
+ kSampleClipboardText + strlen(kSampleClipboardText));
+ clipboard_client_->SetData(
+ scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&data_vector)),
+ {kMimeTypeTextUtf8}, std::move(callback));
Sync();
ASSERT_TRUE(clipboard_client_->IsSelectionOwner());
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
index ec14c3e3b7f..e4337897d54 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
@@ -96,7 +96,7 @@ void WaylandDataDragController::StartSession(const OSExchangeData& data,
Offer(data, operation);
// Create drag icon surface (if any) and store the data to be exchanged.
- icon_surface_.reset(CreateIconSurfaceIfNeeded(data));
+ CreateIconSurfaceIfNeeded(data);
data_ = std::make_unique<OSExchangeData>(data.provider().Clone());
// Starts the wayland drag session setting |this| object as delegate.
@@ -216,13 +216,21 @@ void WaylandDataDragController::OnDragDrop() {
void WaylandDataDragController::OnDataSourceFinish(bool completed) {
DCHECK(data_source_);
- if (origin_window_)
- origin_window_->OnDragSessionClose(data_source_->dnd_action());
+ DCHECK(origin_window_);
+
+ origin_window_->OnDragSessionClose(data_source_->dnd_action());
+
+ // DnD handlers expect DragLeave to be sent for drag sessions that end up
+ // with no data transfer (wl_data_source::cancelled event).
+ if (!completed)
+ origin_window_->OnDragLeave();
origin_window_ = nullptr;
data_source_.reset();
data_offer_.reset();
data_.reset();
+ data_device_->ResetDragDelegate();
+
state_ = State::kIdle;
}
@@ -265,11 +273,11 @@ void WaylandDataDragController::Offer(const OSExchangeData& data,
data_source_->SetAction(operation);
}
-wl_surface* WaylandDataDragController::CreateIconSurfaceIfNeeded(
+void WaylandDataDragController::CreateIconSurfaceIfNeeded(
const OSExchangeData& data) {
icon_bitmap_ = GetDragImage(data);
- return icon_bitmap_ ? wl_compositor_create_surface(connection_->compositor())
- : nullptr;
+ if (icon_bitmap_)
+ icon_surface_ = connection_->CreateSurface();
}
// Asynchronously requests and reads data for every negotiated/supported mime
@@ -293,9 +301,10 @@ void WaylandDataDragController::HandleUnprocessedMimeTypes() {
}
void WaylandDataDragController::OnMimeTypeDataTransferred(
- const PlatformClipboard::Data& contents) {
+ PlatformClipboard::Data contents) {
DCHECK_EQ(state_, State::kTransferring);
- if (!contents.empty()) {
+ DCHECK(contents);
+ if (!contents->data().empty()) {
std::string mime_type = unprocessed_mime_types_.front();
wl::AddToOSExchangeData(contents, mime_type, received_data_.get());
}
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
index 2d1e1b09d6d..5e4c15dd08b 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
@@ -78,9 +78,9 @@ class WaylandDataDragController : public WaylandDataDevice::DragDelegate,
std::string* contents) override;
void Offer(const OSExchangeData& data, int operation);
- wl_surface* CreateIconSurfaceIfNeeded(const OSExchangeData& data);
+ void CreateIconSurfaceIfNeeded(const OSExchangeData& data);
void HandleUnprocessedMimeTypes();
- void OnMimeTypeDataTransferred(const PlatformClipboard::Data& contents);
+ void OnMimeTypeDataTransferred(PlatformClipboard::Data contents);
void OnDataTransferFinished(
std::unique_ptr<ui::OSExchangeData> received_data);
std::string GetNextUnprocessedMimeType();
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
index a320c70dd1d..8bd11603863 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
@@ -14,16 +14,18 @@
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/file_info/file_info.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/events/base_event_utils.h"
+#include "ui/gfx/geometry/point.h"
#include "ui/ozone/platform/wayland/common/data_util.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
-#include "ui/ozone/platform/wayland/test/constants.h"
+#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_data_device.h"
#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
@@ -32,7 +34,9 @@
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/ozone/public/platform_clipboard.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
#include "url/gurl.h"
using testing::_;
@@ -42,32 +46,45 @@ namespace ui {
namespace {
+constexpr char kSampleTextForDragAndDrop[] =
+ "This is a sample text for drag-and-drop.";
+
constexpr FilenameToURLPolicy kFilenameToURLPolicy =
FilenameToURLPolicy::CONVERT_FILENAMES;
template <typename StringType>
PlatformClipboard::Data ToClipboardData(const StringType& data_string) {
- PlatformClipboard::Data result;
- auto* begin =
- reinterpret_cast<typename PlatformClipboard::Data::const_pointer>(
- data_string.data());
- result.assign(begin, begin + (data_string.size() *
- sizeof(typename StringType::value_type)));
- return result;
+ auto* begin = reinterpret_cast<typename std::vector<uint8_t>::const_pointer>(
+ data_string.data());
+ std::vector<uint8_t> result(
+ begin,
+ begin + (data_string.size() * sizeof(typename StringType::value_type)));
+ return scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&result));
}
} // namespace
+class MockDragHandlerDelegate : public WmDragHandler::Delegate {
+ public:
+ MOCK_METHOD1(OnDragLocationChanged, void(const gfx::Point& location));
+ MOCK_METHOD1(OnDragOperationChanged,
+ void(DragDropTypes::DragOperation operation));
+ MOCK_METHOD1(OnDragFinished, void(int operation));
+};
+
class MockDropHandler : public WmDropHandler {
public:
MockDropHandler() = default;
~MockDropHandler() override = default;
- MOCK_METHOD3(OnDragEnter,
+ MOCK_METHOD4(OnDragEnter,
void(const gfx::PointF& point,
std::unique_ptr<OSExchangeData> data,
- int operation));
- MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation));
+ int operation,
+ int modifiers));
+ MOCK_METHOD3(OnDragMotion,
+ int(const gfx::PointF& point, int operation, int modifiers));
MOCK_METHOD0(MockOnDragDrop, void());
MOCK_METHOD0(OnDragLeave, void());
@@ -78,7 +95,8 @@ class MockDropHandler : public WmDropHandler {
OSExchangeData* dropped_data() { return dropped_data_.get(); }
protected:
- void OnDragDrop(std::unique_ptr<OSExchangeData> data) override {
+ void OnDragDrop(std::unique_ptr<OSExchangeData> data,
+ int modifiers) override {
dropped_data_ = std::move(data);
MockOnDragDrop();
on_drop_closure_.Run();
@@ -103,6 +121,7 @@ class WaylandDataDragControllerTest : public WaylandTest {
data_device_manager_ = server_.data_device_manager();
DCHECK(data_device_manager_);
+ drag_handler_delegate_ = std::make_unique<MockDragHandlerDelegate>();
drop_handler_ = std::make_unique<MockDropHandler>();
SetWmDropHandler(window_.get(), drop_handler_.get());
}
@@ -116,38 +135,88 @@ class WaylandDataDragControllerTest : public WaylandTest {
}
base::string16 sample_text_for_dnd() const {
- static auto text = base::ASCIIToUTF16(wl::kSampleTextForDragAndDrop);
+ static auto text = base::ASCIIToUTF16(kSampleTextForDragAndDrop);
return text;
}
+ void ReadDataWhenSourceIsReady() {
+ Sync();
+
+ if (!data_device_manager_->data_source()) {
+ // The data source is created asynchronously via the window's data drag
+ // controller. If it is null now, it means that the task for that has not
+ // yet executed, and we have to come later.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &WaylandDataDragControllerTest::ReadDataWhenSourceIsReady,
+ base::Unretained(this)));
+ return;
+ }
+
+ // Now the server can read the data and give it to our callback.
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+ auto callback = base::BindOnce(
+ [](base::RunLoop* loop, std::vector<uint8_t>&& data) {
+ std::string result(data.begin(), data.end());
+ EXPECT_EQ(kSampleTextForDragAndDrop, result);
+ loop->Quit();
+ },
+ &run_loop);
+ data_device_manager_->data_source()->ReadData(kMimeTypeTextUtf8,
+ std::move(callback));
+ run_loop.Run();
+
+ data_device_manager_->data_source()->OnCancelled();
+ Sync();
+ }
+
+ void ScheduleDragCancel() {
+ Sync();
+
+ if (!data_device_manager_->data_source()) {
+ // The data source is created asynchronously by the data drag controller.
+ // If it is null at this point, it means that the task for that has not
+ // yet executed, and we have to try again a bit later.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel,
+ base::Unretained(this)));
+ return;
+ }
+
+ data_device_manager_->data_source()->OnCancelled();
+ Sync();
+ }
+
protected:
wl::TestDataDeviceManager* data_device_manager_;
std::unique_ptr<MockDropHandler> drop_handler_;
+ std::unique_ptr<MockDragHandlerDelegate> drag_handler_delegate_;
};
TEST_P(WaylandDataDragControllerTest, StartDrag) {
- bool restored_focus = window_->has_pointer_focus();
+ const bool restored_focus = window_->has_pointer_focus();
window_->SetPointerFocus(true);
// The client starts dragging.
+ ASSERT_EQ(PlatformWindowType::kWindow, window_->type());
OSExchangeData os_exchange_data;
os_exchange_data.SetString(sample_text_for_dnd());
- int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
- drag_controller()->StartSession(os_exchange_data, operation);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaylandDataDragControllerTest::ReadDataWhenSourceIsReady,
+ base::Unretained(this)));
+
+ static_cast<WaylandToplevelWindow*>(window_.get())
+ ->StartDrag(os_exchange_data,
+ DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE, {}, true,
+ drag_handler_delegate_.get());
Sync();
- // The server reads the data and the callback gets it.
- base::RunLoop run_loop;
- auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
- std::string result(data.begin(), data.end());
- EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
- loop->Quit();
- },
- &run_loop);
- data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8,
- std::move(callback));
- run_loop.Run();
+ EXPECT_FALSE(data_device()->drag_delegate_);
+
window_->SetPointerFocus(restored_focus);
}
@@ -166,7 +235,7 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithWrongMimeType) {
// to read it with a different mime type.
base::RunLoop run_loop;
auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ [](base::RunLoop* loop, std::vector<uint8_t>&& data) {
std::string result(data.begin(), data.end());
EXPECT_TRUE(result.empty());
loop->Quit();
@@ -194,9 +263,9 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithText) {
// |kTextMimeTypeUtf8|.
base::RunLoop run_loop;
auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ [](base::RunLoop* loop, std::vector<uint8_t>&& data) {
std::string result(data.begin(), data.end());
- EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
+ EXPECT_EQ(kSampleTextForDragAndDrop, result);
loop->Quit();
},
&run_loop);
@@ -208,9 +277,8 @@ TEST_P(WaylandDataDragControllerTest, StartDragWithText) {
TEST_P(WaylandDataDragControllerTest, ReceiveDrag) {
auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(
- kMimeTypeText,
- ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
+ data_offer->OnOffer(kMimeTypeText,
+ ToClipboardData(std::string(kSampleTextForDragAndDrop)));
gfx::Point entered_point(10, 10);
// The server sends an enter event.
@@ -229,11 +297,11 @@ TEST_P(WaylandDataDragControllerTest, ReceiveDrag) {
Sync();
- auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) {
+ auto callback = base::BindOnce([](PlatformClipboard::Data contents) {
std::string result;
- result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]),
- contents.size());
- EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
+ EXPECT_TRUE(contents);
+ result.assign(contents->front_as<char>(), contents->size());
+ EXPECT_EQ(kSampleTextForDragAndDrop, result);
});
// The client requests the data and gets callback with it.
@@ -246,9 +314,8 @@ TEST_P(WaylandDataDragControllerTest, ReceiveDrag) {
TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) {
auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(
- kMimeTypeText,
- ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
+ data_offer->OnOffer(kMimeTypeText,
+ ToClipboardData(std::string(kSampleTextForDragAndDrop)));
data_offer->OnOffer(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16(
"https://sample.com/\r\n"
"Sample")));
@@ -256,7 +323,7 @@ TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) {
kMimeTypeURIList,
ToClipboardData(std::string("file:///home/user/file\r\n")));
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1);
gfx::Point entered_point(10, 10);
data_device_manager_->data_device()->OnEnter(
1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
@@ -303,7 +370,7 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedUriList) {
auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
data_offer->OnOffer(kMimeTypeURIList, ToClipboardData(kCase.content));
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1);
gfx::Point entered_point(10, 10);
data_device_manager_->data_device()->OnEnter(
1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
@@ -358,7 +425,7 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) {
data_offer->OnOffer(kMimeTypeMozillaURL,
ToClipboardData(base::UTF8ToUTF16(kCase.content)));
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _, _)).Times(1);
gfx::Point entered_point(10, 10);
data_device_manager_->data_device()->OnEnter(
1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
@@ -395,6 +462,35 @@ TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) {
}
}
+// Verifies the correct delegate functions are called when a drag session is
+// started and cancelled within the same surface.
+TEST_P(WaylandDataDragControllerTest, StartAndCancel) {
+ const bool restored_focus = window_->has_pointer_focus();
+ window_->SetPointerFocus(true);
+
+ ASSERT_EQ(PlatformWindowType::kWindow, window_->type());
+ OSExchangeData os_exchange_data;
+ os_exchange_data.SetString(sample_text_for_dnd());
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaylandDataDragControllerTest::ScheduleDragCancel,
+ base::Unretained(this)));
+
+ // DnD handlers expect DragLeave to be sent before DragFinished when drag
+ // sessions end up with no data transfer (cancelled). Otherwise, it might lead
+ // to issues like https://crbug.com/1109324.
+ EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1);
+ EXPECT_CALL(*drag_handler_delegate_, OnDragFinished(_)).Times(1);
+
+ static_cast<WaylandToplevelWindow*>(window_.get())
+ ->StartDrag(os_exchange_data, DragDropTypes::DRAG_COPY, {}, true,
+ drag_handler_delegate_.get());
+ Sync();
+
+ window_->SetPointerFocus(restored_focus);
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandDataDragControllerTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
index 8656f41412c..cbdd82a25d0 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_offer_base.cc
@@ -9,14 +9,6 @@
namespace ui {
-namespace {
-
-const char kString[] = "STRING";
-const char kText[] = "TEXT";
-const char kUtf8String[] = "UTF8_STRING";
-
-} // namespace
-
WaylandDataOfferBase::WaylandDataOfferBase() = default;
WaylandDataOfferBase::~WaylandDataOfferBase() = default;
@@ -26,9 +18,10 @@ void WaylandDataOfferBase::EnsureTextMimeTypeIfNeeded() {
if (std::any_of(mime_types_.begin(), mime_types_.end(),
[](const std::string& mime_type) {
- return mime_type == kString || mime_type == kText ||
+ return mime_type == kMimeTypeLinuxString ||
+ mime_type == kMimeTypeLinuxText ||
mime_type == kMimeTypeTextUtf8 ||
- mime_type == kUtf8String;
+ mime_type == kMimeTypeLinuxUtf8String;
})) {
mime_types_.push_back(kMimeTypeText);
text_plain_mime_type_inserted_ = true;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
index 48d500ae5fb..41f186362f9 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
@@ -161,13 +161,18 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window,
}
void WaylandEventSource::OnPointerButtonEvent(EventType type,
- int changed_button) {
+ int changed_button,
+ WaylandWindow* window) {
DCHECK(type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED);
DCHECK(HasAnyPointerButtonFlag(changed_button));
if (!pointer_)
return;
+ auto* prev_focused_window = window_with_pointer_focus_;
+ if (window)
+ HandlePointerFocusChange(window);
+
pointer_flags_ = type == ET_MOUSE_PRESSED
? (pointer_flags_ | changed_button)
: (pointer_flags_ & ~changed_button);
@@ -177,6 +182,9 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type,
MouseEvent event(type, pointer_location_, pointer_location_,
EventTimeForNow(), flags, changed_button);
DispatchEvent(&event);
+
+ if (window)
+ HandlePointerFocusChange(prev_focused_window);
}
void WaylandEventSource::OnPointerMotionEvent(const gfx::PointF& location) {
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
index 7c9c409e702..e646df4fd4d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
@@ -59,6 +59,8 @@ class WaylandEventSource : public PlatformEventSource,
return last_pointer_button_pressed_;
}
+ int keyboard_modifiers() const { return keyboard_modifiers_; }
+
// Starts polling for events from the wayland connection file descriptor.
// This method assumes connection is already estabilished and input objects
// are already bound and properly initialized.
@@ -92,7 +94,9 @@ class WaylandEventSource : public PlatformEventSource,
void OnPointerDestroyed(WaylandPointer* pointer) override;
void OnPointerFocusChanged(WaylandWindow* window,
const gfx::PointF& location) override;
- void OnPointerButtonEvent(EventType evtype, int changed_button) override;
+ void OnPointerButtonEvent(EventType evtype,
+ int changed_button,
+ WaylandWindow* window = nullptr) override;
void OnPointerMotionEvent(const gfx::PointF& location) override;
void OnPointerAxisEvent(const gfx::Vector2d& offset) override;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc
index 9cec9ccbd4b..34bf114fa03 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source_unittest.cc
@@ -79,7 +79,9 @@ TEST_P(WaylandEventSourceTest, CheckPointerButtonHandling) {
uint32_t serial = 0;
uint32_t tstamp = 0;
wl_resource* surface_res =
- server_.GetObject<wl::MockSurface>(window1->GetWidget())->resource();
+ server_
+ .GetObject<wl::MockSurface>(window1->root_surface()->GetSurfaceId())
+ ->resource();
wl_resource* pointer_res = server_.seat()->pointer()->resource();
wl_pointer_send_enter(pointer_res, serial++, surface_res, 0, 0);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc
index 30325c45884..52f5eb0e870 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_watcher.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/check.h"
-#include "base/message_loop/message_loop_current.h"
+#include "base/task/current_thread.h"
#include "ui/events/event.h"
namespace ui {
@@ -38,7 +38,7 @@ bool WaylandEventWatcher::StopProcessingEvents() {
if (!watching_)
return false;
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
watching_ = false;
return controller_.StopWatchingFileDescriptor();
}
@@ -85,9 +85,9 @@ bool WaylandEventWatcher::StartWatchingFd(
DCHECK(!watching_);
}
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
int display_fd = wl_display_get_fd(display_);
- watching_ = base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+ watching_ = base::CurrentUIThread::Get()->WatchFileDescriptor(
display_fd, true, mode, &controller_, this);
return watching_;
}
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
index 7de0fe694d7..a4c905b7895 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
@@ -20,6 +20,7 @@
#include "ui/events/ozone/layout/keyboard_layout_engine.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/events/types/event_type.h"
+#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -101,7 +102,7 @@ void WaylandKeyboard::Enter(void* data,
wl_surface* surface,
wl_array* keys) {
// wl_surface might have been destroyed by this time.
- if (auto* window = WaylandWindow::FromSurface(surface)) {
+ if (auto* window = wl::RootWindowFromWlSurface(surface)) {
auto* self = static_cast<WaylandKeyboard*>(data);
self->delegate_->OnKeyboardFocusChanged(window, /*focused=*/true);
}
@@ -113,7 +114,7 @@ void WaylandKeyboard::Leave(void* data,
wl_surface* surface) {
// wl_surface might have been destroyed by this time.
auto* self = static_cast<WaylandKeyboard*>(data);
- if (auto* window = WaylandWindow::FromSurface(surface))
+ if (auto* window = wl::RootWindowFromWlSurface(surface))
self->delegate_->OnKeyboardFocusChanged(window, /*focused=*/false);
// Upon window focus lose, reset the key repeat timers.
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc
index 701f1678b19..7a4067df952 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_output.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_output.cc
@@ -11,7 +11,7 @@
namespace ui {
-WaylandOutput::WaylandOutput(const uint32_t output_id, wl_output* output)
+WaylandOutput::WaylandOutput(uint32_t output_id, wl_output* output)
: output_id_(output_id),
output_(output),
scale_factor_(kDefaultScaleFactor),
@@ -31,7 +31,7 @@ void WaylandOutput::Initialize(Delegate* delegate) {
wl_output_add_listener(output_.get(), &output_listener, this);
}
-void WaylandOutput::TriggerDelegateNotification() const {
+void WaylandOutput::TriggerDelegateNotifications() const {
DCHECK(!rect_in_physical_pixels_.IsEmpty());
delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_,
scale_factor_);
@@ -67,9 +67,8 @@ void WaylandOutput::OutputHandleMode(void* data,
// static
void WaylandOutput::OutputHandleDone(void* data, struct wl_output* wl_output) {
- WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data);
- if (wayland_output)
- wayland_output->TriggerDelegateNotification();
+ if (auto* output = static_cast<WaylandOutput*>(data))
+ output->TriggerDelegateNotifications();
}
// static
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output.h b/chromium/ui/ozone/platform/wayland/host/wayland_output.h
index 36f8c89fc1e..3bda676a179 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_output.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_output.h
@@ -20,23 +20,23 @@ class WaylandOutput {
public:
class Delegate {
public:
- virtual ~Delegate() {}
-
virtual void OnOutputHandleMetrics(uint32_t output_id,
const gfx::Rect& new_bounds,
int32_t scale_factor) = 0;
+
+ protected:
+ virtual ~Delegate() = default;
};
- WaylandOutput(const uint32_t output_id, wl_output* output);
+ WaylandOutput(uint32_t output_id, wl_output* output);
~WaylandOutput();
void Initialize(Delegate* delegate);
- void TriggerDelegateNotification() const;
-
uint32_t output_id() const { return output_id_; }
bool has_output(wl_output* output) const { return output_.get() == output; }
int32_t scale_factor() const { return scale_factor_; }
+ gfx::Rect bounds() const { return rect_in_physical_pixels_; }
// Tells if the output has already received physical screen dimensions in the
// global compositor space.
@@ -45,6 +45,8 @@ class WaylandOutput {
private:
static constexpr int32_t kDefaultScaleFactor = 1;
+ void TriggerDelegateNotifications() const;
+
// Callback functions used for setting geometric properties of the output
// and available modes.
static void OutputHandleGeometry(void* data,
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc
index 1f403f4928a..4d71d142fe7 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.cc
@@ -4,6 +4,8 @@
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
+#include <algorithm>
+#include <cstdint>
#include <memory>
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
@@ -15,13 +17,16 @@ WaylandOutputManager::WaylandOutputManager() = default;
WaylandOutputManager::~WaylandOutputManager() = default;
+// Output is considered ready when at least one wl_output is fully configured
+// (i.e: wl_output::done received), so that WaylandOutputManager is able to
+// instantiate a valid WaylandScreen when requested by the upper layer.
bool WaylandOutputManager::IsOutputReady() const {
- if (output_list_.empty())
- return false;
- return output_list_.front()->is_ready();
+ return std::find_if(output_list_.begin(), output_list_.end(),
+ [](const auto& output) { return output->is_ready(); }) !=
+ output_list_.end();
}
-void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id,
+void WaylandOutputManager::AddWaylandOutput(uint32_t output_id,
wl_output* output) {
// Make sure an output with |output_id| has not been added yet. It's very
// unlikely to happen, unless a compositor has a bug in the numeric names
@@ -29,26 +34,26 @@ void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id,
auto output_it = GetOutputItById(output_id);
DCHECK(output_it == output_list_.end());
auto wayland_output = std::make_unique<WaylandOutput>(output_id, output);
- WaylandOutput* wayland_output_ptr = wayland_output.get();
- output_list_.push_back(std::move(wayland_output));
-
- OnWaylandOutputAdded(output_id);
// Even if WaylandScreen has not been created, the output still must be
// initialized, which results in setting up a wl_listener and getting the
// geometry and the scaling factor from the Wayland Compositor.
- wayland_output_ptr->Initialize(this);
+ wayland_output->Initialize(this);
+ DCHECK(!wayland_output->is_ready());
+
+ output_list_.push_back(std::move(wayland_output));
}
-void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) {
+void WaylandOutputManager::RemoveWaylandOutput(uint32_t output_id) {
auto output_it = GetOutputItById(output_id);
// Check the comment in the WaylandConnetion::GlobalRemove.
if (output_it == output_list_.end())
return;
+ if (wayland_screen_)
+ wayland_screen_->OnOutputRemoved(output_id);
output_list_.erase(output_it);
- OnWaylandOutputRemoved(output_id);
}
std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen(
@@ -64,10 +69,10 @@ std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen(
// OutOutputHandleScale. All the other hot geometry and scale changes are done
// automatically, and the |wayland_screen_| is notified immediately about the
// changes.
- if (!output_list_.empty()) {
- for (auto& output : output_list_) {
- OnWaylandOutputAdded(output->output_id());
- output->TriggerDelegateNotification();
+ for (const auto& output : output_list_) {
+ if (output->is_ready()) {
+ wayland_screen->OnOutputAddedOrUpdated(
+ output->output_id(), output->bounds(), output->scale_factor());
}
}
@@ -90,22 +95,13 @@ WaylandOutput* WaylandOutputManager::GetOutput(uint32_t id) const {
return output_it->get();
}
-void WaylandOutputManager::OnWaylandOutputAdded(uint32_t output_id) {
- if (wayland_screen_)
- wayland_screen_->OnOutputAdded(output_id);
-}
-
-void WaylandOutputManager::OnWaylandOutputRemoved(uint32_t output_id) {
- if (wayland_screen_)
- wayland_screen_->OnOutputRemoved(output_id);
-}
-
void WaylandOutputManager::OnOutputHandleMetrics(uint32_t output_id,
const gfx::Rect& new_bounds,
int32_t scale_factor) {
- if (wayland_screen_)
- wayland_screen_->OnOutputMetricsChanged(output_id, new_bounds,
+ if (wayland_screen_) {
+ wayland_screen_->OnOutputAddedOrUpdated(output_id, new_bounds,
scale_factor);
+ }
}
WaylandOutputManager::OutputList::const_iterator
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h
index f05828a6d90..e02f10974c5 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_output_manager.h
@@ -44,9 +44,6 @@ class WaylandOutputManager : public WaylandOutput::Delegate {
WaylandScreen* wayland_screen() const { return wayland_screen_.get(); }
private:
- void OnWaylandOutputAdded(uint32_t output_id);
- void OnWaylandOutputRemoved(uint32_t output_id);
-
// WaylandOutput::Delegate:
void OnOutputHandleMetrics(uint32_t output_id,
const gfx::Rect& new_bounds,
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
index 1658a68cc93..13cb8c0a3fc 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
@@ -10,6 +10,7 @@
#include "ui/events/event.h"
#include "ui/events/types/event_type.h"
+#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -45,7 +46,7 @@ void WaylandPointer::Enter(void* data,
wl_fixed_t surface_y) {
DCHECK(data);
WaylandPointer* pointer = static_cast<WaylandPointer*>(data);
- WaylandWindow* window = WaylandWindow::FromSurface(surface);
+ WaylandWindow* window = wl::RootWindowFromWlSurface(surface);
gfx::PointF location{wl_fixed_to_double(surface_x),
wl_fixed_to_double(surface_y)};
pointer->delegate_->OnPointerFocusChanged(window, location);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
index b3f3a6ccbfa..c5cf9f7dccc 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
@@ -74,7 +74,9 @@ class WaylandPointer::Delegate {
virtual void OnPointerDestroyed(WaylandPointer* pointer) = 0;
virtual void OnPointerFocusChanged(WaylandWindow* window,
const gfx::PointF& location) = 0;
- virtual void OnPointerButtonEvent(EventType evtype, int changed_button) = 0;
+ virtual void OnPointerButtonEvent(EventType evtype,
+ int changed_button,
+ WaylandWindow* window = nullptr) = 0;
virtual void OnPointerMotionEvent(const gfx::PointF& location) = 0;
virtual void OnPointerAxisEvent(const gfx::Vector2d& offset) = 0;
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
index cf12f91f1b7..41dfb730784 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
@@ -85,8 +85,8 @@ TEST_P(WaylandPointerTest, Leave) {
Sync();
- wl::MockSurface* other_surface =
- server_.GetObject<wl::MockSurface>(other_widget);
+ wl::MockSurface* other_surface = server_.GetObject<wl::MockSurface>(
+ other_window->root_surface()->GetSurfaceId());
ASSERT_TRUE(other_surface);
wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
index ba52979990a..f1f7c8d43e5 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
@@ -24,10 +24,12 @@ bool WaylandPopup::CreateShellPopup() {
DCHECK(parent_window() && !shell_popup_);
- auto bounds_px = AdjustPopupWindowPosition();
+ auto subsurface_bounds_dip =
+ wl::TranslateWindowBoundsToParentDIP(this, parent_window());
ShellObjectFactory factory;
- shell_popup_ = factory.CreateShellPopupWrapper(connection(), this, bounds_px);
+ shell_popup_ = factory.CreateShellPopupWrapper(connection(), this,
+ subsurface_bounds_dip);
if (!shell_popup_) {
LOG(ERROR) << "Failed to create Wayland shell popup";
return false;
@@ -64,7 +66,7 @@ void WaylandPopup::Hide() {
// Detach buffer from surface in order to completely shutdown popups and
// tooltips, and release resources.
- connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget());
+ connection()->buffer_manager_host()->ResetSurfaceContents(root_surface());
}
bool WaylandPopup::IsVisible() const {
@@ -75,7 +77,7 @@ void WaylandPopup::HandlePopupConfigure(const gfx::Rect& bounds_dip) {
DCHECK(shell_popup());
DCHECK(parent_window());
- SetBufferScale(parent_window()->buffer_scale(), true);
+ root_surface()->SetBufferScale(parent_window()->buffer_scale(), true);
gfx::Rect new_bounds_dip = bounds_dip;
@@ -138,25 +140,9 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) {
return false;
}
// If parent window is known in advanced, we may set the scale early.
- SetBufferScale(parent_window()->buffer_scale(), false);
+ root_surface()->SetBufferScale(parent_window()->buffer_scale(), false);
set_ui_scale(parent_window()->ui_scale());
return true;
}
-gfx::Rect WaylandPopup::AdjustPopupWindowPosition() {
- auto* top_level_parent = GetRootParentWindow();
- DCHECK(top_level_parent);
- DCHECK(buffer_scale() == top_level_parent->buffer_scale());
- DCHECK(ui_scale() == top_level_parent->ui_scale());
-
- // Chromium positions windows in screen coordinates, but Wayland requires them
- // to be in local surface coordinates a.k.a relative to parent window.
- const gfx::Rect parent_bounds_dip =
- gfx::ScaleToRoundedRect(parent_window()->GetBounds(), 1.0 / ui_scale());
- gfx::Rect new_bounds_dip = wl::TranslateBoundsToParentCoordinates(
- gfx::ScaleToRoundedRect(GetBounds(), 1.0 / ui_scale()),
- parent_bounds_dip);
- return gfx::ScaleToRoundedRect(new_bounds_dip, ui_scale() / buffer_scale());
-}
-
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc
index 81af6275b27..fc4d4fac5ca 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -10,8 +10,10 @@
#include "base/stl_util.h"
#include "ui/display/display.h"
#include "ui/display/display_finder.h"
+#include "ui/display/display_list.h"
#include "ui/display/display_observer.h"
#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
@@ -26,9 +28,10 @@ WaylandScreen::WaylandScreen(WaylandConnection* connection)
WaylandScreen::~WaylandScreen() = default;
-void WaylandScreen::OnOutputAdded(uint32_t output_id) {
- display_list_.AddDisplay(display::Display(output_id),
- display::DisplayList::Type::NOT_PRIMARY);
+void WaylandScreen::OnOutputAddedOrUpdated(uint32_t output_id,
+ const gfx::Rect& bounds,
+ int32_t scale) {
+ AddOrUpdateDisplay(output_id, bounds, scale);
}
void WaylandScreen::OnOutputRemoved(uint32_t output_id) {
@@ -49,41 +52,32 @@ void WaylandScreen::OnOutputRemoved(uint32_t output_id) {
display_list_.RemoveDisplay(output_id);
}
-void WaylandScreen::OnOutputMetricsChanged(uint32_t output_id,
- const gfx::Rect& new_bounds,
- int32_t device_pixel_ratio) {
+void WaylandScreen::AddOrUpdateDisplay(uint32_t output_id,
+ const gfx::Rect& new_bounds,
+ int32_t scale_factor) {
display::Display changed_display(output_id);
if (!display::Display::HasForceDeviceScaleFactor())
- changed_display.set_device_scale_factor(device_pixel_ratio);
+ changed_display.set_device_scale_factor(scale_factor);
changed_display.set_bounds(new_bounds);
changed_display.set_work_area(new_bounds);
- bool is_primary = false;
- display::Display display_nearest_origin =
- GetDisplayNearestPoint(gfx::Point(0, 0));
- // If bounds of the nearest to origin display are empty, it must have been the
- // very first and the same display added before.
- if (display_nearest_origin.bounds().IsEmpty()) {
- DCHECK_EQ(display_nearest_origin.id(), changed_display.id());
- is_primary = true;
- } else if (changed_display.bounds().origin() <
- display_nearest_origin.bounds().origin()) {
- // If changed display is nearer to the origin than the previous display,
- // that one must become a primary display.
- is_primary = true;
- } else if (changed_display.bounds().OffsetFromOrigin() ==
- display_nearest_origin.bounds().OffsetFromOrigin()) {
- // If changed display has the same origin as the nearest to origin display,
- // |changed_display| must become a primary one or it has already been the
- // primary one. If a user changed positions of two displays (the second at
- // x,x was set to 0,0), the second change will modify geometry of the
- // display, which used to be the one nearest to the origin.
- is_primary = true;
+ // There are 2 cases where |changed_display| must be set as primary:
+ // 1. When it is the first one being added to the |display_list_|. Or
+ // 2. If it is nearest the origin than the previous primary or has the same
+ // origin as it. When an user, for example, swaps two side-by-side displays,
+ // at some point, as the notification come in, both will have the same
+ // origin.
+ auto type = display::DisplayList::Type::NOT_PRIMARY;
+ if (display_list_.displays().empty()) {
+ type = display::DisplayList::Type::PRIMARY;
+ } else {
+ auto nearest_origin = GetDisplayNearestPoint({0, 0}).bounds().origin();
+ auto changed_origin = changed_display.bounds().origin();
+ if (changed_origin < nearest_origin || changed_origin == nearest_origin)
+ type = display::DisplayList::Type::PRIMARY;
}
- display_list_.UpdateDisplay(
- changed_display, is_primary ? display::DisplayList::Type::PRIMARY
- : display::DisplayList::Type::NOT_PRIMARY);
+ display_list_.AddOrUpdateDisplay(changed_display, type);
auto* wayland_window_manager = connection_->wayland_window_manager();
for (auto* window : wayland_window_manager->GetWindowsOnOutput(output_id))
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h
index 8d65dd8130e..55993fc2a1a 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_screen.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen.h
@@ -14,6 +14,10 @@
#include "ui/gfx/geometry/point.h"
#include "ui/ozone/public/platform_screen.h"
+namespace gfx {
+class Rect;
+}
+
namespace ui {
class WaylandConnection;
@@ -26,11 +30,10 @@ class WaylandScreen : public PlatformScreen {
WaylandScreen& operator=(const WaylandScreen&) = delete;
~WaylandScreen() override;
- void OnOutputAdded(uint32_t output_id);
- void OnOutputRemoved(uint32_t output_id);
- void OnOutputMetricsChanged(uint32_t output_id,
+ void OnOutputAddedOrUpdated(uint32_t output_id,
const gfx::Rect& bounds,
int32_t output_scale);
+ void OnOutputRemoved(uint32_t output_id);
base::WeakPtr<WaylandScreen> GetWeakPtr();
@@ -53,6 +56,10 @@ class WaylandScreen : public PlatformScreen {
void RemoveObserver(display::DisplayObserver* observer) override;
private:
+ void AddOrUpdateDisplay(uint32_t output_id,
+ const gfx::Rect& bounds,
+ int32_t scale);
+
WaylandConnection* connection_ = nullptr;
display::DisplayList display_list_;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
index 899fff6445a..46b30f43b54 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -10,12 +10,15 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display_observer.h"
#include "ui/display/display_switches.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_output.h"
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_screen.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_output.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/platform_window/platform_window_init_properties.h"
@@ -68,15 +71,19 @@ class TestDisplayObserver : public display::DisplayObserver {
class WaylandScreenTest : public WaylandTest {
public:
- WaylandScreenTest() {}
- ~WaylandScreenTest() override {}
+ WaylandScreenTest() = default;
+ ~WaylandScreenTest() override = default;
void SetUp() override {
output_ = server_.output();
- output_->SetRect(gfx::Rect(0, 0, kOutputWidth, kOutputHeight));
WaylandTest::SetUp();
+ output_->SetRect({kOutputWidth, kOutputHeight});
+ output_->SetScale(1);
+ output_->Flush();
+ Sync();
+
output_manager_ = connection_->wayland_output_manager();
ASSERT_TRUE(output_manager_);
@@ -98,17 +105,6 @@ class WaylandScreenTest : public WaylandTest {
std::move(properties));
}
- void UpdateOutputGeometry(wl_resource* output_resource,
- const gfx::Rect& new_rect) {
- wl_output_send_geometry(output_resource, new_rect.x(), new_rect.y(),
- 0 /* physical_width */, 0 /* physical_height */,
- 0 /* subpixel */, "unknown_make", "unknown_model",
- 0 /* transform */);
- wl_output_send_mode(output_resource, WL_OUTPUT_MODE_CURRENT,
- new_rect.width(), new_rect.height(), 0 /* refresh */);
- wl_output_send_done(output_resource);
- }
-
void ValidateTheDisplayForWidget(gfx::AcceleratedWidget widget,
int64_t expected_display_id) {
display::Display display_for_widget =
@@ -146,18 +142,18 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) {
const int64_t old_primary_display_id =
platform_screen_->GetPrimaryDisplay().id();
+ gfx::Rect output1_rect = server_.output()->GetRect();
// Add a second display.
wl::TestOutput* output2 = server_.CreateAndInitializeOutput();
Sync();
- // Update rect of that display.
- gfx::Rect output1_rect = server_.output()->GetRect();
- gfx::Rect output2_rect(output1_rect.width(), 0, 800, 600);
// The second display is located to the right of first display like
// | || |.
- UpdateOutputGeometry(output2->resource(), output2_rect);
+ gfx::Rect output2_rect(output1_rect.width(), 0, 800, 600);
+ output2->SetRect(output2_rect);
+ output2->Flush();
Sync();
@@ -179,7 +175,8 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) {
Sync();
// Updates rect again.
- UpdateOutputGeometry(output2->resource(), output2_rect);
+ output2->SetRect(output2_rect);
+ output2->Flush();
Sync();
@@ -187,11 +184,14 @@ TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) {
added_display_id = observer.GetDisplay().id();
EXPECT_NE(platform_screen_->GetPrimaryDisplay().id(), added_display_id);
- // Now, rearrange displays so that second display becomes a primary one.
+ // Now, rearrange displays so that second display becomes the primary one.
output1_rect = gfx::Rect(1024, 0, 1024, 768);
+ output_->SetRect(output1_rect);
+ output_->Flush();
+
output2_rect = gfx::Rect(0, 0, 1024, 768);
- UpdateOutputGeometry(server_.output()->resource(), output1_rect);
- UpdateOutputGeometry(output2->resource(), output2_rect);
+ output2->SetRect(output2_rect);
+ output2->Flush();
Sync();
@@ -215,25 +215,24 @@ TEST_P(WaylandScreenTest, OutputPropertyChanges) {
TestDisplayObserver observer;
platform_screen_->AddObserver(&observer);
- const gfx::Rect new_rect(0, 0, 800, 600);
- UpdateOutputGeometry(output_->resource(), new_rect);
+ gfx::Rect new_rect{100, 100};
+ output_->SetRect(new_rect);
+ output_->Flush();
Sync();
- uint32_t changed_values = 0;
- changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS;
- changed_values |= display::DisplayObserver::DISPLAY_METRIC_WORK_AREA;
+ uint32_t changed_values = display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
+ display::DisplayObserver::DISPLAY_METRIC_WORK_AREA;
EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
EXPECT_EQ(observer.GetDisplay().bounds(), new_rect);
const int32_t new_scale_value = 2;
output_->SetScale(new_scale_value);
+ output_->Flush();
Sync();
- changed_values = 0;
- changed_values |=
- display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR;
+ changed_values = display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR;
EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value);
@@ -329,7 +328,8 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) {
// Place it on the right side of the primary display.
const gfx::Rect output2_rect =
gfx::Rect(primary_display.bounds().width(), 0, 1024, 768);
- UpdateOutputGeometry(output2->resource(), output2_rect);
+ output2->SetRect(output2_rect);
+ output2->Flush();
Sync();
@@ -360,10 +360,10 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) {
platform_screen_->GetDisplayMatching(gfx::Rect(1019, 0, 10, 10)).id());
// Place second display 700 pixels below along y axis (1024:700,1024x768)
- UpdateOutputGeometry(
- output2->resource(),
+ output2->SetRect(
gfx::Rect(gfx::Point(output2_rect.x(), output2_rect.y() + 700),
output2_rect.size()));
+ output2->Flush();
Sync();
@@ -388,6 +388,8 @@ TEST_P(WaylandScreenTest, GetDisplayMatching) {
platform_screen_->GetDisplayMatching(gfx::Rect(0, 0, 0, 0)).id());
platform_screen_->RemoveObserver(&observer);
+ output2->DestroyGlobal();
+ Sync();
}
TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) {
@@ -406,7 +408,8 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) {
// display.
const gfx::Rect output2_rect =
gfx::Rect(primary_display.bounds().width(), 0, 1024, 768);
- UpdateOutputGeometry(output2->resource(), output2_rect);
+ output2->SetRect(output2_rect);
+ output2->Flush();
Sync();
@@ -419,7 +422,8 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) {
ValidateTheDisplayForWidget(widget, primary_display.id());
// Now, send enter event for the surface, which was created before.
- wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(widget);
+ wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
ASSERT_TRUE(surface);
wl_surface_send_enter(surface->resource(), output_->resource());
@@ -454,6 +458,9 @@ TEST_P(WaylandScreenTest, GetDisplayForAcceleratedWidget) {
// The id of the entered display must correspond to the second output.
ValidateTheDisplayForWidget(widget, secondary_display.id());
+
+ output2->DestroyGlobal();
+ Sync();
}
TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
@@ -463,7 +470,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
PlatformWindowType::kWindow,
gfx::kNullAcceleratedWidget, &delegate);
- auto* surface = server_.GetObject<wl::MockSurface>(window_->GetWidget());
+ auto* surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
ASSERT_TRUE(surface);
// Announce pointer capability so that WaylandPointer is created on the client
@@ -488,8 +496,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
// WaylandScreen must return the last pointer location.
EXPECT_EQ(gfx::Point(10, 20), platform_screen_->GetCursorScreenPoint());
- auto* second_surface =
- server_.GetObject<wl::MockSurface>(second_window->GetWidget());
+ auto* second_surface = server_.GetObject<wl::MockSurface>(
+ second_window->root_surface()->GetSurfaceId());
ASSERT_TRUE(second_surface);
// Now, leave the first surface and enter second one.
wl_pointer_send_leave(pointer->resource(), ++serial, surface->resource());
@@ -530,8 +538,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
Sync();
- auto* menu_surface =
- server_.GetObject<wl::MockSurface>(menu_window->GetWidget());
+ auto* menu_surface = server_.GetObject<wl::MockSurface>(
+ menu_window->root_surface()->GetSurfaceId());
ASSERT_TRUE(menu_surface);
wl_pointer_send_enter(pointer->resource(), ++serial, menu_surface->resource(),
@@ -577,8 +585,8 @@ TEST_P(WaylandScreenTest, GetCursorScreenPoint) {
Sync();
- auto* nested_menu_surface =
- server_.GetObject<wl::MockSurface>(nested_menu_window->GetWidget());
+ auto* nested_menu_surface = server_.GetObject<wl::MockSurface>(
+ nested_menu_window->root_surface()->GetSurfaceId());
ASSERT_TRUE(nested_menu_surface);
wl_pointer_send_enter(pointer->resource(), ++serial,
@@ -616,6 +624,7 @@ TEST_P(WaylandScreenTest, SetBufferScale) {
const int32_t kTripleScale = 3;
EXPECT_CALL(*surface_, SetBufferScale(kTripleScale));
output_->SetScale(kTripleScale);
+ output_->Flush();
Sync();
@@ -637,6 +646,7 @@ TEST_P(WaylandScreenTest, SetBufferScale) {
EXPECT_NE(kForcedUIScale, kDoubleScale);
EXPECT_CALL(*surface_, SetBufferScale(kDoubleScale));
output_->SetScale(kDoubleScale);
+ output_->Flush();
Sync();
@@ -646,11 +656,92 @@ TEST_P(WaylandScreenTest, SetBufferScale) {
display::Display::ResetForceDeviceScaleFactorForTesting();
}
+namespace {
+
+class LazilyConfiguredScreenTest
+ : public WaylandTest,
+ public wl::TestWaylandServerThread::OutputDelegate {
+ public:
+ LazilyConfiguredScreenTest() = default;
+ LazilyConfiguredScreenTest(const LazilyConfiguredScreenTest&) = delete;
+ LazilyConfiguredScreenTest& operator=(const LazilyConfiguredScreenTest&) =
+ delete;
+ ~LazilyConfiguredScreenTest() override = default;
+
+ void SetUp() override {
+ // Being the server's output delegate allows LazilyConfiguredScreenTest to
+ // manipulate wl_outputs during the server's global objects initialization
+ // phase. See SetupOutputs() function below.
+ server_.set_output_delegate(this);
+
+ WaylandTest::SetUp();
+
+ output_manager_ = connection_->wayland_output_manager();
+ ASSERT_TRUE(output_manager_);
+ }
+
+ void TearDown() override {
+ WaylandTest::TearDown();
+ server_.set_output_delegate(nullptr);
+ }
+
+ protected:
+ // wl::TestWaylandServerThread::OutputDelegate:
+ void SetupOutputs(wl::TestOutput* primary) override {
+ // Keep the first wl_output announced "unconfigured" and just caches it for
+ // now, so we can exercise WaylandOutputManager::IsOutputReady() function
+ // when wl_output events come in unordered.
+ primary_output_ = primary;
+
+ // Create/announce a second wl_output object and makes it the first one to
+ // get configuration events (eg: geometry, done, etc). This is achieved by
+ // setting its bounds here.
+ aux_output_ = server_.CreateAndInitializeOutput();
+ aux_output_->SetRect({0, 0, 800, 600});
+ }
+
+ wl::TestOutput* primary_output_ = nullptr;
+ wl::TestOutput* aux_output_ = nullptr;
+ WaylandOutputManager* output_manager_ = nullptr;
+ bool auto_configure;
+};
+
+} // namespace
+
+// Ensures WaylandOutputManager and WaylandScreen properly handle scenarios
+// where multiple wl_output objects are announced but not "configured" (ie:
+// size, position, mode, etc sent to client) at bind time.
+TEST_P(LazilyConfiguredScreenTest, DualOutput) {
+ // Ensure WaylandScreen got properly created and fed with a single display
+ // object, ie: |aux_output_| at server side.
+ EXPECT_TRUE(output_manager_->IsOutputReady());
+ EXPECT_TRUE(screen_);
+ EXPECT_EQ(1u, screen_->GetAllDisplays().size());
+ Sync();
+
+ // Send wl_output configuration events for the first advertised wl_output
+ // object. ie: |primary_output_| at server side.
+ primary_output_->SetRect({800, 0, kOutputWidth, kOutputHeight});
+ primary_output_->SetScale(1);
+ primary_output_->Flush();
+ Sync();
+
+ // And make sure it makes its way into the WaylandScreen's display list at
+ // client side.
+ EXPECT_EQ(2u, screen_->GetAllDisplays().size());
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandScreenTest,
::testing::Values(kXdgShellStable));
INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
WaylandScreenTest,
::testing::Values(kXdgShellV6));
+INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
+ LazilyConfiguredScreenTest,
+ ::testing::Values(kXdgShellStable));
+INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
+ LazilyConfiguredScreenTest,
+ ::testing::Values(kXdgShellV6));
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
index e76aa6376dd..11d37ab75e1 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
@@ -1,44 +1,60 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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 "ui/ozone/platform/wayland/host/wayland_subsurface.h"
+#include <wayland-client.h>
+#include <cstdint>
+
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
-#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
-
-namespace ui {
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace {
gfx::Rect AdjustSubsurfaceBounds(const gfx::Rect& bounds_px,
const gfx::Rect& parent_bounds_px,
float ui_scale,
- int32_t buffer_scale) {
+ int32_t parent_buffer_scale) {
+ // TODO(fangzhoug): Verify the correctness of using ui_scale here, and in
+ // other ozone wayland files.
const auto parent_bounds_dip =
gfx::ScaleToRoundedRect(parent_bounds_px, 1.0 / ui_scale);
+ const auto bounds_dip = gfx::ScaleToRoundedRect(bounds_px, 1.0 / ui_scale);
auto new_bounds_dip =
- wl::TranslateBoundsToParentCoordinates(bounds_px, parent_bounds_dip);
- return gfx::ScaleToRoundedRect(new_bounds_dip, ui_scale / buffer_scale);
+ wl::TranslateBoundsToParentCoordinates(bounds_dip, parent_bounds_dip);
+ return gfx::ScaleToRoundedRect(new_bounds_dip,
+ ui_scale / parent_buffer_scale);
}
} // namespace
-WaylandSubsurface::WaylandSubsurface(PlatformWindowDelegate* delegate,
- WaylandConnection* connection)
- : WaylandWindow(delegate, connection) {}
+namespace ui {
+
+WaylandSubsurface::WaylandSubsurface(WaylandConnection* connection,
+ WaylandWindow* parent)
+ : wayland_surface_(connection, parent),
+ connection_(connection),
+ parent_(parent) {
+ DCHECK(parent_);
+ DCHECK(connection_);
+ if (!surface()) {
+ LOG(ERROR) << "Failed to create wl_surface";
+ return;
+ }
+}
WaylandSubsurface::~WaylandSubsurface() = default;
-void WaylandSubsurface::Show(bool inactive) {
- if (subsurface_)
- return;
+gfx::AcceleratedWidget WaylandSubsurface::GetWidget() const {
+ return wayland_surface_.GetWidget();
+}
- CreateSubsurface();
- UpdateBufferScale(false);
+void WaylandSubsurface::Show() {
+ if (!subsurface_)
+ CreateSubsurface();
}
void WaylandSubsurface::Hide() {
@@ -46,91 +62,89 @@ void WaylandSubsurface::Hide() {
return;
subsurface_.reset();
-
- // Detach buffer from surface in order to completely shutdown menus and
- // tooltips, and release resources.
- connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget());
+ connection_->buffer_manager_host()->ResetSurfaceContents(wayland_surface());
}
bool WaylandSubsurface::IsVisible() const {
return !!subsurface_;
}
-void WaylandSubsurface::SetBounds(const gfx::Rect& bounds) {
- auto old_bounds = GetBounds();
- WaylandWindow::SetBounds(bounds);
+void WaylandSubsurface::UpdateOpaqueRegion() {
+ gfx::Size region_size = enable_blend_ ? gfx::Size() : bounds_px_.size();
+ wl::Object<wl_region> region(
+ wl_compositor_create_region(connection_->compositor()));
+ wl_region_add(region.get(), 0, 0, region_size.width(), region_size.height());
+ wl_surface_set_opaque_region(surface(), region.get());
+}
- if (old_bounds == bounds || !parent_window())
+void WaylandSubsurface::SetBounds(const gfx::Rect& bounds) {
+ if (bounds_px_ == bounds)
return;
- // Translate location from screen to surface coordinates.
- auto bounds_px = AdjustSubsurfaceBounds(
- GetBounds(), parent_window()->GetBounds(), ui_scale(), buffer_scale());
- wl_subsurface_set_position(subsurface_.get(), bounds_px.x() / buffer_scale(),
- bounds_px.y() / buffer_scale());
- wl_surface_commit(surface());
- connection()->ScheduleFlush();
+ bounds_px_ = bounds;
+ if (IsVisible()) {
+ // Translate location from screen to surface coordinates.
+ auto bounds_px =
+ AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(),
+ parent_->ui_scale(), parent_->buffer_scale());
+ wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y());
+ }
}
void WaylandSubsurface::CreateSubsurface() {
- auto* parent = parent_window();
- if (!parent) {
- // wl_subsurface can be used for several purposes: tooltips and drag arrow
- // windows. If we are in a drag process, use the entered window. Otherwise,
- // it must be a tooltip.
- if (connection()->IsDragInProgress()) {
- parent = connection()->data_drag_controller()->entered_window();
- set_parent_window(parent);
- } else {
- // If Aura does not not provide a reference parent window, needed by
- // Wayland, we get the current focused window to place and show the
- // tooltips.
- parent =
- connection()->wayland_window_manager()->GetCurrentFocusedWindow();
- }
- }
+ DCHECK(parent_);
- // Tooltip and drag arrow creation is an async operation. By the time Aura
- // actually creates them, it is possible that the user has already moved the
- // mouse/pointer out of the window that triggered the tooltip, or user is no
- // longer in a drag/drop process. In this case, parent is NULL.
- if (!parent)
- return;
-
- wl_subcompositor* subcompositor = connection()->subcompositor();
+ wl_subcompositor* subcompositor = connection_->subcompositor();
DCHECK(subcompositor);
- subsurface_.reset(wl_subcompositor_get_subsurface(subcompositor, surface(),
- parent->surface()));
+ subsurface_ = wayland_surface()->CreateSubsurface(parent_->root_surface());
- // Chromium positions tooltip windows in screen coordinates, but Wayland
- // requires them to be in local surface coordinates a.k.a relative to parent
- // window.
- auto bounds_px = AdjustSubsurfaceBounds(GetBounds(), parent->GetBounds(),
- ui_scale(), buffer_scale());
+ // Chromium positions quads in display::Display coordinates in physical
+ // pixels, but Wayland requires them to be in local surface coordinates a.k.a
+ // relative to parent window.
+ auto bounds_px =
+ AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(),
+ parent_->ui_scale(), parent_->buffer_scale());
DCHECK(subsurface_);
- // Convert position to DIP.
- wl_subsurface_set_position(subsurface_.get(), bounds_px.x() / buffer_scale(),
- bounds_px.y() / buffer_scale());
- wl_subsurface_set_desync(subsurface_.get());
- wl_surface_commit(parent->surface());
- connection()->ScheduleFlush();
-
- // Notify the observers the window has been configured. Please note that
- // subsurface doesn't send ack configure events. Thus, notify the observers as
- // soon as the subsurface is created.
- connection()->wayland_window_manager()->NotifyWindowConfigured(this);
+ wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y());
+ wl_subsurface_set_sync(subsurface_.get());
+
+ // Subsurfaces don't need to trap input events. Its display rect is fully
+ // contained in |parent_|'s. Setting input_region to empty allows |parent_| to
+ // dispatch all of the input to platform window.
+ wl::Object<wl_region> region(
+ wl_compositor_create_region(connection_->compositor()));
+ wl_region_add(region.get(), 0, 0, 0, 0);
+ wl_surface_set_input_region(surface(), region.get());
}
-bool WaylandSubsurface::OnInitialize(PlatformWindowInitProperties properties) {
- // If we do not have parent window provided, we must always use a focused
- // window or a window that entered drag whenever the subsurface is created.
- if (properties.parent_widget == gfx::kNullAcceleratedWidget) {
- DCHECK(!parent_window());
- return true;
+void WaylandSubsurface::ConfigureAndShowSurface(
+ gfx::OverlayTransform transform,
+ const gfx::Rect& bounds_rect,
+ bool enable_blend,
+ const WaylandSurface* reference_below,
+ const WaylandSurface* reference_above) {
+ wayland_surface()->SetBufferScale(parent_->buffer_scale(), false);
+
+ gfx::Rect bounds_px{
+ bounds_rect.origin() + parent_->GetBounds().origin().OffsetFromOrigin(),
+ bounds_rect.size()};
+ auto old_bounds = bounds_px_;
+ SetBounds(bounds_px);
+
+ if (old_bounds != bounds_px_ || enable_blend_ != enable_blend) {
+ enable_blend_ = enable_blend;
+ UpdateOpaqueRegion();
+ }
+
+ Show();
+
+ DCHECK(!reference_above || !reference_below);
+ if (reference_below) {
+ wl_subsurface_place_above(subsurface_.get(), reference_below->surface());
+ } else if (reference_above) {
+ wl_subsurface_place_below(subsurface_.get(), reference_above->surface());
}
- set_parent_window(GetParentWindow(properties.parent_widget));
- return true;
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h
index e5c8bed26f8..50098b21715 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.h
@@ -1,36 +1,72 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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 UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SUBSURFACE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SUBSURFACE_H_
-#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
namespace ui {
+class WaylandConnection;
+class WaylandWindow;
-class WaylandSubsurface : public WaylandWindow {
+// Wraps a wl_surface with a wl_subsurface role assigned. It is used to submit a
+// buffer as a sub region of WaylandWindow.
+class WaylandSubsurface {
public:
- WaylandSubsurface(PlatformWindowDelegate* delegate,
- WaylandConnection* connection);
- ~WaylandSubsurface() override;
+ WaylandSubsurface(WaylandConnection* connection, WaylandWindow* parent);
+ WaylandSubsurface(const WaylandSubsurface&) = delete;
+ WaylandSubsurface& operator=(const WaylandSubsurface&) = delete;
+ ~WaylandSubsurface();
- // PlatformWindow overrides:
- void Show(bool inactive) override;
- void Hide() override;
- bool IsVisible() const override;
- void SetBounds(const gfx::Rect& bounds) override;
+ wl_surface* surface() const { return wayland_surface_.surface(); }
+ int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); }
+ WaylandSurface* wayland_surface() { return &wayland_surface_; }
+ gfx::Rect bounds_px() { return bounds_px_; }
+ bool IsOpaque() const { return !enable_blend_; }
- private:
- // WaylandWindow overrides:
- bool OnInitialize(PlatformWindowInitProperties properties) override;
+ gfx::AcceleratedWidget GetWidget() const;
+
+ // Sets up wl_surface and wl_subsurface. Allows an overlay to be shown
+ // correctly once a wl_buffer is attached.
+ void ConfigureAndShowSurface(gfx::OverlayTransform transform,
+ const gfx::Rect& bounds_rect,
+ bool enable_blend,
+ const WaylandSurface* reference_below,
+ const WaylandSurface* reference_above);
+
+ // Assigns wl_subsurface role to the wl_surface so it is visible when a
+ // wl_buffer is attached.
+ void Show();
+ // Remove wl_subsurface role to make this invisible.
+ void Hide();
+ bool IsVisible() const;
- // Creates (if necessary) and shows a subsurface window.
+ private:
+ // Helper of Show(). It does the role-assigning to wl_surface.
void CreateSubsurface();
+ void SetBounds(const gfx::Rect& bounds);
+
+ // Tells wayland compositor to update the opaque region according to
+ // |enable_blend_| and |bounds_px_|.
+ void UpdateOpaqueRegion();
+ WaylandSurface wayland_surface_;
wl::Object<wl_subsurface> subsurface_;
- DISALLOW_COPY_AND_ASSIGN(WaylandSubsurface);
+ WaylandConnection* const connection_;
+ // |parent_| refers to the WaylandWindow whose wl_surface is the parent to
+ // this subsurface.
+ WaylandWindow* const parent_;
+
+ // Pixel bounds within the display to position this subsurface.
+ gfx::Rect bounds_px_;
+ bool enable_blend_ = true;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
index c2cf21baf62..0300a7bcacc 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
@@ -4,21 +4,139 @@
#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace ui {
-WaylandSurface::WaylandSurface() = default;
-WaylandSurface::~WaylandSurface() = default;
+WaylandSurface::WaylandSurface(WaylandConnection* connection,
+ WaylandWindow* root_window)
+ : connection_(connection),
+ root_window_(root_window),
+ surface_(connection->CreateSurface()) {}
-gfx::AcceleratedWidget WaylandSurface::GetWidget() const {
+WaylandSurface::~WaylandSurface() {
+ if (surface_) {
+ wl_surface_add_listener(surface_.get(), nullptr, nullptr);
+ wl_surface_set_user_data(surface_.get(), nullptr);
+ }
+}
+
+uint32_t WaylandSurface::GetSurfaceId() const {
if (!surface_)
- return gfx::kNullAcceleratedWidget;
+ return 0u;
return surface_.id();
}
-gfx::AcceleratedWidget WaylandSurface::GetRootWidget() const {
+gfx::AcceleratedWidget WaylandSurface::GetWidget() const {
return root_window_->GetWidget();
}
+bool WaylandSurface::Initialize() {
+ if (!surface_)
+ return false;
+
+ wl_surface_set_user_data(surface_.get(), this);
+
+ static struct wl_surface_listener surface_listener = {
+ &WaylandSurface::Enter,
+ &WaylandSurface::Leave,
+ };
+ wl_surface_add_listener(surface_.get(), &surface_listener, this);
+
+ return true;
+}
+
+void WaylandSurface::AttachBuffer(wl_buffer* buffer) {
+ // The logic in DamageBuffer currently relies on attachment coordinates of
+ // (0, 0). If this changes, then the calculation in DamageBuffer will also
+ // need to be updated.
+ wl_surface_attach(surface_.get(), buffer, 0, 0);
+ connection_->ScheduleFlush();
+}
+
+void WaylandSurface::Damage(const gfx::Rect& pending_damage_region) {
+ if (connection_->compositor_version() >=
+ WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
+ // wl_surface_damage_buffer relies on compositor API version 4. See
+ // https://bit.ly/2u00lv6 for details.
+ // We don't need to apply any scaling because pending_damage_region is
+ // already in buffer coordinates.
+ wl_surface_damage_buffer(
+ surface_.get(), pending_damage_region.x(), pending_damage_region.y(),
+ pending_damage_region.width(), pending_damage_region.height());
+ } else {
+ // The calculation for damage region relies on two assumptions:
+ // 1) The buffer is always attached at surface location (0, 0)
+ // 2) The API wl_surface::set_buffer_transform is not used.
+ // It's possible to write logic that accounts for both cases above, but
+ // it's currently unnecessary.
+ //
+ // Note: The damage region may not be an integer multiple of scale. To
+ // keep the implementation simple, the x() and y() coordinates round down,
+ // and the width() and height() calculations always add an extra pixel.
+ wl_surface_damage(surface_.get(), pending_damage_region.x() / buffer_scale_,
+ pending_damage_region.y() / buffer_scale_,
+ pending_damage_region.width() / buffer_scale_ + 1,
+ pending_damage_region.height() / buffer_scale_ + 1);
+ }
+ connection_->ScheduleFlush();
+}
+
+void WaylandSurface::Commit() {
+ wl_surface_commit(surface_.get());
+ connection_->ScheduleFlush();
+}
+
+void WaylandSurface::SetBufferScale(int32_t new_scale, bool update_bounds) {
+ DCHECK_GT(new_scale, 0);
+
+ if (new_scale == buffer_scale_)
+ return;
+
+ buffer_scale_ = new_scale;
+ wl_surface_set_buffer_scale(surface_.get(), buffer_scale_);
+ connection_->ScheduleFlush();
+}
+
+void WaylandSurface::SetBounds(const gfx::Rect& bounds_px) {
+ // It's important to set opaque region for opaque windows (provides
+ // optimization hint for the Wayland compositor).
+ if (!root_window_->IsOpaqueWindow())
+ return;
+
+ wl::Object<wl_region> region(
+ wl_compositor_create_region(connection_->compositor()));
+ wl_region_add(region.get(), 0, 0, bounds_px.width(), bounds_px.height());
+
+ wl_surface_set_opaque_region(surface_.get(), region.get());
+
+ connection_->ScheduleFlush();
+}
+
+wl::Object<wl_subsurface> WaylandSurface::CreateSubsurface(
+ WaylandSurface* parent) {
+ DCHECK(parent);
+ wl_subcompositor* subcompositor = connection_->subcompositor();
+ DCHECK(subcompositor);
+ wl::Object<wl_subsurface> subsurface(wl_subcompositor_get_subsurface(
+ subcompositor, surface_.get(), parent->surface_.get()));
+ return subsurface;
+}
+
+// static
+void WaylandSurface::Enter(void* data,
+ struct wl_surface* wl_surface,
+ struct wl_output* output) {
+ static_cast<WaylandSurface*>(data)->root_window_->AddEnteredOutputId(output);
+}
+
+// static
+void WaylandSurface::Leave(void* data,
+ struct wl_surface* wl_surface,
+ struct wl_output* output) {
+ static_cast<WaylandSurface*>(data)->root_window_->RemoveEnteredOutputId(
+ output);
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
index e432ceb7e7d..37755f9ac3d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
@@ -5,17 +5,21 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_
+#include <cstdint>
+
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
namespace ui {
+class WaylandConnection;
class WaylandWindow;
// Wrapper of a wl_surface, owned by a WaylandWindow or a WlSubsurface.
class WaylandSurface {
public:
- WaylandSurface();
+ WaylandSurface(WaylandConnection* connection, WaylandWindow* root_window);
WaylandSurface(const WaylandSurface&) = delete;
WaylandSurface& operator=(const WaylandSurface&) = delete;
~WaylandSurface();
@@ -23,20 +27,55 @@ class WaylandSurface {
WaylandWindow* root_window() const { return root_window_; }
wl_surface* surface() const { return surface_.get(); }
int32_t buffer_scale() const { return buffer_scale_; }
+ void set_buffer_scale(int32_t scale) { buffer_scale_ = scale; }
- // gfx::AcceleratedWidget identifies a wl_surface or a ui::WaylandWindow. Note
- // that GetWidget() and GetRootWidget() do not necessarily return the same
- // result.
+ // Returns an id that identifies the |wl_surface_|.
+ uint32_t GetSurfaceId() const;
+ // Returns a gfx::AcceleratedWidget that identifies the WaylandWindow that
+ // this WaylandSurface belongs to.
gfx::AcceleratedWidget GetWidget() const;
- gfx::AcceleratedWidget GetRootWidget() const;
+
+ // Initializes the WaylandSurface and returns true iff success.
+ // This may return false if a wl_surface could not be created, for example.
+ bool Initialize();
+
+ // Attaches the given wl_buffer to the underlying wl_surface at (0, 0).
+ void AttachBuffer(wl_buffer* buffer);
+
+ // Damages the surface according to |pending_damage_region|, which should be
+ // in surface coordinates (dp).
+ void Damage(const gfx::Rect& pending_damage_region);
+
+ // Commits the underlying wl_surface.
+ void Commit();
+
+ // Sets the buffer scale for this surface.
+ void SetBufferScale(int32_t scale, bool update_bounds);
+
+ // Sets the bounds on this surface. This is used for determining the opaque
+ // region.
+ void SetBounds(const gfx::Rect& bounds_px);
+
+ // Creates a wl_subsurface relating this surface and a parent surface,
+ // |parent|. Callers take ownership of the wl_subsurface.
+ wl::Object<wl_subsurface> CreateSubsurface(WaylandSurface* parent);
private:
- WaylandWindow* root_window_ = nullptr;
+ WaylandConnection* const connection_;
+ WaylandWindow* const root_window_;
wl::Object<wl_surface> surface_;
+
// Wayland's scale factor for the output that this window currently belongs
// to.
int32_t buffer_scale_ = 1;
- friend class WaylandWindow;
+
+ // wl_surface_listener
+ static void Enter(void* data,
+ struct wl_surface* wl_surface,
+ struct wl_output* output);
+ static void Leave(void* data,
+ struct wl_surface* wl_surface,
+ struct wl_output* output);
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 10ecbcf7b47..86bab661269 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -4,6 +4,9 @@
#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
+#include "base/run_loop.h"
+#include "base/unguessable_token.h"
+#include "build/lacros_buildflags.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/hit_test.h"
@@ -14,8 +17,10 @@
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/extensions/wayland_extension.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
namespace ui {
@@ -36,6 +41,7 @@ WaylandToplevelWindow::~WaylandToplevelWindow() {
drag_handler_delegate_->OnDragFinished(
DragDropTypes::DragOperation::DRAG_NONE);
}
+ CancelDrag();
}
bool WaylandToplevelWindow::CreateShellSurface() {
@@ -46,7 +52,11 @@ bool WaylandToplevelWindow::CreateShellSurface() {
return false;
}
- shell_surface_->SetAppId(app_id_);
+#if BUILDFLAG(IS_LACROS)
+ shell_surface_->SetAppId(window_unique_id_);
+#else
+ shell_surface_->SetAppId(wm_class_class_);
+#endif
shell_surface_->SetTitle(window_title_);
SetSizeConstraints();
TriggerStateChanges();
@@ -78,13 +88,29 @@ void WaylandToplevelWindow::DispatchHostWindowDragMovement(
connection()->ScheduleFlush();
}
-void WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data,
+bool WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data,
int operation,
gfx::NativeCursor cursor,
+ bool can_grab_pointer,
WmDragHandler::Delegate* delegate) {
DCHECK(!drag_handler_delegate_);
drag_handler_delegate_ = delegate;
connection()->data_drag_controller()->StartSession(data, operation);
+
+ base::RunLoop drag_loop(base::RunLoop::Type::kNestableTasksAllowed);
+ drag_loop_quit_closure_ = drag_loop.QuitClosure();
+
+ auto alive = weak_ptr_factory_.GetWeakPtr();
+ drag_loop.Run();
+ if (!alive)
+ return false;
+ return true;
+}
+
+void WaylandToplevelWindow::CancelDrag() {
+ if (drag_loop_quit_closure_.is_null())
+ return;
+ std::move(drag_loop_quit_closure_).Run();
}
void WaylandToplevelWindow::Show(bool inactive) {
@@ -113,7 +139,7 @@ void WaylandToplevelWindow::Hide() {
// Detach buffer from surface in order to completely shutdown menus and
// tooltips, and release resources.
- connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget());
+ connection()->buffer_manager_host()->ResetSurfaceContents(root_surface());
}
bool WaylandToplevelWindow::IsVisible() const {
@@ -182,6 +208,14 @@ void WaylandToplevelWindow::SizeConstraintsChanged() {
SetSizeConstraints();
}
+std::string WaylandToplevelWindow::GetWindowUniqueId() const {
+#if BUILDFLAG(IS_LACROS)
+ return window_unique_id_;
+#else
+ return std::string();
+#endif
+}
+
void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width,
int32_t height,
bool is_maximized,
@@ -254,9 +288,11 @@ void WaylandToplevelWindow::OnDragEnter(const gfx::PointF& point,
// Wayland sends locations in DIP so they need to be translated to
// physical pixels.
+ // TODO(crbug.com/1102857): get the real event modifier here.
drop_handler->OnDragEnter(
gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data),
- operation);
+ operation,
+ /*modifiers=*/0);
}
int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point,
@@ -267,15 +303,18 @@ int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point,
// Wayland sends locations in DIP so they need to be translated to
// physical pixels.
+ // TODO(crbug.com/1102857): get the real event modifier here.
return drop_handler->OnDragMotion(
- gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation);
+ gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation,
+ /*modifiers=*/0);
}
void WaylandToplevelWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) {
WmDropHandler* drop_handler = GetWmDropHandler(*this);
if (!drop_handler)
return;
- drop_handler->OnDragDrop(std::move(data));
+ // TODO(crbug.com/1102857): get the real event modifier here.
+ drop_handler->OnDragDrop(std::move(data), /*modifiers=*/0);
}
void WaylandToplevelWindow::OnDragLeave() {
@@ -290,11 +329,18 @@ void WaylandToplevelWindow::OnDragSessionClose(uint32_t dnd_action) {
drag_handler_delegate_->OnDragFinished(dnd_action);
drag_handler_delegate_ = nullptr;
connection()->event_source()->ResetPointerFlags();
+ std::move(drag_loop_quit_closure_).Run();
}
bool WaylandToplevelWindow::OnInitialize(
PlatformWindowInitProperties properties) {
- app_id_ = properties.wm_class_class;
+#if BUILDFLAG(IS_LACROS)
+ auto token = base::UnguessableToken::Create();
+ window_unique_id_ = "org.chromium.lacros." + token.ToString();
+#else
+ wm_class_class_ = properties.wm_class_class;
+#endif
+ SetWaylandExtension(this, static_cast<WaylandExtension*>(this));
SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this));
return true;
}
@@ -309,6 +355,11 @@ void WaylandToplevelWindow::EndMoveLoop() {
connection()->window_drag_controller()->StopDragging();
}
+void WaylandToplevelWindow::StartWindowDraggingSessionIfNeeded() {
+ DCHECK(connection()->window_drag_controller());
+ connection()->window_drag_controller()->StartDragSession();
+}
+
void WaylandToplevelWindow::TriggerStateChanges() {
if (!shell_surface_)
return;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index 0455078ee38..4c0a6a7d013 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -5,11 +5,13 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_
+#include "build/lacros_buildflags.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/platform_window/extensions/wayland_extension.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_move_loop_handler.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
namespace ui {
@@ -18,7 +20,8 @@ class ShellSurfaceWrapper;
class WaylandToplevelWindow : public WaylandWindow,
public WmMoveResizeHandler,
public WmDragHandler,
- public WmMoveLoopHandler {
+ public WmMoveLoopHandler,
+ public WaylandExtension {
public:
WaylandToplevelWindow(PlatformWindowDelegate* delegate,
WaylandConnection* connection);
@@ -38,10 +41,12 @@ class WaylandToplevelWindow : public WaylandWindow,
const gfx::Point& pointer_location_in_px) override;
// WmDragHandler
- void StartDrag(const ui::OSExchangeData& data,
+ bool StartDrag(const ui::OSExchangeData& data,
int operation,
gfx::NativeCursor cursor,
+ bool can_grab_pointer,
WmDragHandler::Delegate* delegate) override;
+ void CancelDrag() override;
// PlatformWindow
void Show(bool inactive) override;
@@ -54,6 +59,7 @@ class WaylandToplevelWindow : public WaylandWindow,
void Restore() override;
PlatformWindowState GetPlatformWindowState() const override;
void SizeConstraintsChanged() override;
+ std::string GetWindowUniqueId() const override;
private:
// WaylandWindow overrides:
@@ -75,6 +81,9 @@ class WaylandToplevelWindow : public WaylandWindow,
bool RunMoveLoop(const gfx::Vector2d& drag_offset) override;
void EndMoveLoop() override;
+ // WaylandExtension:
+ void StartWindowDraggingSessionIfNeeded() override;
+
void TriggerStateChanges();
void SetWindowState(PlatformWindowState state);
@@ -112,11 +121,17 @@ class WaylandToplevelWindow : public WaylandWindow,
bool is_active_ = false;
+#if BUILDFLAG(IS_LACROS)
+ // Unique ID for this window. May be shared over non-Wayland IPC transports
+ // (e.g. mojo) to identify the window.
+ std::string window_unique_id_;
+#else
// Id of the chromium app passed through
// PlatformWindowInitProperties::wm_class_class. This is used by Wayland
// compositor to identify the app, unite it's windows into the same stack of
// windows and find *.desktop file to set various preferences including icons.
- std::string app_id_;
+ std::string wm_class_class_;
+#endif
// Title of the ShellSurface.
base::string16 window_title_;
@@ -124,6 +139,10 @@ class WaylandToplevelWindow : public WaylandWindow,
// Max and min sizes of the WaylandToplevelWindow window.
base::Optional<gfx::Size> min_size_;
base::Optional<gfx::Size> max_size_;
+
+ base::OnceClosure drag_loop_quit_closure_;
+
+ base::WeakPtrFactory<WaylandToplevelWindow> weak_ptr_factory_{this};
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc
index aff5c24befa..9298a43d67d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_touch.cc
@@ -8,6 +8,7 @@
#include "base/time/time.h"
#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -47,7 +48,7 @@ void WaylandTouch::Down(void* data,
DCHECK(touch);
touch->connection_->set_serial(serial);
- WaylandWindow* window = WaylandWindow::FromSurface(surface);
+ WaylandWindow* window = wl::RootWindowFromWlSurface(surface);
gfx::PointF location(wl_fixed_to_double(x), wl_fixed_to_double(y));
base::TimeTicks timestamp =
base::TimeTicks() + base::TimeDelta::FromMilliseconds(time);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
index 21ebb192e5a..2e436462afb 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -6,6 +6,7 @@
#include <wayland-client.h>
+#include <algorithm>
#include <memory>
#include "base/bind.h"
@@ -21,30 +22,44 @@
#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
+#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
+#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom.h"
+
+namespace {
+
+bool OverlayStackOrderCompare(
+ const ui::ozone::mojom::WaylandOverlayConfigPtr& i,
+ const ui::ozone::mojom::WaylandOverlayConfigPtr& j) {
+ return i->z_order < j->z_order;
+}
+
+} // namespace
namespace ui {
WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate,
WaylandConnection* connection)
- : delegate_(delegate), connection_(connection) {}
+ : delegate_(delegate),
+ connection_(connection),
+ accelerated_widget_(
+ connection->wayland_window_manager()->AllocateAcceleratedWidget()) {}
WaylandWindow::~WaylandWindow() {
+ shutting_down_ = true;
+
PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
- if (surface())
+
+ for (const auto& widget_subsurface : wayland_subsurfaces()) {
+ connection_->wayland_window_manager()->RemoveSubsurface(
+ GetWidget(), widget_subsurface.get());
+ }
+ if (root_surface_)
connection_->wayland_window_manager()->RemoveWindow(GetWidget());
if (parent_window_)
parent_window_->set_child_window(nullptr);
}
-// static
-WaylandWindow* WaylandWindow::FromSurface(wl_surface* surface) {
- if (!surface)
- return nullptr;
- return static_cast<WaylandWindow*>(
- wl_proxy_get_user_data(reinterpret_cast<wl_proxy*>(surface)));
-}
-
void WaylandWindow::OnWindowLostCapture() {
delegate_->OnLostCapture();
}
@@ -76,12 +91,17 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) {
else
ui_scale_ = display.device_scale_factor();
}
- SetBufferScale(new_scale, update_bounds);
+ // At this point, buffer_scale() still returns the old scale.
+ if (update_bounds)
+ SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / buffer_scale()));
+
+ root_surface_->SetBufferScale(new_scale, update_bounds);
}
gfx::AcceleratedWidget WaylandWindow::GetWidget() const {
- return wayland_surface_.GetWidget();
+ return accelerated_widget_;
}
+
void WaylandWindow::SetPointerFocus(bool focus) {
has_pointer_focus_ = focus;
@@ -116,10 +136,7 @@ void WaylandWindow::SetBounds(const gfx::Rect& bounds_px) {
return;
bounds_px_ = bounds_px;
- // Opaque region is based on the size of the window. Thus, update the region
- // on each update.
- MaybeUpdateOpaqueRegion();
-
+ root_surface_->SetBounds(bounds_px);
delegate_->OnBoundsChanged(bounds_px_);
}
@@ -316,24 +333,19 @@ void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) {
}
bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) {
+ root_surface_ = std::make_unique<WaylandSurface>(connection_, this);
+ if (!root_surface_->Initialize()) {
+ LOG(ERROR) << "Failed to create wl_surface";
+ return false;
+ }
+
// Properties contain DIP bounds but the buffer scale is initially 1 so it's
// OK to assign. The bounds will be recalculated when the buffer scale
// changes.
- DCHECK_EQ(buffer_scale(), 1);
bounds_px_ = properties.bounds;
opacity_ = properties.opacity;
type_ = properties.type;
- wayland_surface_.surface_.reset(
- wl_compositor_create_surface(connection_->compositor()));
- wayland_surface_.root_window_ = this;
- if (!surface()) {
- LOG(ERROR) << "Failed to create wl_surface";
- return false;
- }
- wl_surface_set_user_data(surface(), this);
- AddSurfaceListener();
-
connection_->wayland_window_manager()->AddWindow(GetWidget(), this);
if (!OnInitialize(std::move(properties)))
@@ -346,27 +358,11 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) {
// Will do nothing for menus because they have got their scale above.
UpdateBufferScale(false);
+ root_surface_->SetBounds(bounds_px_);
- MaybeUpdateOpaqueRegion();
return true;
}
-void WaylandWindow::SetBufferScale(int32_t new_scale, bool update_bounds) {
- DCHECK_GT(new_scale, 0);
-
- if (new_scale == buffer_scale())
- return;
-
- auto old_scale = buffer_scale();
- wayland_surface_.buffer_scale_ = new_scale;
- if (update_bounds)
- SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / old_scale));
-
- DCHECK(surface());
- wl_surface_set_buffer_scale(surface(), buffer_scale());
- connection_->ScheduleFlush();
-}
-
WaylandWindow* WaylandWindow::GetParentWindow(
gfx::AcceleratedWidget parent_widget) {
auto* parent_window =
@@ -393,14 +389,6 @@ WaylandWindow* WaylandWindow::GetRootParentWindow() {
return parent_window_ ? parent_window_->GetRootParentWindow() : this;
}
-void WaylandWindow::AddSurfaceListener() {
- static struct wl_surface_listener surface_listener = {
- &WaylandWindow::Enter,
- &WaylandWindow::Leave,
- };
- wl_surface_add_listener(surface(), &surface_listener, this);
-}
-
void WaylandWindow::AddEnteredOutputId(struct wl_output* output) {
// Wayland does weird things for menus so instead of tracking outputs that
// we entered or left, we take that from the parent window and ignore this
@@ -477,18 +465,6 @@ WaylandWindow* WaylandWindow::GetTopMostChildWindow() {
return child_window_ ? child_window_->GetTopMostChildWindow() : this;
}
-void WaylandWindow::MaybeUpdateOpaqueRegion() {
- if (!IsOpaqueWindow())
- return;
-
- wl::Object<wl_region> region(
- wl_compositor_create_region(connection_->compositor()));
- wl_region_add(region.get(), 0, 0, bounds_px_.width(), bounds_px_.height());
- wl_surface_set_opaque_region(surface(), region.get());
-
- connection_->ScheduleFlush();
-}
-
bool WaylandWindow::IsOpaqueWindow() const {
return opacity_ == ui::PlatformWindowOpacity::kOpaqueWindow;
}
@@ -505,26 +481,145 @@ uint32_t WaylandWindow::DispatchEventToDelegate(
return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE;
}
-// static
-void WaylandWindow::Enter(void* data,
- struct wl_surface* wl_surface,
- struct wl_output* output) {
- auto* window = static_cast<WaylandWindow*>(data);
- if (window) {
- DCHECK(window->surface() == wl_surface);
- window->AddEnteredOutputId(output);
+std::unique_ptr<WaylandSurface> WaylandWindow::TakeWaylandSurface() {
+ DCHECK(shutting_down_);
+ DCHECK(root_surface_);
+ return std::move(root_surface_);
+}
+
+bool WaylandWindow::RequestSubsurface() {
+ auto subsurface = std::make_unique<WaylandSubsurface>(connection_, this);
+ if (!subsurface->surface())
+ return false;
+ connection_->wayland_window_manager()->AddSubsurface(GetWidget(),
+ subsurface.get());
+ subsurface_stack_above_.push_back(subsurface.get());
+ auto result = wayland_subsurfaces_.emplace(std::move(subsurface));
+ DCHECK(result.second);
+ return true;
+}
+
+bool WaylandWindow::ArrangeSubsurfaceStack(size_t above, size_t below) {
+ while (wayland_subsurfaces_.size() < above + below) {
+ if (!RequestSubsurface())
+ return false;
}
+
+ DCHECK(subsurface_stack_below_.size() + subsurface_stack_above_.size() >=
+ above + below);
+
+ if (subsurface_stack_above_.size() < above) {
+ auto splice_start = subsurface_stack_below_.begin();
+ for (size_t i = 0; i < below; ++i)
+ ++splice_start;
+ subsurface_stack_above_.splice(subsurface_stack_above_.end(),
+ subsurface_stack_below_, splice_start,
+ subsurface_stack_below_.end());
+
+ } else if (subsurface_stack_below_.size() < below) {
+ auto splice_start = subsurface_stack_above_.end();
+ for (size_t i = 0; i < below - subsurface_stack_below_.size(); ++i)
+ --splice_start;
+ subsurface_stack_below_.splice(subsurface_stack_below_.end(),
+ subsurface_stack_above_, splice_start,
+ subsurface_stack_above_.end());
+ }
+
+ DCHECK(subsurface_stack_below_.size() >= below);
+ DCHECK(subsurface_stack_above_.size() >= above);
+ return true;
}
-// static
-void WaylandWindow::Leave(void* data,
- struct wl_surface* wl_surface,
- struct wl_output* output) {
- auto* window = static_cast<WaylandWindow*>(data);
- if (window) {
- DCHECK(window->surface() == wl_surface);
- window->RemoveEnteredOutputId(output);
+bool WaylandWindow::CommitOverlays(
+ std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr>& overlays) {
+ // |overlays| is sorted from bottom to top.
+ std::sort(overlays.begin(), overlays.end(), OverlayStackOrderCompare);
+
+ // Find the location where z_oder becomes non-negative.
+ ozone::mojom::WaylandOverlayConfigPtr value =
+ ozone::mojom::WaylandOverlayConfig::New();
+ auto split = std::lower_bound(overlays.begin(), overlays.end(), value,
+ OverlayStackOrderCompare);
+ CHECK((*split)->z_order >= 0);
+ size_t num_primary_planes = (*split)->z_order == 0 ? 1 : 0;
+
+ size_t above = (overlays.end() - split) - num_primary_planes;
+ size_t below = split - overlays.begin();
+ // Re-arrange the list of subsurfaces to fit the |overlays|. Request extra
+ // subsurfaces if needed.
+ if (!ArrangeSubsurfaceStack(above, below))
+ return false;
+
+ {
+ // Iterate through |subsurface_stack_below_|, setup subsurfaces and place
+ // them in corresponding order. Commit wl_buffers once a subsurface is
+ // configured.
+ auto overlay_iter = split - 1;
+ for (auto iter = subsurface_stack_below_.begin();
+ iter != subsurface_stack_below_.end(); ++iter, --overlay_iter) {
+ if (overlay_iter >= overlays.begin()) {
+ WaylandSurface* reference_above = nullptr;
+ if (overlay_iter == split - 1) {
+ // It's possible that |overlays| does not contain primary plane, we
+ // still want to place relative to the surface with z_order=0.
+ reference_above = root_surface();
+ } else {
+ reference_above = (*std::next(iter))->wayland_surface();
+ }
+ (*iter)->ConfigureAndShowSurface(
+ (*overlay_iter)->transform, (*overlay_iter)->bounds_rect,
+ (*overlay_iter)->enable_blend, nullptr, reference_above);
+ connection_->buffer_manager_host()->CommitBufferInternal(
+ (*iter)->wayland_surface(), (*overlay_iter)->buffer_id,
+ gfx::Rect());
+ } else {
+ // If there're more subsurfaces requested that we don't need at the
+ // moment, hide them.
+ (*iter)->Hide();
+ }
+ }
+
+ // Iterate through |subsurface_stack_above_|, setup subsurfaces and place
+ // them in corresponding order. Commit wl_buffers once a subsurface is
+ // configured.
+ overlay_iter = split + num_primary_planes;
+ for (auto iter = subsurface_stack_above_.begin();
+ iter != subsurface_stack_above_.end(); ++iter, ++overlay_iter) {
+ if (overlay_iter < overlays.end()) {
+ WaylandSurface* reference_below = nullptr;
+ if (overlay_iter == split + num_primary_planes) {
+ // It's possible that |overlays| does not contain primary plane, we
+ // still want to place relative to the surface with z_order=0.
+ reference_below = root_surface();
+ } else {
+ reference_below = (*std::prev(iter))->wayland_surface();
+ }
+ (*iter)->ConfigureAndShowSurface(
+ (*overlay_iter)->transform, (*overlay_iter)->bounds_rect,
+ (*overlay_iter)->enable_blend, reference_below, nullptr);
+ connection_->buffer_manager_host()->CommitBufferInternal(
+ (*iter)->wayland_surface(), (*overlay_iter)->buffer_id,
+ gfx::Rect());
+ } else {
+ // If there're more subsurfaces requested that we don't need at the
+ // moment, hide them.
+ (*iter)->Hide();
+ }
+ }
}
+
+ if (num_primary_planes) {
+ // TODO: forward fence.
+ connection_->buffer_manager_host()->CommitBufferInternal(
+ root_surface(), (*split)->buffer_id, (*split)->damage_region);
+ } else {
+ // Subsurfaces are set to desync, above operations will only take effects
+ // when root_surface is committed.
+ root_surface()->Commit();
+ }
+
+ // commit all;
+ return true;
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h
index 9c42eb59c80..cd9f1e9fb35 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h
@@ -5,6 +5,7 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_H_
+#include <list>
#include <memory>
#include <set>
#include <vector>
@@ -18,6 +19,7 @@
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom-forward.h"
#include "ui/platform_window/platform_window.h"
#include "ui/platform_window/platform_window_delegate.h"
#include "ui/platform_window/platform_window_init_properties.h"
@@ -31,20 +33,22 @@ namespace ui {
class BitmapCursorOzone;
class OSExchangeData;
class WaylandConnection;
+class WaylandSubsurface;
+class WaylandWindowDragController;
+
+using WidgetSubsurfaceSet = base::flat_set<std::unique_ptr<WaylandSubsurface>>;
class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
public:
~WaylandWindow() override;
// A factory method that can create any of the derived types of WaylandWindow
- // (WaylandToplevelWindow, WaylandPopup and WaylandSubsurface).
+ // (WaylandToplevelWindow, WaylandPopup and WaylandAuxiliaryWindow).
static std::unique_ptr<WaylandWindow> Create(
PlatformWindowDelegate* delegate,
WaylandConnection* connection,
PlatformWindowInitProperties properties);
- static WaylandWindow* FromSurface(wl_surface* surface);
-
void OnWindowLostCapture();
// Updates the surface buffer scale of the window. Top level windows take
@@ -54,8 +58,10 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// to do so (this is not needed upon window initialization).
void UpdateBufferScale(bool update_bounds);
- WaylandSurface* wayland_surface() { return &wayland_surface_; }
- wl_surface* surface() const { return wayland_surface_.surface(); }
+ WaylandSurface* root_surface() const { return root_surface_.get(); }
+ const WidgetSubsurfaceSet& wayland_subsurfaces() const {
+ return wayland_subsurfaces_;
+ }
void set_parent_window(WaylandWindow* parent_window) {
parent_window_ = parent_window;
@@ -64,6 +70,16 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
gfx::AcceleratedWidget GetWidget() const;
+ // Creates a WaylandSubsurface to put into |wayland_subsurfaces_|. Called if
+ // more subsurfaces are needed when a frame arrives.
+ bool RequestSubsurface();
+ // Re-arrange the |subsurface_stack_above_| and |subsurface_stack_below_| s.t.
+ // subsurface_stack_above_.size() >= above and
+ // subsurface_stack_below_.size() >= below.
+ bool ArrangeSubsurfaceStack(size_t above, size_t below);
+ bool CommitOverlays(
+ std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr>& overlays);
+
// Set whether this window has pointer focus and should dispatch mouse events.
void SetPointerFocus(bool focus);
bool has_pointer_focus() const { return has_pointer_focus_; }
@@ -82,7 +98,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
void set_child_window(WaylandWindow* window) { child_window_ = window; }
WaylandWindow* child_window() const { return child_window_; }
- int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); }
+ int32_t buffer_scale() const { return root_surface_->buffer_scale(); }
int32_t ui_scale() const { return ui_scale_; }
const base::flat_set<uint32_t>& entered_outputs_ids() const {
@@ -145,8 +161,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
virtual void OnDragEnter(const gfx::PointF& point,
std::unique_ptr<OSExchangeData> data,
int operation);
- virtual int OnDragMotion(const gfx::PointF& point,
- int operation);
+ virtual int OnDragMotion(const gfx::PointF& point, int operation);
virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data);
virtual void OnDragLeave();
virtual void OnDragSessionClose(uint32_t dnd_action);
@@ -157,6 +172,17 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// Returns a top most child window within the same hierarchy.
WaylandWindow* GetTopMostChildWindow();
+ // This should be called when a WaylandSurface part of this window becomes
+ // partially or fully within the scanout region of |output|.
+ void AddEnteredOutputId(struct wl_output* output);
+
+ // This should be called when a WaylandSurface part of this window becomes
+ // fully outside of the scanout region of |output|.
+ void RemoveEnteredOutputId(struct wl_output* output);
+
+ // Returns true iff this window is opaque.
+ bool IsOpaqueWindow() const;
+
protected:
WaylandWindow(PlatformWindowDelegate* delegate,
WaylandConnection* connection);
@@ -170,10 +196,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// Gets a parent window for this window.
WaylandWindow* GetParentWindow(gfx::AcceleratedWidget parent_widget);
- // Sets the buffer scale.
- void SetBufferScale(int32_t scale, bool update_bounds);
-
- // Sets the ui scale.
void set_ui_scale(int32_t ui_scale) { ui_scale_ = ui_scale; }
private:
@@ -182,41 +204,38 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// Initializes the WaylandWindow with supplied properties.
bool Initialize(PlatformWindowInitProperties properties);
- // Install a surface listener and start getting wl_output enter/leave events.
- void AddSurfaceListener();
-
- void AddEnteredOutputId(struct wl_output* output);
- void RemoveEnteredOutputId(struct wl_output* output);
-
void UpdateCursorPositionFromEvent(std::unique_ptr<Event> event);
WaylandWindow* GetTopLevelWindow();
- // It's important to set opaque region for opaque windows (provides
- // optimization hint for the Wayland compositor).
- void MaybeUpdateOpaqueRegion();
-
- bool IsOpaqueWindow() const;
-
uint32_t DispatchEventToDelegate(const PlatformEvent& native_event);
// Additional initialization of derived classes.
virtual bool OnInitialize(PlatformWindowInitProperties properties) = 0;
- // wl_surface_listener
- static void Enter(void* data,
- struct wl_surface* wl_surface,
- struct wl_output* output);
- static void Leave(void* data,
- struct wl_surface* wl_surface,
- struct wl_output* output);
+ // WaylandWindowDragController might need to take ownership of the wayland
+ // surface whether the window that originated the DND session gets destroyed
+ // in the middle of that session (e.g: when it is snapped into a tab strip).
+ // Surface ownership is allowed to be taken only when the window is under
+ // destruction, i.e: |shutting_down_| is set. This can be done, for example,
+ // by implementing |WaylandWindowObserver::OnWindowRemoved|.
+ friend WaylandWindowDragController;
+ std::unique_ptr<WaylandSurface> TakeWaylandSurface();
PlatformWindowDelegate* delegate_;
WaylandConnection* connection_;
WaylandWindow* parent_window_ = nullptr;
WaylandWindow* child_window_ = nullptr;
- WaylandSurface wayland_surface_;
+ std::unique_ptr<WaylandSurface> root_surface_;
+ WidgetSubsurfaceSet wayland_subsurfaces_;
+
+ // The stack of sub-surfaces to take effect when Commit() is called.
+ // |subsurface_stack_above_| refers to subsurfaces that are stacked above the
+ // parent.
+ // Subsurface at the front of the list is the closest to the parent.
+ std::list<WaylandSubsurface*> subsurface_stack_above_;
+ std::list<WaylandSubsurface*> subsurface_stack_below_;
// The current cursor bitmap (immutable).
scoped_refptr<BitmapCursorOzone> bitmap_;
@@ -250,6 +269,12 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// The type of the current WaylandWindow object.
ui::PlatformWindowType type_ = ui::PlatformWindowType::kWindow;
+ // Set when the window enters in shutdown process.
+ bool shutting_down_ = false;
+
+ // AcceleratedWidget for this window. This will be unique even over time.
+ gfx::AcceleratedWidget accelerated_widget_;
+
DISALLOW_COPY_AND_ASSIGN(WaylandWindow);
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
index 08d5cb633ca..64ffd8b4845 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
@@ -12,9 +12,9 @@
#include "base/callback.h"
#include "base/check.h"
#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/notreached.h"
#include "base/run_loop.h"
+#include "base/task/current_thread.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
@@ -64,19 +64,48 @@ WaylandWindowDragController::WaylandWindowDragController(
WaylandWindowDragController::~WaylandWindowDragController() = default;
-bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window,
- const gfx::Vector2d& offset) {
- DCHECK_LE(state_, State::kAttached);
- DCHECK(window);
+bool WaylandWindowDragController::StartDragSession() {
+ if (state_ != State::kIdle)
+ return true;
- if (!OfferWindow())
+ origin_window_ = window_manager_->GetCurrentFocusedWindow();
+ if (!origin_window_) {
+ LOG(ERROR) << "Failed to get origin window.";
return false;
+ }
+
+ VLOG(1) << "Starting DND session.";
+ state_ = State::kAttached;
+
+ DCHECK(!data_source_);
+ data_source_ = data_device_manager_->CreateSource(this);
+ data_source_->Offer({kMimeTypeChromiumWindow});
+ data_source_->SetAction(DragDropTypes::DRAG_MOVE);
+
+ // TODO(crbug.com/1099418): Use dragged window's surface as icon surface
+ // once "immediate drag" protocol extensions are available.
+ data_device_->StartDrag(*data_source_, *origin_window_,
+ /*icon_surface=*/nullptr, this);
+
+ pointer_grab_owner_ = origin_window_;
+
+ // Observe window so we can take ownership of the origin surface in case it
+ // is destroyed during the DND session.
+ window_manager_->AddObserver(this);
+ return true;
+}
+bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window,
+ const gfx::Vector2d& offset) {
DCHECK_EQ(state_, State::kAttached);
+ DCHECK(window);
dragged_window_ = window;
drag_offset_ = offset;
+
RunLoop();
+ dragged_window_ = nullptr;
+
DCHECK(state_ == State::kAttached || state_ == State::kDropped);
bool dropped = state_ == State::kDropped;
if (dropped)
@@ -91,9 +120,11 @@ void WaylandWindowDragController::StopDragging() {
VLOG(1) << "End drag loop requested. state=" << state_;
// This function is supposed to be called to indicate that the window was just
- // snapped into a tab strip. So switch to |kAttached| state and ask to quit
- // the nested loop.
+ // snapped into a tab strip. So switch to |kAttached| state, store the focused
+ // window as the pointer grabber and ask to quit the nested loop.
state_ = State::kAttached;
+ pointer_grab_owner_ = window_manager_->GetCurrentFocusedWindow();
+ DCHECK(pointer_grab_owner_);
QuitLoop();
}
@@ -118,6 +149,8 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window,
uint32_t serial) {
DCHECK_GE(state_, State::kAttached);
DCHECK(window);
+ DCHECK(data_source_);
+ DCHECK(data_offer_);
// Forward focus change event to the input delegate, so other components, such
// as WaylandScreen, are able to properly retrieve focus related info during
@@ -126,8 +159,13 @@ void WaylandWindowDragController::OnDragEnter(WaylandWindow* window,
VLOG(1) << "OnEnter. widget=" << window->GetWidget();
+ // TODO(crbug.com/1102946): Exo does not support custom mime types. In this
+ // case, |data_offer_| will hold an empty mime_types list and, at this point,
+ // it's safe just to skip the offer checks and requests here.
+ if (data_offer_->mime_types().empty())
+ return;
+
// Ensure this is a valid "window drag" offer.
- DCHECK(data_offer_);
DCHECK_EQ(data_offer_->mime_types().size(), 1u);
DCHECK_EQ(data_offer_->mime_types().front(), kMimeTypeChromiumWindow);
@@ -161,16 +199,39 @@ void WaylandWindowDragController::OnDragLeave() {
// which would require hacky workarounds in HandleDropAndResetState function
// to properly detect and handle such cases.
- VLOG(1) << "OnLeave";
+ if (!data_offer_)
+ return;
+ VLOG(1) << "OnLeave";
data_offer_.reset();
+
+ // As Wayland clients are only aware of surface-local coordinates and there is
+ // no implicit grab during DND sessions, a fake motion event with negative
+ // coordinates must be used here to make it possible for higher level UI
+ // components to detect when a window should be detached. E.g: On Chrome,
+ // dragging a tab all the way up to the top edge of the window won't work
+ // without this fake motion event upon wl_data_device::leave events.
+ if (state_ == State::kAttached)
+ pointer_delegate_->OnPointerMotionEvent({-1, -1});
}
void WaylandWindowDragController::OnDragDrop() {
- // Not used for window dragging sessions. Handling of drop events is fully
- // done at OnDataSourceFinish function, i.e: wl_data_source::{cancel,finish}.
+ DCHECK_GE(state_, State::kAttached);
+ VLOG(1) << "Dropped. state=" << state_;
+
+ // Some compositors, e.g: Exo, may delay the wl_data_source::cancelled event
+ // delivery for some seconds, when the drop happens within a toplevel surface.
+ // Such event is handled by OnDataSourceFinish() function below, which is the
+ // single entry point for the drop event in window drag controller. In order
+ // to prevent such delay, the current data offer must be destroyed here.
+ DCHECK(data_offer_);
+ data_offer_.reset();
}
+// This function is called when either 'cancelled' or 'finished' data source
+// events is received during a window dragging session. It is used to detect
+// when drop happens, since it is the only event sent by the server regardless
+// where it happens, inside or outside toplevel surfaces.
void WaylandWindowDragController::OnDataSourceFinish(bool completed) {
DCHECK_GE(state_, State::kAttached);
DCHECK(data_source_);
@@ -180,7 +241,8 @@ void WaylandWindowDragController::OnDataSourceFinish(bool completed) {
// Release DND objects.
data_offer_.reset();
data_source_.reset();
- icon_surface_.reset();
+ origin_surface_.reset();
+ origin_window_ = nullptr;
dragged_window_ = nullptr;
// Transition to |kDropped| state and determine the next action to take. If
@@ -193,6 +255,7 @@ void WaylandWindowDragController::OnDataSourceFinish(bool completed) {
HandleDropAndResetState();
data_device_->ResetDragDelegate();
+ window_manager_->RemoveObserver(this);
}
void WaylandWindowDragController::OnDataSourceSend(const std::string& mime_type,
@@ -208,7 +271,7 @@ bool WaylandWindowDragController::CanDispatchEvent(const PlatformEvent& event) {
uint32_t WaylandWindowDragController::DispatchEvent(
const PlatformEvent& event) {
DCHECK_EQ(state_, State::kDetached);
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
VLOG(2) << "Dispatch. event=" << event->GetName();
@@ -219,30 +282,10 @@ uint32_t WaylandWindowDragController::DispatchEvent(
return POST_DISPATCH_PERFORM_DEFAULT;
}
-bool WaylandWindowDragController::OfferWindow() {
- DCHECK_LE(state_, State::kAttached);
-
- auto* window = window_manager_->GetCurrentFocusedWindow();
- if (!window) {
- LOG(ERROR) << "Failed to get focused window.";
- return false;
- }
-
- if (!data_source_)
- data_source_ = data_device_manager_->CreateSource(this);
-
- if (state_ == State::kIdle) {
- DCHECK(!icon_surface_);
- icon_surface_.reset(
- wl_compositor_create_surface(connection_->compositor()));
-
- VLOG(1) << "Starting DND session.";
- state_ = State::kAttached;
- data_source_->Offer({kMimeTypeChromiumWindow});
- data_source_->SetAction(DragDropTypes::DRAG_MOVE);
- data_device_->StartDrag(*data_source_, *window, icon_surface_.get(), this);
- }
- return true;
+void WaylandWindowDragController::OnWindowRemoved(WaylandWindow* window) {
+ DCHECK_NE(state_, State::kIdle);
+ if (window == origin_window_)
+ origin_surface_ = origin_window_->TakeWaylandSurface();
}
void WaylandWindowDragController::HandleMotionEvent(MouseEvent* event) {
@@ -273,14 +316,15 @@ void WaylandWindowDragController::HandleMotionEvent(MouseEvent* event) {
// about to finish.
void WaylandWindowDragController::HandleDropAndResetState() {
DCHECK_EQ(state_, State::kDropped);
- DCHECK(window_manager_->GetCurrentFocusedWindow());
- VLOG(1) << "Notifying drop. window="
- << window_manager_->GetCurrentFocusedWindow();
+ DCHECK(pointer_grab_owner_);
+ VLOG(1) << "Notifying drop. window=" << pointer_grab_owner_;
EventFlags pointer_button = EF_LEFT_MOUSE_BUTTON;
DCHECK(connection_->event_source()->IsPointerButtonPressed(pointer_button));
- pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button);
+ pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button,
+ pointer_grab_owner_);
+ pointer_grab_owner_ = nullptr;
state_ = State::kIdle;
}
@@ -288,7 +332,8 @@ void WaylandWindowDragController::RunLoop() {
DCHECK_EQ(state_, State::kAttached);
DCHECK(dragged_window_);
- VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget();
+ VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget()
+ << " offset=" << drag_offset_.ToString();
// TODO(crbug.com/896640): Handle cursor
auto old_dispatcher = std::move(nested_dispatcher_);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
index 071cc9672bc..fa3ed531119 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
@@ -21,6 +21,7 @@
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_observer.h"
namespace ui {
@@ -29,6 +30,7 @@ class WaylandDataDeviceManager;
class WaylandDataOffer;
class WaylandWindow;
class WaylandWindowManager;
+class WaylandSurface;
// Drag controller implementation that drives window moving sessions (aka: tab
// dragging). Wayland Drag and Drop protocol is used, under the hood, to keep
@@ -37,7 +39,8 @@ class WaylandWindowManager;
// TODO(crbug.com/896640): Use drag icon to emulate window moving.
class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
public WaylandDataSource::Delegate,
- public PlatformEventDispatcher {
+ public PlatformEventDispatcher,
+ public WaylandWindowObserver {
public:
// Constants used to keep track of the drag controller state.
enum class State {
@@ -55,7 +58,12 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
delete;
~WaylandWindowDragController() override;
- bool Drag(WaylandToplevelWindow* surface, const gfx::Vector2d& offset);
+ // Starts a new Wayland DND session for window dragging, if not done yet. A
+ // new data source is setup and the focused window is used as the origin
+ // surface.
+ bool StartDragSession();
+
+ bool Drag(WaylandToplevelWindow* window, const gfx::Vector2d& offset);
void StopDragging();
State state() const { return state_; }
@@ -81,9 +89,9 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
bool CanDispatchEvent(const PlatformEvent& event) override;
uint32_t DispatchEvent(const PlatformEvent& event) override;
- // Offers the focused window as available to be dragged. A new data source is
- // setup and the underlying DnD session is started, if not done yet.
- bool OfferWindow();
+ // WaylandWindowObserver:
+ void OnWindowRemoved(WaylandWindow* window) override;
+
// Handles drag/move mouse |event|, while in |kDetached| mode, forwarding it
// as a bounds change event to the upper layer handlers.
void HandleMotionEvent(MouseEvent* event);
@@ -103,12 +111,26 @@ class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
WaylandPointer::Delegate* const pointer_delegate_;
State state_ = State::kIdle;
- WaylandToplevelWindow* dragged_window_ = nullptr;
gfx::Vector2d drag_offset_;
std::unique_ptr<WaylandDataSource> data_source_;
std::unique_ptr<WaylandDataOffer> data_offer_;
- wl::Object<wl_surface> icon_surface_;
+
+ // The current toplevel window being dragged, when in detached mode.
+ WaylandToplevelWindow* dragged_window_ = nullptr;
+
+ // Keeps track of the window that holds the pointer grab. i.e: the owner of
+ // the surface that must receive the mouse release event upon drop.
+ WaylandWindow* pointer_grab_owner_ = nullptr;
+
+ // The window where the DND session originated from. i.e: which had the
+ // pointer focus when the session was initiated.
+ WaylandWindow* origin_window_ = nullptr;
+
+ // The |origin_window_| can be destroyed during the DND session. If this
+ // happens, |origin_surface_| takes ownership of its surface and ensure it
+ // is kept alive until the end of the session.
+ std::unique_ptr<WaylandSurface> origin_surface_;
std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_;
base::OnceClosure quit_loop_closure_;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
index 56a13b46e48..4f24aadffb4 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
@@ -21,7 +21,6 @@
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
-#include "ui/ozone/platform/wayland/test/constants.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_data_device.h"
@@ -31,8 +30,9 @@
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/ozone/test/mock_platform_window_delegate.h"
+#include "ui/platform_window/extensions/wayland_extension.h"
#include "ui/platform_window/platform_window_delegate.h"
-#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
+#include "ui/platform_window/wm/wm_move_loop_handler.h"
using testing::_;
using testing::Mock;
@@ -109,7 +109,8 @@ class WaylandWindowDragControllerTest : public WaylandTest,
void SendDndEnter(WaylandWindow* window) {
EXPECT_TRUE(window);
- OfferAndEnter(server_.GetObject<wl::MockSurface>(window->GetWidget()));
+ OfferAndEnter(server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId()));
}
void SendDndLeave() {
@@ -124,7 +125,8 @@ class WaylandWindowDragControllerTest : public WaylandTest,
void SendPointerEnter(WaylandWindow* window,
MockPlatformWindowDelegate* delegate) {
- auto* surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
wl_pointer_send_enter(pointer_->resource(), NextSerial(),
surface->resource(), 0, 0);
EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1);
@@ -133,6 +135,18 @@ class WaylandWindowDragControllerTest : public WaylandTest,
EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow());
}
+ void SendPointerLeave(WaylandWindow* window,
+ MockPlatformWindowDelegate* delegate) {
+ auto* surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
+ wl_pointer_send_leave(pointer_->resource(), NextSerial(),
+ surface->resource());
+ EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1);
+ Sync();
+
+ EXPECT_EQ(nullptr, window_manager()->GetCurrentFocusedWindow());
+ }
+
void SendPointerPress(WaylandWindow* window,
MockPlatformWindowDelegate* delegate,
int button) {
@@ -195,10 +209,14 @@ TEST_P(WaylandWindowDragControllerTest, DragInsideWindowAndDrop) {
SendPointerPress(window_.get(), &delegate_, BTN_LEFT);
SendPointerMotion(window_.get(), &delegate_, {10, 10});
- // Set up an "interaction flow" and RunMoveLoop:
+ // Set up an "interaction flow", start the drag session and run move loop:
// - Event dispatching and bounds changes are monitored
// - At each event, emulates a new event at server side and proceeds to the
// next test step.
+ auto* wayland_extension = GetWaylandExtension(*window_);
+ wayland_extension->StartWindowDraggingSessionIfNeeded();
+ EXPECT_EQ(State::kAttached, drag_controller()->state());
+
auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
DCHECK(move_loop_handler);
@@ -281,21 +299,20 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) {
SendPointerEnter(window_.get(), &delegate_);
SendPointerPress(window_.get(), &delegate_, BTN_LEFT);
SendPointerMotion(window_.get(), &delegate_, {10, 10});
+ Sync();
- // Sets up an "interaction flow" and RunMoveLoop:
+ // Sets up an "interaction flow", start the drag session and run move loop:
// - Event dispatching and bounds changes are monitored
// - At each event, emulates a new event on server side and proceeds to the
// next test step.
+ auto* wayland_extension = GetWaylandExtension(*window_);
+ wayland_extension->StartWindowDraggingSessionIfNeeded();
+ EXPECT_EQ(State::kAttached, drag_controller()->state());
+
auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
DCHECK(move_loop_handler);
- enum {
- kStarted,
- kDragging,
- kExitedWindow,
- kDropping,
- kDone
- } test_step = kStarted;
+ enum { kStarted, kDragging, kExitedDropping, kDone } test_step = kStarted;
EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
EXPECT_TRUE(event->IsMouseEvent());
@@ -310,13 +327,7 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) {
SendDndMotion({20, 20});
test_step = kDragging;
break;
- case kExitedWindow:
- EXPECT_EQ(ET_MOUSE_EXITED, event->type());
- // Release mouse button with no window foucsed.
- SendDndDrop();
- test_step = kDropping;
- break;
- case kDropping:
+ case kExitedDropping:
EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
EXPECT_EQ(State::kDropped, drag_controller()->state());
// Ensure PlatformScreen keeps consistent.
@@ -343,8 +354,9 @@ TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) {
EXPECT_EQ(kDragging, test_step);
EXPECT_EQ(gfx::Point(20, 20), bounds.origin());
+ SendDndLeave();
SendDndDrop();
- test_step = kDropping;
+ test_step = kExitedDropping;
});
// RunMoveLoop() blocks until the dragging sessions ends, so resume test
@@ -394,10 +406,14 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) {
SendPointerPress(source_window, &delegate_, BTN_LEFT);
SendPointerMotion(source_window, &delegate_, {10, 10});
- // Sets up an "interaction flow" and RunMoveLoop:
+ // Sets up an "interaction flow", start the drag session and run move loop:
// - Event dispatching and bounds changes are monitored
// - At each event, emulates a new event on server side and proceeds to the
// next test step.
+ auto* wayland_extension = GetWaylandExtension(*window_);
+ wayland_extension->StartWindowDraggingSessionIfNeeded();
+ EXPECT_EQ(State::kAttached, drag_controller()->state());
+
auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
DCHECK(move_loop_handler);
@@ -479,20 +495,22 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) {
EXPECT_EQ(target_window->GetWidget(),
screen_->GetLocalProcessWidgetAtPoint({50, 50}, {}));
+ // Emulates a pointer::leave event being sent before data_source::cancelled,
+ // what happens with some compositors, e.g: Exosphere. Even in these cases,
+ // WaylandWindowDragController must guarantee the mouse button release event
+ // (aka: drop) is delivered to the upper layer listeners.
+ SendPointerLeave(target_window, &delegate_);
+
SendDndDrop();
- EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce([&](Event* event) {
EXPECT_TRUE(event->IsMouseEvent());
switch (test_step) {
case kSnapped:
EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
EXPECT_EQ(State::kDropped, drag_controller()->state());
+ EXPECT_EQ(target_window, window_manager()->GetCurrentFocusedWindow());
test_step = kDone;
break;
- case kDone:
- EXPECT_EQ(ET_MOUSE_EXITED, event->type());
- EXPECT_EQ(target_window->GetWidget(),
- screen_->GetLocalProcessWidgetAtPoint({30, 42}, {}));
- break;
default:
FAIL() << " event=" << event->GetName()
<< " state=" << drag_controller()->state()
@@ -508,6 +526,50 @@ TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) {
screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
}
+// Verifies wl_data_device::leave events are properly handled and propagated
+// while in window dragging "attached" mode.
+TEST_P(WaylandWindowDragControllerTest, DragExitAttached) {
+ // Ensure there is no window currently focused
+ EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(gfx::kNullAcceleratedWidget,
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ SendPointerEnter(window_.get(), &delegate_);
+ SendPointerPress(window_.get(), &delegate_, BTN_LEFT);
+ SendPointerMotion(window_.get(), &delegate_, {10, 10});
+ Sync();
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ auto* wayland_extension = GetWaylandExtension(*window_);
+ wayland_extension->StartWindowDraggingSessionIfNeeded();
+ EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1);
+ Sync();
+ Sync();
+ EXPECT_EQ(State::kAttached, drag_controller()->state());
+
+ // Emulate a wl_data_device::leave and make sure a motion event is dispatched
+ // in response.
+ SendDndLeave();
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce([&](Event* event) {
+ EXPECT_EQ(ET_MOUSE_DRAGGED, event->type());
+ EXPECT_EQ(gfx::Point(-1, -1).ToString(),
+ event->AsMouseEvent()->location().ToString());
+ });
+ Sync();
+
+ SendDndDrop();
+ EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1);
+ Sync();
+
+ SendPointerEnter(window_.get(), &delegate_);
+ Sync();
+
+ EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandWindowDragControllerTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
index 29d70dbdc39..7854b05b999 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
@@ -5,9 +5,9 @@
#include <memory>
#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/wayland/host/wayland_auxiliary_window.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_popup.h"
-#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -30,13 +30,13 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create(
} else if (connection->IsDragInProgress()) {
// We are in the process of drag and requested a popup. Most probably,
// it is an arrow window.
- window.reset(new WaylandSubsurface(delegate, connection));
+ window.reset(new WaylandAuxiliaryWindow(delegate, connection));
} else {
window.reset(new WaylandPopup(delegate, connection));
}
break;
case PlatformWindowType::kTooltip:
- window.reset(new WaylandSubsurface(delegate, connection));
+ window.reset(new WaylandAuxiliaryWindow(delegate, connection));
break;
case PlatformWindowType::kWindow:
case PlatformWindowType::kBubble:
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
index 51f07bbc27a..0c9b86e81fc 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
@@ -110,6 +110,28 @@ void WaylandWindowManager::RemoveWindow(gfx::AcceleratedWidget widget) {
observer.OnWindowRemoved(window);
}
+void WaylandWindowManager::AddSubsurface(gfx::AcceleratedWidget widget,
+ WaylandSubsurface* subsurface) {
+ auto* window = window_map_[widget];
+ DCHECK(window);
+
+ for (WaylandWindowObserver& observer : observers_)
+ observer.OnSubsurfaceAdded(window, subsurface);
+}
+
+void WaylandWindowManager::RemoveSubsurface(gfx::AcceleratedWidget widget,
+ WaylandSubsurface* subsurface) {
+ auto* window = window_map_[widget];
+ DCHECK(window);
+
+ for (WaylandWindowObserver& observer : observers_)
+ observer.OnSubsurfaceRemoved(window, subsurface);
+}
+
+gfx::AcceleratedWidget WaylandWindowManager::AllocateAcceleratedWidget() {
+ return ++last_accelerated_widget_;
+}
+
std::vector<WaylandWindow*> WaylandWindowManager::GetAllWindows() const {
std::vector<WaylandWindow*> result;
for (auto entry : window_map_)
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
index 1f4cad045ca..ca32c24833f 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
@@ -16,6 +16,7 @@
namespace ui {
class WaylandWindow;
+class WaylandSubsurface;
// Stores and returns WaylandWindows. Clients that are interested in knowing
// when a new window is added or removed, but set self as an observer.
@@ -63,6 +64,13 @@ class WaylandWindowManager {
void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window);
void RemoveWindow(gfx::AcceleratedWidget widget);
+ void AddSubsurface(gfx::AcceleratedWidget widget,
+ WaylandSubsurface* subsurface);
+ void RemoveSubsurface(gfx::AcceleratedWidget widget,
+ WaylandSubsurface* subsurface);
+
+ // Creates a new unique gfx::AcceleratedWidget.
+ gfx::AcceleratedWidget AllocateAcceleratedWidget();
private:
base::ObserverList<WaylandWindowObserver> observers_;
@@ -71,6 +79,10 @@ class WaylandWindowManager {
WaylandWindow* located_events_grabber_ = nullptr;
+ // Stores strictly monotonically increasing counter for allocating unique
+ // AccelerateWidgets.
+ gfx::AcceleratedWidget last_accelerated_widget_ = gfx::kNullAcceleratedWidget;
+
DISALLOW_COPY_AND_ASSIGN(WaylandWindowManager);
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc
index 73b6d2c187e..8237298cde9 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager_unittests.cc
@@ -100,8 +100,8 @@ TEST_P(WaylandWindowManagerTest, GetCurrentFocusedWindow) {
auto* pointer = server_.seat()->pointer();
ASSERT_TRUE(pointer);
- wl::MockSurface* surface =
- server_.GetObject<wl::MockSurface>(window1->GetWidget());
+ wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(
+ window1->root_surface()->GetSurfaceId());
wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0);
Sync();
@@ -137,8 +137,8 @@ TEST_P(WaylandWindowManagerTest, GetCurrentKeyboardFocusedWindow) {
auto* keyboard = server_.seat()->keyboard();
ASSERT_TRUE(keyboard);
- wl::MockSurface* surface =
- server_.GetObject<wl::MockSurface>(window1->GetWidget());
+ wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(
+ window1->root_surface()->GetSurfaceId());
struct wl_array empty;
wl_array_init(&empty);
@@ -159,7 +159,15 @@ TEST_P(WaylandWindowManagerTest, GetCurrentKeyboardFocusedWindow) {
TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) {
MockPlatformWindowDelegate delegate;
+ // Create a second wl_output.
wl::TestOutput* output = server_.CreateAndInitializeOutput();
+ Sync();
+
+ // Place it at the right of the first output.
+ int x = server_.output()->GetRect().x();
+ output->SetRect({x, 0, 800, 600});
+ output->Flush();
+ Sync();
auto window1 = CreateWaylandWindowWithParams(PlatformWindowType::kWindow,
kDefaultBounds, &delegate);
@@ -169,8 +177,8 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) {
Sync();
- wl::MockSurface* surface =
- server_.GetObject<wl::MockSurface>(window1->GetWidget());
+ wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(
+ window1->root_surface()->GetSurfaceId());
ASSERT_TRUE(surface);
wl_surface_send_enter(surface->resource(), output->resource());
@@ -186,7 +194,8 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) {
EXPECT_TRUE(window1.get() == *windows_on_output.begin());
- surface = server_.GetObject<wl::MockSurface>(window2->GetWidget());
+ surface = server_.GetObject<wl::MockSurface>(
+ window2->root_surface()->GetSurfaceId());
ASSERT_TRUE(surface);
wl_surface_send_enter(surface->resource(), output->resource());
@@ -194,6 +203,9 @@ TEST_P(WaylandWindowManagerTest, GetWindowsOnOutput) {
windows_on_output = manager_->GetWindowsOnOutput(output_id);
EXPECT_EQ(2u, windows_on_output.size());
+
+ output->DestroyGlobal();
+ Sync();
}
TEST_P(WaylandWindowManagerTest, GetAllWindows) {
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
index e289ea88e3d..5ce43f062f3 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
@@ -14,4 +14,11 @@ void WaylandWindowObserver::OnWindowRemoved(WaylandWindow* window) {}
void WaylandWindowObserver::OnWindowConfigured(WaylandWindow* window) {}
+void WaylandWindowObserver::OnSubsurfaceAdded(WaylandWindow* window,
+ WaylandSubsurface* subsurface) {}
+
+void WaylandWindowObserver::OnSubsurfaceRemoved(WaylandWindow* window,
+ WaylandSubsurface* subsurface) {
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
index c461b03114e..2434eb779a4 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
@@ -10,6 +10,7 @@
namespace ui {
class WaylandWindow;
+class WaylandSubsurface;
// Observers for window management notifications.
class WaylandWindowObserver : public base::CheckedObserver {
@@ -23,6 +24,14 @@ class WaylandWindowObserver : public base::CheckedObserver {
// Called when |window| has been ack configured.
virtual void OnWindowConfigured(WaylandWindow* window);
+ // Called when |window| adds |subsurface|.
+ virtual void OnSubsurfaceAdded(WaylandWindow* window,
+ WaylandSubsurface* subsurface);
+
+ // Called when |window| removes |subsurface|.
+ virtual void OnSubsurfaceRemoved(WaylandWindow* window,
+ WaylandSubsurface* subsurface);
+
protected:
~WaylandWindowObserver() override;
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index d7c533edc09..2626222a427 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -19,7 +19,9 @@
#include "ui/base/hit_test.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
+#include "ui/gfx/overlay_transform.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
+#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
#include "ui/ozone/platform/wayland/test/mock_pointer.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/test_keyboard.h"
@@ -28,8 +30,8 @@
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/ozone/test/mock_platform_window_delegate.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
#include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
using ::testing::_;
using ::testing::Eq;
@@ -98,9 +100,9 @@ class WaylandWindowTest : public WaylandTest {
}
protected:
- void SendConfigureEventPopup(gfx::AcceleratedWidget menu_widget,
+ void SendConfigureEventPopup(WaylandWindow* menu_window,
const gfx::Rect bounds) {
- auto* popup = GetPopupByWidget(menu_widget);
+ auto* popup = GetPopupByWindow(menu_window);
ASSERT_TRUE(popup);
if (GetParam() == kXdgShellV6) {
zxdg_popup_v6_send_configure(popup->resource(), bounds.x(), bounds.y(),
@@ -164,9 +166,9 @@ class WaylandWindowTest : public WaylandTest {
Mock::VerifyAndClearExpectations(&delegate_);
}
- void VerifyXdgPopupPosition(gfx::AcceleratedWidget menu_widget,
+ void VerifyXdgPopupPosition(WaylandWindow* menu_window,
const PopupPosition& position) {
- auto* popup = GetPopupByWidget(menu_widget);
+ auto* popup = GetPopupByWindow(menu_window);
ASSERT_TRUE(popup);
EXPECT_EQ(popup->anchor_rect(), position.anchor_rect);
@@ -207,8 +209,9 @@ class WaylandWindowTest : public WaylandTest {
EXPECT_FALSE(window->CanDispatchEvent(&test_key_event));
}
- wl::TestXdgPopup* GetPopupByWidget(gfx::AcceleratedWidget widget) {
- wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ wl::TestXdgPopup* GetPopupByWindow(WaylandWindow* window) {
+ wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
if (mock_surface) {
auto* mock_xdg_surface = mock_surface->xdg_surface();
if (mock_xdg_surface)
@@ -384,7 +387,8 @@ TEST_P(WaylandWindowTest, StartWithFullscreen) {
// The state must not be changed to the fullscreen before the surface is
// activated.
- auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
EXPECT_FALSE(mock_surface->xdg_surface());
EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0);
window->ToggleFullscreen();
@@ -428,7 +432,8 @@ TEST_P(WaylandWindowTest, StartMaximized) {
// The state must not be changed to the fullscreen before the surface is
// activated.
- auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
EXPECT_FALSE(mock_surface->xdg_surface());
EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
@@ -1196,8 +1201,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) {
uint32_t serial = 0;
// Test that CanDispatchEvent is set correctly.
- wl::MockSurface* toplevel_surface =
- server_.GetObject<wl::MockSurface>(widget_);
+ wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial,
toplevel_surface->resource(), 0, 0);
@@ -1240,8 +1245,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) {
VerifyCanDispatchKeyEvents({window_.get()},
{menu_window.get(), nested_menu_window.get()});
- wl::MockSurface* menu_window_surface =
- server_.GetObject<wl::MockSurface>(menu_window->GetWidget());
+ wl::MockSurface* menu_window_surface = server_.GetObject<wl::MockSurface>(
+ menu_window->root_surface()->GetSurfaceId());
wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial,
toplevel_surface->resource());
@@ -1263,7 +1268,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) {
{}, {window_.get(), menu_window.get(), nested_menu_window.get()});
wl::MockSurface* nested_menu_window_surface =
- server_.GetObject<wl::MockSurface>(nested_menu_window->GetWidget());
+ server_.GetObject<wl::MockSurface>(
+ nested_menu_window->root_surface()->GetSurfaceId());
wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial,
menu_window_surface->resource());
@@ -1351,11 +1357,10 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
Sync();
- gfx::AcceleratedWidget menu_window_widget = menu_window->GetWidget();
- VerifyXdgPopupPosition(menu_window_widget, menu_window_positioner);
+ VerifyXdgPopupPosition(menu_window.get(), menu_window_positioner);
EXPECT_CALL(menu_window_delegate, OnBoundsChanged(_)).Times(0);
- SendConfigureEventPopup(menu_window_widget, menu_window_bounds);
+ SendConfigureEventPopup(menu_window.get(), menu_window_bounds);
Sync();
@@ -1367,15 +1372,13 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
nested_menu_window_positioner.size);
std::unique_ptr<WaylandWindow> nested_menu_window =
CreateWaylandWindowWithParams(
- PlatformWindowType::kMenu, menu_window_widget,
+ PlatformWindowType::kMenu, menu_window->GetWidget(),
nested_menu_window_bounds, &nested_menu_window_delegate);
EXPECT_TRUE(nested_menu_window);
Sync();
- gfx::AcceleratedWidget nested_menu_window_widget =
- nested_menu_window->GetWidget();
- VerifyXdgPopupPosition(nested_menu_window_widget,
+ VerifyXdgPopupPosition(nested_menu_window.get(),
nested_menu_window_positioner);
EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0);
@@ -1384,7 +1387,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
nested_menu_window_positioner.anchor_rect.y());
gfx::Rect calculated_nested_bounds = nested_menu_window_bounds;
calculated_nested_bounds.set_origin(origin);
- SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds);
+ SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
Sync();
@@ -1402,7 +1405,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
nested_menu_window_delegate,
OnBoundsChanged(gfx::Rect({139, 156}, nested_menu_window_bounds.size())));
calculated_nested_bounds.set_origin({-301, 80});
- SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds);
+ SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
Sync();
@@ -1417,7 +1420,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
gfx::Rect({0, 363}, window_->GetBounds().size())));
EXPECT_CALL(menu_window_delegate,
OnBoundsChanged(gfx::Rect({440, 0}, menu_window_bounds.size())));
- SendConfigureEventPopup(menu_window_widget,
+ SendConfigureEventPopup(menu_window.get(),
gfx::Rect({440, -363}, menu_window_bounds.size()));
Sync();
@@ -1428,23 +1431,22 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
nested_menu_window.reset();
nested_menu_window_bounds.set_origin({723, 258});
nested_menu_window = CreateWaylandWindowWithParams(
- PlatformWindowType::kMenu, menu_window_widget, nested_menu_window_bounds,
- &nested_menu_window_delegate);
+ PlatformWindowType::kMenu, menu_window->GetWidget(),
+ nested_menu_window_bounds, &nested_menu_window_delegate);
EXPECT_TRUE(nested_menu_window);
Sync();
- nested_menu_window_widget = nested_menu_window->GetWidget();
// We must get the anchor on gfx::Point(4, 258).
nested_menu_window_positioner.anchor_rect.set_origin({4, 258});
- VerifyXdgPopupPosition(nested_menu_window_widget,
+ VerifyXdgPopupPosition(nested_menu_window.get(),
nested_menu_window_positioner);
Sync();
EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0);
calculated_nested_bounds.set_origin({283, 258});
- SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds);
+ SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
Sync();
@@ -1459,7 +1461,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
nested_menu_window_delegate,
OnBoundsChanged(gfx::Rect({149, 258}, nested_menu_window_bounds.size())));
calculated_nested_bounds.set_origin({-291, 258});
- SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds);
+ SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
Sync();
@@ -1472,7 +1474,7 @@ TEST_P(WaylandWindowTest, AdjustPopupBounds) {
OnBoundsChanged(gfx::Rect({0, 0}, window_->GetBounds().size())));
EXPECT_CALL(menu_window_delegate,
OnBoundsChanged(gfx::Rect({440, 76}, menu_window_bounds.size())));
- SendConfigureEventPopup(menu_window_widget,
+ SendConfigureEventPopup(menu_window.get(),
gfx::Rect({440, 76}, menu_window_bounds.size()));
Sync();
@@ -1486,8 +1488,8 @@ ACTION_P(VerifyRegion, ptr) {
}
TEST_P(WaylandWindowTest, SetOpaqueRegion) {
- wl::MockSurface* mock_surface =
- server_.GetObject<wl::MockSurface>(window_->GetWidget());
+ wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
gfx::Rect new_bounds(0, 0, 500, 600);
auto state_array = MakeStateArray({XDG_TOPLEVEL_STATE_ACTIVATED});
@@ -1525,7 +1527,7 @@ TEST_P(WaylandWindowTest, OnCloseRequest) {
Sync();
}
-TEST_P(WaylandWindowTest, SubsurfaceSimpleParent) {
+TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) {
VerifyAndClearExpectations();
std::unique_ptr<WaylandWindow> second_window = CreateWaylandWindowWithParams(
@@ -1536,60 +1538,64 @@ TEST_P(WaylandWindowTest, SubsurfaceSimpleParent) {
// Test case 1: if the subsurface is provided with a parent widget, it must
// always use that as a parent.
gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
- std::unique_ptr<WaylandWindow> subsuface_window =
+ std::unique_ptr<WaylandWindow> auxiliary_window =
CreateWaylandWindowWithParams(PlatformWindowType::kTooltip,
window_->GetWidget(), subsurface_bounds,
&delegate_);
- EXPECT_TRUE(subsuface_window);
+ EXPECT_TRUE(auxiliary_window);
// The subsurface mustn't take the focused window as a parent, but use the
// provided one.
second_window->SetPointerFocus(true);
- subsuface_window->Show(false);
+ auxiliary_window->Show(false);
Sync();
- auto* mock_surface_subsurface =
- server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget());
+ auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
+ auxiliary_window->root_surface()->GetSurfaceId());
auto* test_subsurface = mock_surface_subsurface->sub_surface();
EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin());
EXPECT_FALSE(test_subsurface->sync());
auto* parent_resource =
- server_.GetObject<wl::MockSurface>(window_->GetWidget())->resource();
+ server_
+ .GetObject<wl::MockSurface>(window_->root_surface()->GetSurfaceId())
+ ->resource();
EXPECT_EQ(parent_resource, test_subsurface->parent_resource());
// Test case 2: the subsurface must use the focused window as its parent.
- subsuface_window = CreateWaylandWindowWithParams(
+ auxiliary_window = CreateWaylandWindowWithParams(
PlatformWindowType::kTooltip, gfx::kNullAcceleratedWidget,
subsurface_bounds, &delegate_);
- EXPECT_TRUE(subsuface_window);
+ EXPECT_TRUE(auxiliary_window);
// The tooltip must take the focused window.
second_window->SetPointerFocus(true);
- subsuface_window->Show(false);
+ auxiliary_window->Show(false);
Sync();
- // Get new surface after recreating the WaylandSubsurface.
- mock_surface_subsurface =
- server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget());
+ // Get new surface after recreating the WaylandAuxiliaryWindow.
+ mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
+ auxiliary_window->root_surface()->GetSurfaceId());
test_subsurface = mock_surface_subsurface->sub_surface();
auto* second_parent_resource =
- server_.GetObject<wl::MockSurface>(second_window->GetWidget())
+ server_
+ .GetObject<wl::MockSurface>(
+ second_window->root_surface()->GetSurfaceId())
->resource();
EXPECT_EQ(second_parent_resource, test_subsurface->parent_resource());
- subsuface_window->Hide();
+ auxiliary_window->Hide();
Sync();
// The subsurface must take the focused window.
second_window->SetPointerFocus(false);
window_->SetPointerFocus(true);
- subsuface_window->Show(false);
+ auxiliary_window->Show(false);
Sync();
@@ -1616,27 +1622,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu) {
VerifyAndClearExpectations();
gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 40, 20));
- std::unique_ptr<WaylandWindow> nestedPopup_window =
+ std::unique_ptr<WaylandWindow> nested_popup_window =
CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
menu_window->GetWidget(),
nestedPopup_bounds, &delegate_);
- EXPECT_TRUE(nestedPopup_window);
+ EXPECT_TRUE(nested_popup_window);
VerifyAndClearExpectations();
- nestedPopup_window->SetPointerFocus(true);
+ nested_popup_window->SetPointerFocus(true);
Sync();
- auto* mock_surface_nested_popup =
- GetPopupByWidget(nestedPopup_window->GetWidget());
+ auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
ASSERT_TRUE(mock_surface_nested_popup);
auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
EXPECT_EQ(4, anchor_width);
- nestedPopup_window->SetPointerFocus(false);
+ nested_popup_window->SetPointerFocus(false);
}
// Case 2: When the menu bounds are positive and there is a negative or
@@ -1653,27 +1658,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu1) {
VerifyAndClearExpectations();
gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 10, 20));
- std::unique_ptr<WaylandWindow> nestedPopup_window =
+ std::unique_ptr<WaylandWindow> nested_popup_window =
CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
menu_window->GetWidget(),
nestedPopup_bounds, &delegate_);
- EXPECT_TRUE(nestedPopup_window);
+ EXPECT_TRUE(nested_popup_window);
VerifyAndClearExpectations();
- nestedPopup_window->SetPointerFocus(true);
+ nested_popup_window->SetPointerFocus(true);
Sync();
- auto* mock_surface_nested_popup =
- GetPopupByWidget(nestedPopup_window->GetWidget());
+ auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
ASSERT_TRUE(mock_surface_nested_popup);
auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
EXPECT_EQ(1, anchor_width);
- nestedPopup_window->SetPointerFocus(false);
+ nested_popup_window->SetPointerFocus(false);
}
// Case 3: When the menu bounds are negative and there is a positive,
@@ -1690,27 +1694,26 @@ TEST_P(WaylandWindowTest, NestedPopupMenu2) {
VerifyAndClearExpectations();
gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20));
- std::unique_ptr<WaylandWindow> nestedPopup_window =
+ std::unique_ptr<WaylandWindow> nested_popup_window =
CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
menu_window->GetWidget(),
nestedPopup_bounds, &delegate_);
- EXPECT_TRUE(nestedPopup_window);
+ EXPECT_TRUE(nested_popup_window);
VerifyAndClearExpectations();
- nestedPopup_window->SetPointerFocus(true);
+ nested_popup_window->SetPointerFocus(true);
Sync();
- auto* mock_surface_nested_popup =
- GetPopupByWidget(nestedPopup_window->GetWidget());
+ auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
ASSERT_TRUE(mock_surface_nested_popup);
auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
EXPECT_EQ(8, anchor_width);
- nestedPopup_window->SetPointerFocus(false);
+ nested_popup_window->SetPointerFocus(false);
}
// Case 4: When the menu bounds are negative and there is a negative,
@@ -1727,20 +1730,19 @@ TEST_P(WaylandWindowTest, NestedPopupMenu3) {
VerifyAndClearExpectations();
gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20));
- std::unique_ptr<WaylandWindow> nestedPopup_window =
+ std::unique_ptr<WaylandWindow> nested_popup_window =
CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
menu_window->GetWidget(),
nestedPopup_bounds, &delegate_);
- EXPECT_TRUE(nestedPopup_window);
+ EXPECT_TRUE(nested_popup_window);
VerifyAndClearExpectations();
- nestedPopup_window->SetPointerFocus(true);
+ nested_popup_window->SetPointerFocus(true);
Sync();
- auto* mock_surface_nested_popup =
- GetPopupByWidget(nestedPopup_window->GetWidget());
+ auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
ASSERT_TRUE(mock_surface_nested_popup);
@@ -1748,10 +1750,10 @@ TEST_P(WaylandWindowTest, NestedPopupMenu3) {
EXPECT_EQ(1, anchor_width);
- nestedPopup_window->SetPointerFocus(false);
+ nested_popup_window->SetPointerFocus(false);
}
-TEST_P(WaylandWindowTest, SubsurfaceNestedParent) {
+TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) {
VerifyAndClearExpectations();
gfx::Rect menu_window_bounds(gfx::Point(10, 10), gfx::Size(100, 100));
@@ -1763,22 +1765,22 @@ TEST_P(WaylandWindowTest, SubsurfaceNestedParent) {
VerifyAndClearExpectations();
gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
- std::unique_ptr<WaylandWindow> subsuface_window =
+ std::unique_ptr<WaylandWindow> auxiliary_window =
CreateWaylandWindowWithParams(PlatformWindowType::kTooltip,
menu_window->GetWidget(), subsurface_bounds,
&delegate_);
- EXPECT_TRUE(subsuface_window);
+ EXPECT_TRUE(auxiliary_window);
VerifyAndClearExpectations();
menu_window->SetPointerFocus(true);
- subsuface_window->Show(false);
+ auxiliary_window->Show(false);
Sync();
- auto* mock_surface_subsurface =
- server_.GetObject<wl::MockSurface>(subsuface_window->GetWidget());
+ auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
+ auxiliary_window->root_surface()->GetSurfaceId());
auto* test_subsurface = mock_surface_subsurface->sub_surface();
auto new_origin = subsurface_bounds.origin() -
@@ -1825,7 +1827,8 @@ TEST_P(WaylandWindowTest, DestroysCreatesSurfaceOnHideShow) {
Sync();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
EXPECT_TRUE(mock_surface->xdg_surface());
EXPECT_TRUE(mock_surface->xdg_surface()->xdg_toplevel());
@@ -1854,7 +1857,8 @@ TEST_P(WaylandWindowTest, DestroysCreatesPopupsOnHideShow) {
Sync();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
EXPECT_TRUE(mock_surface->xdg_surface());
EXPECT_TRUE(mock_surface->xdg_surface()->xdg_popup());
@@ -1893,7 +1897,8 @@ TEST_P(WaylandWindowTest, SetsPropertiesOnShow) {
Sync();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
auto* mock_xdg_toplevel = mock_surface->xdg_surface()->xdg_toplevel();
// Only app id must be set now.
@@ -1952,8 +1957,8 @@ TEST_P(WaylandWindowTest, CreatesPopupOnButtonPressSerial) {
constexpr uint32_t button_press_serial = 2;
constexpr uint32_t button_release_serial = 3;
- wl::MockSurface* toplevel_surface =
- server_.GetObject<wl::MockSurface>(window_->GetWidget());
+ wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
struct wl_array empty;
wl_array_init(&empty);
wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial,
@@ -1977,7 +1982,7 @@ TEST_P(WaylandWindowTest, CreatesPopupOnButtonPressSerial) {
Sync();
- auto* test_popup = GetPopupByWidget(popup->GetWidget());
+ auto* test_popup = GetPopupByWindow(popup.get());
ASSERT_TRUE(test_popup);
EXPECT_NE(test_popup->grab_serial(), button_release_serial);
EXPECT_EQ(test_popup->grab_serial(), button_press_serial);
@@ -1996,8 +2001,8 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) {
constexpr uint32_t touch_down_serial = 2;
constexpr uint32_t touch_up_serial = 3;
- wl::MockSurface* toplevel_surface =
- server_.GetObject<wl::MockSurface>(window_->GetWidget());
+ wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
struct wl_array empty;
wl_array_init(&empty);
wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial,
@@ -2021,7 +2026,7 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) {
Sync();
- auto* test_popup = GetPopupByWidget(popup->GetWidget());
+ auto* test_popup = GetPopupByWindow(popup.get());
ASSERT_TRUE(test_popup);
EXPECT_NE(test_popup->grab_serial(), touch_up_serial);
EXPECT_EQ(test_popup->grab_serial(), touch_down_serial);
@@ -2075,11 +2080,48 @@ TEST_P(WaylandWindowTest, DoesNotGrabPopupIfNoSeat) {
Sync();
- auto* test_popup = GetPopupByWidget(popup->GetWidget());
+ auto* test_popup = GetPopupByWindow(popup.get());
ASSERT_TRUE(test_popup);
EXPECT_EQ(test_popup->grab_serial(), 0u);
}
+TEST_P(WaylandWindowTest, OneWaylandSubsurface) {
+ VerifyAndClearExpectations();
+
+ std::unique_ptr<WaylandWindow> window = CreateWaylandWindowWithParams(
+ PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
+ gfx::Rect(0, 0, 640, 480), &delegate_);
+ EXPECT_TRUE(window);
+
+ gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
+ bool result = window->RequestSubsurface();
+ EXPECT_TRUE(result);
+
+ WaylandSubsurface* wayland_subsurface =
+ window->wayland_subsurfaces().begin()->get();
+
+ Sync();
+
+ auto* mock_surface_root_window = server_.GetObject<wl::MockSurface>(
+ window->root_surface()->GetSurfaceId());
+ auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
+ wayland_subsurface->wayland_surface()->GetSurfaceId());
+ EXPECT_TRUE(mock_surface_subsurface);
+ wayland_subsurface->ConfigureAndShowSurface(
+ gfx::OVERLAY_TRANSFORM_NONE, subsurface_bounds, true, nullptr, nullptr);
+ connection_->ScheduleFlush();
+
+ Sync();
+
+ auto* test_subsurface = mock_surface_subsurface->sub_surface();
+ EXPECT_TRUE(test_subsurface);
+ auto* parent_resource = mock_surface_root_window->resource();
+ EXPECT_EQ(parent_resource, test_subsurface->parent_resource());
+
+ EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin());
+ EXPECT_TRUE(test_subsurface->sync());
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandWindowTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
index 758ae1f222e..99eb2e7012a 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
@@ -329,7 +329,7 @@ bool XDGPopupWrapperImpl::InitializeStable(
}
xdg_popup_add_listener(xdg_popup_.get(), &xdg_popup_listener, this);
- wl_surface_commit(wayland_window_->surface());
+ wayland_window_->root_surface()->Commit();
return true;
}
@@ -393,7 +393,7 @@ bool XDGPopupWrapperImpl::InitializeV6(
zxdg_popup_v6_add_listener(zxdg_popup_v6_.get(), &zxdg_popup_v6_listener,
this);
- wl_surface_commit(wayland_window_->surface());
+ wayland_window_->root_surface()->Commit();
return true;
}
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
index ae1ca159fd4..f1a03831363 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h"
+#include <aura-shell-client-protocol.h>
#include <xdg-shell-client-protocol.h>
#include <xdg-shell-unstable-v6-client-protocol.h>
@@ -267,8 +268,8 @@ bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) {
// configuration acknowledgement on each configure event.
surface_for_popup_ = !with_toplevel;
- xdg_surface_.reset(xdg_wm_base_get_xdg_surface(connection_->shell(),
- wayland_window_->surface()));
+ xdg_surface_.reset(xdg_wm_base_get_xdg_surface(
+ connection_->shell(), wayland_window_->root_surface()->surface()));
if (!xdg_surface_) {
LOG(ERROR) << "Failed to create xdg_surface";
return false;
@@ -287,8 +288,7 @@ bool XDGSurfaceWrapperImpl::InitializeStable(bool with_toplevel) {
return false;
}
xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener, this);
- wl_surface_commit(wayland_window_->surface());
-
+ wayland_window_->root_surface()->Commit();
connection_->ScheduleFlush();
return true;
}
@@ -307,7 +307,7 @@ bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) {
surface_for_popup_ = !with_toplevel;
zxdg_surface_v6_.reset(zxdg_shell_v6_get_xdg_surface(
- connection_->shell_v6(), wayland_window_->surface()));
+ connection_->shell_v6(), wayland_window_->root_surface()->surface()));
if (!zxdg_surface_v6_) {
LOG(ERROR) << "Failed to create zxdg_surface";
return false;
@@ -328,8 +328,15 @@ bool XDGSurfaceWrapperImpl::InitializeV6(bool with_toplevel) {
}
zxdg_toplevel_v6_add_listener(zxdg_toplevel_v6_.get(),
&zxdg_toplevel_v6_listener, this);
- wl_surface_commit(wayland_window_->surface());
+ if (connection_->aura_shell()) {
+ aura_surface_.reset(zaura_shell_get_aura_surface(
+ connection_->aura_shell(), wayland_window_->root_surface()->surface()));
+ zaura_surface_set_fullscreen_mode(aura_surface_.get(),
+ ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE);
+ }
+
+ wayland_window_->root_surface()->Commit();
connection_->ScheduleFlush();
return true;
}
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h
index ea1e81b44dc..2381aecfd9c 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h
@@ -87,6 +87,7 @@ class XDGSurfaceWrapperImpl : public ShellSurfaceWrapper {
wl::Object<zxdg_toplevel_v6> zxdg_toplevel_v6_;
wl::Object<struct xdg_surface> xdg_surface_;
wl::Object<xdg_toplevel> xdg_toplevel_;
+ wl::Object<zaura_surface> aura_surface_;
bool surface_for_popup_ = false;
diff --git a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
index 7b72165b4ff..b1c021ad330 100644
--- a/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
+++ b/chromium/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
@@ -57,7 +57,7 @@ void ZWPTextInputWrapperV1::Reset() {
void ZWPTextInputWrapperV1::Activate(WaylandWindow* window) {
zwp_text_input_v1_activate(obj_.get(), connection_->seat(),
- window->surface());
+ window->root_surface()->surface());
}
void ZWPTextInputWrapperV1::Deactivate() {
diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 894166db3f9..8de40eb6896 100644
--- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -17,17 +17,19 @@
#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
#include "ui/base/ime/linux/input_method_auralinux.h"
+#include "ui/base/ui_base_features.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/ozone/common/stub_overlay_manager.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h"
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_overlay_manager.h"
#include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_connector.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
#include "ui/ozone/platform/wayland/host/wayland_input_method_context_factory.h"
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -48,7 +50,7 @@
#if defined(WAYLAND_GBM)
#include "ui/base/ui_base_features.h"
-#include "ui/gfx/linux/gbm_wrapper.h"
+#include "ui/gfx/linux/gbm_wrapper.h" // nogncheck
#include "ui/ozone/platform/wayland/gpu/drm_render_node_handle.h"
#endif
@@ -62,32 +64,30 @@ namespace ui {
namespace {
constexpr OzonePlatform::PlatformProperties kWaylandPlatformProperties = {
- /*needs_view_token=*/false,
-
// Supporting server-side decorations requires a support of
// xdg-decorations. But this protocol has been accepted into the upstream
// recently, and it will take time before it is taken by compositors. For
// now, always use custom frames and disallow switching to server-side
// frames.
// https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988
- /*custom_frame_pref_default=*/true,
- /*use_system_title_bar=*/false,
-
- /*message_pump_type_for_gpu=*/base::MessagePumpType::DEFAULT,
-
- /*supports_vulkan_swap_chain=*/false,
+ .custom_frame_pref_default = true,
// Wayland doesn't provide clients with global screen coordinates. Instead,
// it forces clients to position windows relative to their top level windows
// if the have child-parent relationship. In case of toplevel windows,
// clients simply don't know their position on screens and always assume
// they are located at some arbitrary position.
- /*ignore_screen_bounds_for_menus=*/true,
+ .ignore_screen_bounds_for_menus = true,
+};
+
+constexpr OzonePlatform::InitializedHostProperties
+ kWaylandInitializedHostProperties = {
+ /*supports_overlays=*/false,
};
class OzonePlatformWayland : public OzonePlatform {
public:
- OzonePlatformWayland() {}
+ OzonePlatformWayland() { CHECK(features::IsUsingOzonePlatform()); }
~OzonePlatformWayland() override {}
// OzonePlatform
@@ -139,6 +139,12 @@ class OzonePlatformWayland : public OzonePlatform {
return connection_->clipboard();
}
+ int GetKeyModifiers() const override {
+ DCHECK(connection_);
+ DCHECK(connection_->event_source());
+ return connection_->event_source()->keyboard_modifiers();
+ }
+
std::unique_ptr<InputMethod> CreateInputMethod(
internal::InputMethodDelegate* delegate,
gfx::AcceleratedWidget widget) override {
@@ -187,7 +193,6 @@ class OzonePlatformWayland : public OzonePlatform {
buffer_manager_connector_ = std::make_unique<WaylandBufferManagerConnector>(
connection_->buffer_manager_host());
cursor_factory_ = std::make_unique<BitmapCursorFactoryOzone>();
- overlay_manager_ = std::make_unique<StubOverlayManager>();
input_controller_ = CreateStubInputController();
gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
@@ -199,12 +204,15 @@ class OzonePlatformWayland : public OzonePlatform {
std::make_unique<GtkUiDelegateWayland>(connection_.get());
GtkUiDelegate::SetInstance(gtk_ui_delegate_.get());
#endif
+
+ // TODO(crbug.com/1097007): report which Wayland compositor is used.
}
void InitializeGPU(const InitParams& args) override {
buffer_manager_ = std::make_unique<WaylandBufferManagerGpu>();
surface_factory_ = std::make_unique<WaylandSurfaceFactory>(
connection_.get(), buffer_manager_.get());
+ overlay_manager_ = std::make_unique<WaylandOverlayManager>();
#if defined(WAYLAND_GBM)
const base::FilePath drm_node_path = path_finder_.GetDrmRenderNodePath();
if (drm_node_path.empty()) {
@@ -227,6 +235,10 @@ class OzonePlatformWayland : public OzonePlatform {
return kWaylandPlatformProperties;
}
+ const InitializedHostProperties& GetInitializedHostProperties() override {
+ return kWaylandInitializedHostProperties;
+ }
+
void AddInterfaces(mojo::BinderMap* binders) override {
binders->Add<ozone::mojom::WaylandBufferManagerGpu>(
base::BindRepeating(
@@ -249,7 +261,6 @@ class OzonePlatformWayland : public OzonePlatform {
std::unique_ptr<WaylandConnection> connection_;
std::unique_ptr<WaylandSurfaceFactory> surface_factory_;
std::unique_ptr<CursorFactory> cursor_factory_;
- std::unique_ptr<StubOverlayManager> overlay_manager_;
std::unique_ptr<InputController> input_controller_;
std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
std::unique_ptr<WaylandInputMethodContextFactory>
@@ -258,6 +269,7 @@ class OzonePlatformWayland : public OzonePlatform {
// Objects, which solely live in the GPU process.
std::unique_ptr<WaylandBufferManagerGpu> buffer_manager_;
+ std::unique_ptr<WaylandOverlayManager> overlay_manager_;
// Provides supported buffer formats for native gpu memory buffers
// framework.
diff --git a/chromium/ui/ozone/platform/wayland/test/constants.h b/chromium/ui/ozone/platform/wayland/test/constants.h
deleted file mode 100644
index e06dc71f6b6..00000000000
--- a/chromium/ui/ozone/platform/wayland/test/constants.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
-#define UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
-
-namespace wl {
-
-constexpr char kSampleClipboardText[] = "This is a sample text for clipboard.";
-constexpr char kSampleTextForDragAndDrop[] =
- "This is a sample text for drag-and-drop.";
-constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8";
-constexpr char kTextMimeTypeText[] = "text/plain";
-
-} // namespace wl
-
-#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_CONSTANTS_H_
diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc
index 20557517f99..524b4f45bc3 100644
--- a/chromium/ui/ozone/platform/wayland/test/mock_surface.cc
+++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.cc
@@ -99,10 +99,8 @@ MockSurface* MockSurface::FromResource(wl_resource* resource) {
void MockSurface::AttachNewBuffer(wl_resource* buffer_resource,
int32_t x,
int32_t y) {
- if (attached_buffer_) {
- DCHECK(!prev_attached_buffer_);
+ if (attached_buffer_)
prev_attached_buffer_ = attached_buffer_;
- }
attached_buffer_ = buffer_resource;
Attach(buffer_resource, x, y);
@@ -113,13 +111,14 @@ void MockSurface::DestroyPrevAttachedBuffer() {
prev_attached_buffer_ = nullptr;
}
-void MockSurface::ReleasePrevAttachedBuffer() {
- if (!prev_attached_buffer_)
- return;
-
- wl_buffer_send_release(prev_attached_buffer_);
- wl_client_flush(wl_resource_get_client(prev_attached_buffer_));
- prev_attached_buffer_ = nullptr;
+void MockSurface::ReleaseBuffer(wl_resource* buffer) {
+ DCHECK(buffer);
+ wl_buffer_send_release(buffer);
+ wl_client_flush(wl_resource_get_client(buffer));
+ if (buffer == prev_attached_buffer_)
+ prev_attached_buffer_ = nullptr;
+ if (buffer == attached_buffer_)
+ attached_buffer_ = nullptr;
}
void MockSurface::SendFrameCallback() {
diff --git a/chromium/ui/ozone/platform/wayland/test/mock_surface.h b/chromium/ui/ozone/platform/wayland/test/mock_surface.h
index 412d8386713..e562a20b144 100644
--- a/chromium/ui/ozone/platform/wayland/test/mock_surface.h
+++ b/chromium/ui/ozone/platform/wayland/test/mock_surface.h
@@ -57,11 +57,14 @@ class MockSurface : public ServerObject {
frame_callback_ = callback_resource;
}
+ wl_resource* attached_buffer() const { return attached_buffer_; }
+ wl_resource* prev_attached_buffer() const { return prev_attached_buffer_; }
+
bool has_role() const { return !!xdg_surface_ || !!sub_surface_; }
void AttachNewBuffer(wl_resource* buffer_resource, int32_t x, int32_t y);
void DestroyPrevAttachedBuffer();
- void ReleasePrevAttachedBuffer();
+ void ReleaseBuffer(wl_resource* buffer);
void SendFrameCallback();
private:
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_device_manager.h b/chromium/ui/ozone/platform/wayland/test/test_data_device_manager.h
index ace5051ea37..133e884a436 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_device_manager.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_device_manager.h
@@ -34,8 +34,8 @@ class TestDataDeviceManager : public GlobalObject {
}
private:
- TestDataDevice* data_device_;
- TestDataSource* data_source_;
+ TestDataDevice* data_device_ = nullptr;
+ TestDataSource* data_source_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestDataDeviceManager);
};
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc b/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc
index b830dada81e..73736c7b002 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_offer.cc
@@ -14,16 +14,15 @@
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
-#include "ui/ozone/platform/wayland/test/constants.h"
namespace wl {
namespace {
void WriteDataOnWorkerThread(base::ScopedFD fd,
- const ui::PlatformClipboard::Data& data) {
- if (!base::WriteFileDescriptor(
- fd.get(), reinterpret_cast<const char*>(data.data()), data.size())) {
+ ui::PlatformClipboard::Data data) {
+ if (!base::WriteFileDescriptor(fd.get(), data->front_as<char>(),
+ data->size())) {
LOG(ERROR) << "Failed to write selection data to clipboard.";
}
}
@@ -81,7 +80,7 @@ void TestDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) {
}
void TestDataOffer::OnOffer(const std::string& mime_type,
- const ui::PlatformClipboard::Data& data) {
+ ui::PlatformClipboard::Data data) {
data_to_offer_[mime_type] = data;
wl_data_offer_send_offer(resource(), mime_type.c_str());
}
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_offer.h b/chromium/ui/ozone/platform/wayland/test/test_data_offer.h
index 67080d3a0a9..6b96582da7d 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_offer.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_offer.h
@@ -33,8 +33,7 @@ class TestDataOffer : public ServerObject {
~TestDataOffer() override;
void Receive(const std::string& mime_type, base::ScopedFD fd);
- void OnOffer(const std::string& mime_type,
- const ui::PlatformClipboard::Data& data);
+ void OnOffer(const std::string& mime_type, ui::PlatformClipboard::Data data);
private:
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
index 4d11ec8e9b1..28acb1f554c 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
@@ -18,7 +18,6 @@
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
-#include "ui/ozone/platform/wayland/test/constants.h"
namespace wl {
diff --git a/chromium/ui/ozone/platform/wayland/test/test_output.cc b/chromium/ui/ozone/platform/wayland/test/test_output.cc
index 1718d0d9bb5..e83e5971a8c 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_output.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_output.cc
@@ -6,6 +6,8 @@
#include <wayland-server-protocol.h>
+#include "base/optional.h"
+
namespace wl {
namespace {
@@ -17,23 +19,48 @@ TestOutput::TestOutput()
TestOutput::~TestOutput() = default;
-// Notify clients of the change for output position.
-void TestOutput::OnBind() {
- if (rect_.IsEmpty())
+void TestOutput::SetRect(const gfx::Rect& rect) {
+ pending_rect_ = rect;
+}
+
+void TestOutput::SetScale(int32_t factor) {
+ pending_scale_ = factor;
+}
+
+void TestOutput::Flush() {
+ constexpr char kUnknownMake[] = "unknown_make";
+ constexpr char kUnknownModel[] = "unknown_model";
+
+ if (!pending_rect_ && !pending_scale_)
return;
- const char* kUnknownMake = "unknown";
- const char* kUnknownModel = "unknown";
- wl_output_send_geometry(resource(), rect_.x(), rect_.y(), 0, 0, 0,
- kUnknownMake, kUnknownModel, 0);
- wl_output_send_mode(resource(), WL_OUTPUT_MODE_CURRENT, rect_.width(),
- rect_.height(), 0);
+ if (pending_rect_) {
+ rect_ = std::move(pending_rect_.value());
+ wl_output_send_geometry(resource(), rect_.x(), rect_.y(),
+ 0 /* physical_width */, 0 /* physical_height */,
+ 0 /* subpixel */, kUnknownMake, kUnknownModel,
+ 0 /* transform */);
+ wl_output_send_mode(resource(), WL_OUTPUT_MODE_CURRENT, rect_.width(),
+ rect_.height(), 0);
+ }
+
+ if (pending_scale_) {
+ scale_ = std::move(pending_scale_.value());
+ wl_output_send_scale(resource(), scale_);
+ }
wl_output_send_done(resource());
}
-void TestOutput::SetScale(int32_t factor) {
- wl_output_send_scale(resource(), factor);
- wl_output_send_done(resource());
+// Notifies clients about the changes in the output configuration, if any. Doing
+// this at bind time is the most common behavior among Wayland compositors. But
+// there are some compositors that do it "lazily". An example is ChromeOS'
+// Exosphere.
+//
+// Such behavior can be emulated with this class, by just instantiating an
+// object with no setter calls. Such calls might then be done later on demand,
+// so clients get notified about such changes when Flush() is called.
+void TestOutput::OnBind() {
+ Flush();
}
} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/test/test_output.h b/chromium/ui/ozone/platform/wayland/test/test_output.h
index 8dffd41cae2..a44c0157364 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_output.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_output.h
@@ -5,7 +5,10 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_OUTPUT_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_OUTPUT_H_
+#include <cstdint>
+
#include "base/macros.h"
+#include "base/optional.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/platform/wayland/test/global_object.h"
@@ -16,14 +19,22 @@ class TestOutput : public GlobalObject {
public:
TestOutput();
~TestOutput() override;
- void SetRect(const gfx::Rect rect) { rect_ = rect; }
- const gfx::Rect GetRect() { return rect_; }
- void OnBind() override;
+ const gfx::Rect GetRect() { return rect_; }
+ void SetRect(const gfx::Rect& rect);
void SetScale(int32_t factor);
+ void Flush();
+
+ protected:
+ void OnBind() override;
+
private:
gfx::Rect rect_;
+ int32_t scale_;
+
+ base::Optional<gfx::Rect> pending_rect_ = base::nullopt;
+ base::Optional<int32_t> pending_scale_ = base::nullopt;
DISALLOW_COPY_AND_ASSIGN(TestOutput);
};
diff --git a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
index a16655c278b..c6f0fe7cabc 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
@@ -4,10 +4,12 @@
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
-#include <stdlib.h>
#include <sys/socket.h>
#include <wayland-server.h>
+
+#include <cstdlib>
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
@@ -50,10 +52,6 @@ bool TestWaylandServerThread::Start(uint32_t shell_version) {
base::ScopedFD server_fd(fd[0]);
base::ScopedFD client_fd(fd[1]);
- // If client has not specified rect before, user standard ones.
- if (output_.GetRect().IsEmpty())
- output_.SetRect(gfx::Rect(0, 0, 800, 600));
-
if (wl_display_init_shm(display_.get()) < 0)
return false;
if (!compositor_.Initialize(display_.get()))
@@ -62,6 +60,8 @@ bool TestWaylandServerThread::Start(uint32_t shell_version) {
return false;
if (!output_.Initialize(display_.get()))
return false;
+ SetupOutputs();
+
if (!data_device_manager_.Initialize(display_.get()))
return false;
if (!seat_.Initialize(display_.get()))
@@ -115,6 +115,18 @@ MockWpPresentation* TestWaylandServerThread::EnsureWpPresentation() {
return nullptr;
}
+// By default, just make sure primary screen has bounds set. Otherwise delegates
+// it, making it possible to emulate different scenarios, such as, multi-screen,
+// lazy configuration, arbitrary ordering of the outputs metadata sending, etc.
+void TestWaylandServerThread::SetupOutputs() {
+ if (output_delegate_) {
+ output_delegate_->SetupOutputs(&output_);
+ return;
+ }
+ if (output_.GetRect().IsEmpty())
+ output_.SetRect(gfx::Rect{0, 0, 800, 600});
+}
+
void TestWaylandServerThread::DoPause() {
base::RunLoop().RunUntilIdle();
pause_event_.Signal();
diff --git a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
index 75a5b8d8c1f..e3d4d489202 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
@@ -5,9 +5,10 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
+#include <wayland-server-core.h>
+
#include <memory>
#include <vector>
-#include <wayland-server-core.h>
#include "base/message_loop/message_pump_libevent.h"
#include "base/synchronization/waitable_event.h"
@@ -37,6 +38,8 @@ struct DisplayDeleter {
class TestWaylandServerThread : public base::Thread,
base::MessagePumpLibevent::FdWatcher {
public:
+ class OutputDelegate;
+
TestWaylandServerThread();
~TestWaylandServerThread() override;
@@ -84,7 +87,12 @@ class TestWaylandServerThread : public base::Thread,
wl_display* display() const { return display_.get(); }
+ void set_output_delegate(OutputDelegate* delegate) {
+ output_delegate_ = delegate;
+ }
+
private:
+ void SetupOutputs();
void DoPause();
std::unique_ptr<base::MessagePump> CreateMessagePump();
@@ -116,9 +124,22 @@ class TestWaylandServerThread : public base::Thread,
base::MessagePumpLibevent::FdWatchController controller_;
+ OutputDelegate* output_delegate_ = nullptr;
+
DISALLOW_COPY_AND_ASSIGN(TestWaylandServerThread);
};
+class TestWaylandServerThread::OutputDelegate {
+ public:
+ // Tests may implement this such that it emulates different display/output
+ // test scenarios. For example, multi-screen, lazy configuration, arbitrary
+ // ordering of the outputs metadata events, etc.
+ virtual void SetupOutputs(TestOutput* primary_output) = 0;
+
+ protected:
+ virtual ~OutputDelegate() = default;
+};
+
} // namespace wl
#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
index 67c8f2bbaf2..4ce3ad031cd 100644
--- a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
+++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
@@ -64,11 +64,12 @@ void WaylandTest::SetUp() {
// Pause the server after it has responded to all incoming events.
server_.Pause();
- surface_ = server_.GetObject<wl::MockSurface>(widget_);
+ auto id = window_->root_surface()->GetSurfaceId();
+ surface_ = server_.GetObject<wl::MockSurface>(id);
ASSERT_TRUE(surface_);
// The surface must be activated before buffers are attached.
- ActivateSurface(server_.GetObject<wl::MockSurface>(widget_)->xdg_surface());
+ ActivateSurface(server_.GetObject<wl::MockSurface>(id)->xdg_surface());
Sync();
diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
index acf7594d5e2..8721395d80b 100644
--- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -456,7 +456,8 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
false /* fail */);
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
@@ -493,7 +494,7 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
Sync();
@@ -534,7 +535,7 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
// Now, send the release callback. The host manager must send the submission
// and presentation callbacks in correct order.
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -565,7 +566,8 @@ TEST_P(WaylandBufferManagerTest,
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
auto* mock_wp_presentation = server_.EnsureWpPresentation();
ASSERT_TRUE(mock_wp_presentation);
@@ -685,7 +687,8 @@ TEST_P(WaylandBufferManagerTest,
ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
false /* fail */);
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
@@ -727,7 +730,7 @@ TEST_P(WaylandBufferManagerTest,
mock_wp_presentation->ReleasePresentationCallback();
// Release previous buffer and commit third buffer.
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
Sync();
@@ -742,7 +745,7 @@ TEST_P(WaylandBufferManagerTest,
Sync();
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -804,7 +807,9 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
constexpr uint32_t kDmabufBufferId2 = 2;
const gfx::AcceleratedWidget widget = window_->GetWidget();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
@@ -893,7 +898,7 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) {
auto widget = temp_window->GetWidget();
// Subsurface doesn't have an interface for sending configure events.
- // Thus, WaylandSubsurface notifies the manager that the window is
+ // Thus, WaylandAuxiliaryWindow notifies the manager that the window is
// activated upon creation of the subsurface. Skip calling Show() and call
// later then.
if (type != PlatformWindowType::kTooltip)
@@ -901,7 +906,9 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) {
Sync();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ temp_window->root_surface()->GetSurfaceId());
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
@@ -968,7 +975,8 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
constexpr uint32_t kNumberOfCommits = 3;
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
@@ -1011,7 +1019,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
Sync();
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -1047,7 +1055,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
Sync();
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -1119,7 +1127,8 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
auto widget = temp_window->GetWidget();
auto bounds = temp_window->GetBounds();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ temp_window->root_surface()->GetSurfaceId());
ASSERT_TRUE(mock_surface);
ActivateSurface(mock_surface->xdg_surface());
@@ -1164,7 +1173,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
Sync();
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
Sync();
@@ -1180,10 +1189,217 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
}
+// Tests that OnSubmission and OnPresentation are properly triggered if a buffer
+// is committed twice in a row and those buffers are destroyed.
+TEST_P(WaylandBufferManagerTest, DestroyBufferCommittedTwiceInARow) {
+ constexpr uint32_t kBufferId1 = 1;
+ constexpr uint32_t kBufferId2 = 2;
+
+ const gfx::AcceleratedWidget widget = window_->GetWidget();
+ const gfx::Rect bounds = window_->GetBounds();
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
+
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
+ false /* fail */);
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Can't call OnSubmission until there is a release.
+ EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Destroying buffer2 should do nothing yet.
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Destroying buffer1 should give us two acks for buffer2.
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK))
+ .Times(2);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(2);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+}
+
+// Tests that OnSubmission and OnPresentation are properly triggered if a buffer
+// is committed twice in a row and then released.
+TEST_P(WaylandBufferManagerTest, ReleaseBufferCommittedTwiceInARow) {
+ constexpr uint32_t kBufferId1 = 1;
+ constexpr uint32_t kBufferId2 = 2;
+
+ const gfx::AcceleratedWidget widget = window_->GetWidget();
+ const gfx::Rect bounds = window_->GetBounds();
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
+
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
+ false /* fail */);
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+ auto* wl_buffer1 = mock_surface->attached_buffer();
+
+ // Can't call OnSubmission until there is a release.
+ EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Releasing buffer1 should trigger two acks for buffer2.
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK))
+ .Times(2);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(2);
+ mock_surface->ReleaseBuffer(wl_buffer1);
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
+}
+
+// Tests that OnSubmission and OnPresentation callbacks are properly called
+// even if buffers are not released in the same order they were committed.
+TEST_P(WaylandBufferManagerTest, ReleaseOrderDifferentToCommitOrder) {
+ constexpr uint32_t kBufferId1 = 1;
+ constexpr uint32_t kBufferId2 = 2;
+ constexpr uint32_t kBufferId3 = 3;
+
+ const gfx::AcceleratedWidget widget = window_->GetWidget();
+ const gfx::Rect bounds = window_->GetBounds();
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
+
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
+ false /* fail */);
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
+ EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+ auto* wl_buffer1 = mock_surface->attached_buffer();
+
+ // Can't call OnSubmission until there is a release.
+ EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+ EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(2);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+ auto* wl_buffer2 = mock_surface->attached_buffer();
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds);
+ mock_surface->SendFrameCallback();
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Releasing buffer2 can't trigger OnSubmission for buffer3, because
+ // OnSubmission for buffer2 has not been sent yet.
+ EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+ mock_surface->ReleaseBuffer(wl_buffer2);
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ // Releasing buffer1 should trigger acks for both.
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId3, _)).Times(1);
+ mock_surface->ReleaseBuffer(wl_buffer1);
+ Sync();
+
+ testing::Mock::VerifyAndClearExpectations(&mock_surface_gpu);
+
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/);
+}
+
// This test verifies that submitting the buffer more than once results in
// OnSubmission callback as Wayland compositor is not supposed to release the
// buffer committed twice.
-TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
+TEST_P(WaylandBufferManagerTest,
+ OnSubmissionCalledForBufferCommitedTwiceInARow) {
constexpr uint32_t kBufferId1 = 1;
constexpr uint32_t kBufferId2 = 2;
@@ -1207,7 +1423,8 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
.Times(1);
ASSERT_TRUE(!connection_->presentation());
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(
+ window_->root_surface()->GetSurfaceId());
EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
EXPECT_CALL(*mock_surface,
@@ -1254,7 +1471,7 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
Sync();
@@ -1294,9 +1511,6 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
EXPECT_CALL(*mock_surface, DamageBuffer(_, _, _, _)).Times(0);
EXPECT_CALL(*mock_surface, Commit()).Times(0);
- // It must be ok if Wayland compositor decides to release the buffer at some
- // point.
- mock_surface->ReleasePrevAttachedBuffer();
mock_surface->SendFrameCallback();
Sync();
@@ -1329,7 +1543,7 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
.Times(1);
EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
- mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
mock_surface->SendFrameCallback();
Sync();
@@ -1340,6 +1554,37 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
}
+// Tests that submitting a single buffer only receives an OnSubmission. This is
+// required behaviour to make sure that submitting buffers in a quiescent state
+// will be immediately acked.
+TEST_P(WaylandBufferManagerTest, OnSubmissionCalledForSingleBuffer) {
+ constexpr uint32_t kBufferId1 = 1;
+
+ const gfx::AcceleratedWidget widget = window_->GetWidget();
+ const gfx::Rect bounds = window_->GetBounds();
+
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+ false /* fail */);
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+ Sync();
+
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandBufferManagerTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/x11/BUILD.gn b/chromium/ui/ozone/platform/x11/BUILD.gn
index 35b64eb2caa..6a501134142 100644
--- a/chromium/ui/ozone/platform/x11/BUILD.gn
+++ b/chromium/ui/ozone/platform/x11/BUILD.gn
@@ -58,7 +58,7 @@ source_set("x11") {
"//ui/ozone:ozone_base",
"//ui/ozone/common",
"//ui/platform_window",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
"//ui/platform_window/x11",
]
@@ -102,7 +102,6 @@ source_set("x11_unittests") {
"//ui/events:test_support",
"//ui/events/devices/x11",
"//ui/events/platform/x11",
- "//ui/events/x:unittests",
"//ui/ozone:platform",
"//ui/ozone:test_support",
"//ui/ozone/common",
diff --git a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc
index 5b6673be54e..2d2550ae517 100644
--- a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc
+++ b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.cc
@@ -22,4 +22,8 @@ void GLEGLUtilityX11::ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
ChoosePlatformCustomAlphaAndBufferSize(alpha_size, buffer_size);
}
+bool GLEGLUtilityX11::IsTransparentBackgroundSupported() const {
+ return ui::IsTransparentBackgroundSupported();
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h
index e9a9981bbe2..e68d20a8118 100644
--- a/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h
+++ b/chromium/ui/ozone/platform/x11/gl_egl_utility_x11.h
@@ -23,6 +23,7 @@ class GLEGLUtilityX11 : public PlatformGLEGLUtility {
std::vector<EGLAttrib>* display_attributes) override;
void ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
EGLint* buffer_size) override;
+ bool IsTransparentBackgroundSupported() const override;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.cc b/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.cc
index 1806337fff4..1e8373db915 100644
--- a/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.cc
+++ b/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.cc
@@ -4,23 +4,45 @@
#include "ui/ozone/platform/x11/gl_surface_egl_readback_x11.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+#include "ui/base/x/x11_util.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
+namespace {
+
+constexpr x11::GraphicsContext kNoGC = x11::GraphicsContext{};
+
+}
+
GLSurfaceEglReadbackX11::GLSurfaceEglReadbackX11(gfx::AcceleratedWidget window)
- : window_(window), xdisplay_(gfx::GetXDisplay()) {}
+ : window_(static_cast<x11::Window>(window)),
+ connection_(x11::Connection::Get()) {}
bool GLSurfaceEglReadbackX11::Initialize(gl::GLSurfaceFormat format) {
if (!GLSurfaceEglReadback::Initialize(format))
return false;
// We don't need to reinitialize |window_graphics_context_|.
- if (window_graphics_context_)
+ if (window_graphics_context_ != kNoGC)
return true;
- window_graphics_context_ = XCreateGC(xdisplay_, window_, 0, nullptr);
- if (!window_graphics_context_) {
+ window_graphics_context_ = connection_->GenerateId<x11::GraphicsContext>();
+ auto gc_future = connection_->CreateGC({window_graphics_context_, window_});
+
+ if (auto attributes = connection_->GetWindowAttributes({window_}).Sync()) {
+ visual_ = attributes->visual;
+ } else {
+ DLOG(ERROR) << "Failed to get attributes for window "
+ << static_cast<uint32_t>(window_);
+ Destroy();
+ return false;
+ }
+
+ if (gc_future.Sync().error) {
DLOG(ERROR) << "XCreateGC failed";
Destroy();
return false;
@@ -30,87 +52,30 @@ bool GLSurfaceEglReadbackX11::Initialize(gl::GLSurfaceFormat format) {
}
void GLSurfaceEglReadbackX11::Destroy() {
- if (pixmap_graphics_context_) {
- XFreeGC(xdisplay_, pixmap_graphics_context_);
- pixmap_graphics_context_ = nullptr;
+ if (window_graphics_context_ != kNoGC) {
+ connection_->FreeGC({window_graphics_context_});
+ window_graphics_context_ = kNoGC;
}
- if (pixmap_) {
- XFreePixmap(xdisplay_, pixmap_);
- pixmap_ = x11::None;
- }
-
- if (window_graphics_context_) {
- XFreeGC(xdisplay_, window_graphics_context_);
- window_graphics_context_ = nullptr;
- }
-
- XSync(xdisplay_, x11::False);
+ connection_->Sync();
GLSurfaceEglReadback::Destroy();
}
-bool GLSurfaceEglReadbackX11::Resize(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- bool has_alpha) {
- if (!GLSurfaceEglReadback::Resize(size, scale_factor, color_space,
- has_alpha)) {
- return false;
- }
-
- XWindowAttributes attributes;
- if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
- DLOG(ERROR) << "XGetWindowAttributes failed";
- return false;
- }
-
- // Destroy the previous pixmap and graphics context.
- if (pixmap_graphics_context_) {
- XFreeGC(xdisplay_, pixmap_graphics_context_);
- pixmap_graphics_context_ = nullptr;
- }
- if (pixmap_) {
- XFreePixmap(xdisplay_, pixmap_);
- pixmap_ = x11::None;
- }
-
- // Recreate a pixmap to hold the frame.
- pixmap_ = XCreatePixmap(xdisplay_, window_, size.width(), size.height(),
- attributes.depth);
- if (!pixmap_) {
- DLOG(ERROR) << "XCreatePixmap failed";
- return false;
- }
-
- // Recreate a graphics context for the pixmap.
- pixmap_graphics_context_ = XCreateGC(xdisplay_, pixmap_, 0, nullptr);
- if (!pixmap_graphics_context_) {
- DLOG(ERROR) << "XCreateGC failed";
- return false;
- }
-
- return true;
-}
-
GLSurfaceEglReadbackX11::~GLSurfaceEglReadbackX11() {
Destroy();
}
bool GLSurfaceEglReadbackX11::HandlePixels(uint8_t* pixels) {
- XWindowAttributes attributes;
- if (!XGetWindowAttributes(xdisplay_, window_, &attributes)) {
- DLOG(ERROR) << "XGetWindowAttributes failed";
- return false;
- }
+ SkImageInfo image_info =
+ SkImageInfo::Make(GetSize().width(), GetSize().height(),
+ kBGRA_8888_SkColorType, kPremul_SkAlphaType);
+ SkPixmap pixmap(image_info, pixels, image_info.minRowBytes());
// Copy pixels into pixmap and then update the XWindow.
const gfx::Size size = GetSize();
- gfx::PutARGBImage(xdisplay_, attributes.visual, attributes.depth, pixmap_,
- pixmap_graphics_context_, pixels, size.width(),
- size.height());
- XCopyArea(xdisplay_, pixmap_, window_, window_graphics_context_, 0, 0,
- size.width(), size.height(), 0, 0);
+ DrawPixmap(connection_, visual_, window_, window_graphics_context_, pixmap, 0,
+ 0, 0, 0, size.width(), size.height());
return true;
}
diff --git a/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.h b/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.h
index ed56bcaa4f4..8f8396569f0 100644
--- a/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.h
+++ b/chromium/ui/ozone/platform/x11/gl_surface_egl_readback_x11.h
@@ -6,6 +6,7 @@
#define UI_OZONE_PLATFORM_X11_GL_SURFACE_EGL_READBACK_X11_H_
#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
#include "ui/ozone/common/gl_surface_egl_readback.h"
namespace ui {
@@ -13,15 +14,11 @@ namespace ui {
// GLSurface implementation that copies pixels from readback to an XWindow.
class GLSurfaceEglReadbackX11 : public GLSurfaceEglReadback {
public:
- GLSurfaceEglReadbackX11(gfx::AcceleratedWidget window);
+ explicit GLSurfaceEglReadbackX11(gfx::AcceleratedWidget window);
// gl::GLSurface:
bool Initialize(gl::GLSurfaceFormat format) override;
void Destroy() override;
- bool Resize(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- bool has_alpha) override;
private:
~GLSurfaceEglReadbackX11() override;
@@ -29,11 +26,10 @@ class GLSurfaceEglReadbackX11 : public GLSurfaceEglReadback {
// gl::GLSurfaceEglReadback:
bool HandlePixels(uint8_t* pixels) override;
- const gfx::AcceleratedWidget window_;
- Display* const xdisplay_;
- GC window_graphics_context_ = nullptr;
- GC pixmap_graphics_context_ = nullptr;
- Pixmap pixmap_ = x11::None;
+ const x11::Window window_;
+ x11::Connection* const connection_;
+ x11::GraphicsContext window_graphics_context_{};
+ x11::VisualId visual_{};
DISALLOW_COPY_AND_ASSIGN(GLSurfaceEglReadbackX11);
};
diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc
index 394ef374c81..4997e86af1a 100644
--- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/message_loop/message_pump_type.h"
+#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/base/buildflags.h"
@@ -22,6 +23,7 @@
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/events/x/events_x_utils.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/ozone/common/stub_overlay_manager.h"
@@ -63,15 +65,19 @@ namespace ui {
namespace {
constexpr OzonePlatform::PlatformProperties kX11PlatformProperties{
- /*needs_view_token=*/false,
- /*custom_frame_pref_default=*/false,
- /*use_system_title_bar=*/true,
+ .needs_view_token = false,
+ .custom_frame_pref_default = false,
+ .use_system_title_bar = true,
// When the Ozone X11 backend is running, use a UI loop to grab Expose
// events. See GLSurfaceGLX and https://crbug.com/326995.
- /*message_pump_type_for_gpu=*/base::MessagePumpType::UI,
- /*supports_vulkan_swap_chain=*/true,
-};
+ .message_pump_type_for_gpu = base::MessagePumpType::UI,
+ // When the Ozone X11 backend is running, use a UI loop to dispatch
+ // SHM completion events.
+ .message_pump_type_for_viz_compositor = base::MessagePumpType::UI,
+ .supports_vulkan_swap_chain = true,
+ .platform_shows_drag_image = false,
+ .supports_global_application_menus = true};
// Singleton OzonePlatform implementation for X11 platform.
class OzonePlatformX11 : public OzonePlatform,
@@ -132,6 +138,8 @@ class OzonePlatformX11 : public OzonePlatform,
return gl_egl_utility_.get();
}
+ int GetKeyModifiers() const override { return GetModifierKeyState(); }
+
std::unique_ptr<InputMethod> CreateInputMethod(
internal::InputMethodDelegate* delegate,
gfx::AcceleratedWidget) override {
@@ -184,9 +192,12 @@ class OzonePlatformX11 : public OzonePlatform,
#if BUILDFLAG(USE_GTK)
DCHECK(!GtkUiDelegate::instance());
- gtk_ui_delegate_ = std::make_unique<GtkUiDelegateX11>(gfx::GetXDisplay());
+ gtk_ui_delegate_ =
+ std::make_unique<GtkUiDelegateX11>(x11::Connection::Get());
GtkUiDelegate::SetInstance(gtk_ui_delegate_.get());
#endif
+
+ base::UmaHistogramEnumeration("Linux.WindowManager", GetWindowManagerUMA());
}
void InitializeGPU(const InitParams& params) override {
@@ -197,7 +208,12 @@ class OzonePlatformX11 : public OzonePlatform,
if (!params.single_process)
CreatePlatformEventSource();
- surface_factory_ozone_ = std::make_unique<X11SurfaceFactory>();
+ // Set up the X11 connection before the sandbox gets set up. This cannot be
+ // done later since opening the connection requires socket() and connect().
+ auto connection = x11::Connection::Get()->Clone();
+ connection->DetachFromSequence();
+ surface_factory_ozone_ =
+ std::make_unique<X11SurfaceFactory>(std::move(connection));
gl_egl_utility_ = std::make_unique<GLEGLUtilityX11>();
}
@@ -229,10 +245,10 @@ class OzonePlatformX11 : public OzonePlatform,
if (common_initialized_)
return;
- // If XOpenDisplay() failed there is nothing we can do. Crash here instead
- // of crashing later. If you are crashing here, make sure there is an X
- // server running and $DISPLAY is set.
- CHECK(gfx::GetXDisplay()) << "Missing X server or $DISPLAY";
+ // If opening the connection failed there is nothing we can do. Crash here
+ // instead of crashing later. If you are crashing here, make sure there is
+ // an X server running and $DISPLAY is set.
+ CHECK(x11::Connection::Get()) << "Missing X server or $DISPLAY";
ui::SetDefaultX11ErrorHandlers();
@@ -244,8 +260,8 @@ class OzonePlatformX11 : public OzonePlatform,
if (event_source_)
return;
- XDisplay* display = gfx::GetXDisplay();
- event_source_ = std::make_unique<X11EventSource>(display);
+ auto* connection = x11::Connection::Get();
+ event_source_ = std::make_unique<X11EventSource>(connection);
}
bool common_initialized_ = false;
diff --git a/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc b/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc
index 59cd3740d63..ce6c1d8fd1e 100644
--- a/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc
+++ b/chromium/ui/ozone/platform/x11/x11_canvas_surface.cc
@@ -11,13 +11,8 @@
namespace ui {
-X11CanvasSurface::X11CanvasSurface(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> gpu_task_runner)
- : task_runner_(base::SequencedTaskRunnerHandle::Get()),
- x11_software_bitmap_presenter_(widget,
- task_runner_.get(),
- std::move(gpu_task_runner)) {}
+X11CanvasSurface::X11CanvasSurface(gfx::AcceleratedWidget widget)
+ : x11_software_bitmap_presenter_(widget) {}
X11CanvasSurface::~X11CanvasSurface() = default;
diff --git a/chromium/ui/ozone/platform/x11/x11_canvas_surface.h b/chromium/ui/ozone/platform/x11/x11_canvas_surface.h
index a93e22677a0..72a4181fd60 100644
--- a/chromium/ui/ozone/platform/x11/x11_canvas_surface.h
+++ b/chromium/ui/ozone/platform/x11/x11_canvas_surface.h
@@ -28,8 +28,7 @@ namespace ui {
// the software output is destroyed.
class X11CanvasSurface : public SurfaceOzoneCanvas {
public:
- X11CanvasSurface(gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> gpu_task_runner);
+ explicit X11CanvasSurface(gfx::AcceleratedWidget widget);
~X11CanvasSurface() override;
// SurfaceOzoneCanvas overrides:
@@ -45,8 +44,6 @@ class X11CanvasSurface : public SurfaceOzoneCanvas {
// Current surface we paint to.
sk_sp<SkSurface> surface_;
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
// Helper X11 bitmap presenter that presents the contents.
X11SoftwareBitmapPresenter x11_software_bitmap_presenter_;
diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc
index bea9cffe465..16a04768639 100644
--- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc
+++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.cc
@@ -25,23 +25,21 @@ namespace {
const char kChromeSelection[] = "CHROME_SELECTION";
const char kClipboard[] = "CLIPBOARD";
-const char kString[] = "STRING";
const char kTargets[] = "TARGETS";
const char kTimestamp[] = "TIMESTAMP";
-const char kUtf8String[] = "UTF8_STRING";
// Helps to allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
void ExpandTypes(std::vector<std::string>* list) {
bool has_mime_type_text = Contains(*list, ui::kMimeTypeText);
- bool has_string = Contains(*list, kString);
+ bool has_string = Contains(*list, kMimeTypeLinuxString);
bool has_mime_type_utf8 = Contains(*list, kMimeTypeTextUtf8);
- bool has_utf8_string = Contains(*list, kUtf8String);
+ bool has_utf8_string = Contains(*list, kMimeTypeLinuxUtf8String);
if (has_mime_type_text && !has_string)
- list->push_back(kString);
+ list->push_back(kMimeTypeLinuxString);
if (has_string && !has_mime_type_text)
list->push_back(ui::kMimeTypeText);
if (has_mime_type_utf8 && !has_utf8_string)
- list->push_back(kUtf8String);
+ list->push_back(kMimeTypeLinuxUtf8String);
if (has_utf8_string && !has_mime_type_utf8)
list->push_back(kMimeTypeTextUtf8);
}
@@ -65,7 +63,7 @@ struct X11ClipboardOzone::SelectionState {
std::vector<std::string> mime_types;
// Data most recently read from remote clipboard.
- std::vector<unsigned char> data;
+ PlatformClipboard::Data data;
// Mime type of most recently read data from remote clipboard.
std::string data_mime_type;
@@ -86,6 +84,8 @@ X11ClipboardOzone::X11ClipboardOzone()
x_property_(gfx::GetAtom(kChromeSelection)),
connection_(x11::Connection::Get()),
x_window_(CreateDummyWindow("Chromium Clipboard Window")) {
+ connection_->xfixes().QueryVersion(
+ {x11::XFixes::major_version, x11::XFixes::minor_version});
if (!connection_->xfixes().present())
return;
using_xfixes_ = true;
@@ -114,7 +114,7 @@ bool X11ClipboardOzone::DispatchXEvent(x11::Event* xev) {
if (auto* notify = xev->As<x11::SelectionNotifyEvent>())
return notify->requestor == x_window_ && OnSelectionNotify(*notify);
if (auto* notify = xev->As<x11::XFixes::SelectionNotifyEvent>())
- return notify->owner == x_window_ && OnSetSelectionOwnerNotify(*notify);
+ return notify->window == x_window_ && OnSetSelectionOwnerNotify(*notify);
return false;
}
@@ -161,15 +161,17 @@ bool X11ClipboardOzone::OnSelectionRequest(
std::string key = target_name;
// Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
- if (key == kUtf8String && !Contains(offer_data_map, kUtf8String)) {
+ if (key == kMimeTypeLinuxUtf8String &&
+ !Contains(offer_data_map, kMimeTypeLinuxUtf8String)) {
key = kMimeTypeTextUtf8;
- } else if (key == kString && !Contains(offer_data_map, kString)) {
+ } else if (key == kMimeTypeLinuxString &&
+ !Contains(offer_data_map, kMimeTypeLinuxString)) {
key = kMimeTypeText;
}
auto it = offer_data_map.find(key);
if (it != offer_data_map.end()) {
ui::SetArrayProperty(event.requestor, event.property, event.target,
- it->second);
+ it->second->data());
}
}
@@ -226,7 +228,8 @@ bool X11ClipboardOzone::OnSelectionNotify(
ui::GetArrayProperty(x_window_, x_property_, &data, &type);
ui::DeleteProperty(x_window_, x_property_);
if (type != x11::Atom::None)
- selection_state.data = std::move(data);
+ selection_state.data = scoped_refptr<base::RefCountedBytes>(
+ base::RefCountedBytes::TakeVector(&data));
// If we have a saved callback, invoke it now, otherwise this was a prefetch
// and we have already saved |data_| for the next call to
@@ -238,6 +241,13 @@ bool X11ClipboardOzone::OnSelectionNotify(
.Run(selection_state.data);
}
return true;
+ } else if (static_cast<x11::Atom>(event.property) == x11::Atom::None &&
+ selection_state.request_clipboard_data_callback) {
+ // If the remote peer could not send data in the format we requested,
+ // or failed for any reason, we will send empty data.
+ std::move(selection_state.request_clipboard_data_callback)
+ .Run(selection_state.data);
+ return true;
}
return false;
@@ -251,7 +261,7 @@ bool X11ClipboardOzone::OnSetSelectionOwnerNotify(
auto& selection_state = GetSelectionState(selection);
selection_state.mime_types.clear();
selection_state.data_mime_type.clear();
- selection_state.data.clear();
+ selection_state.data.reset();
QueryTargets(selection);
}
@@ -299,14 +309,14 @@ void X11ClipboardOzone::QueryTargets(x11::Atom selection) {
void X11ClipboardOzone::ReadRemoteClipboard(x11::Atom selection) {
auto& selection_state = GetSelectionState(selection);
- selection_state.data.clear();
+ selection_state.data.reset();
// Allow conversions for text/plain[;charset=utf-8] <=> [UTF8_]STRING.
std::string target = selection_state.data_mime_type;
if (!Contains(selection_state.mime_types, target)) {
if (target == kMimeTypeText) {
- target = kString;
+ target = kMimeTypeLinuxString;
} else if (target == kMimeTypeTextUtf8) {
- target = kUtf8String;
+ target = kMimeTypeLinuxUtf8String;
}
}
@@ -344,8 +354,9 @@ void X11ClipboardOzone::RequestClipboardData(
// If we have already prefetched the clipboard for the correct mime type,
// then send it right away, otherwise save the callback and attempt to get the
// requested mime type from the remote clipboard.
- if (!using_xfixes_ || (selection_state.data_mime_type == mime_type &&
- !selection_state.data.empty())) {
+ if (!using_xfixes_ ||
+ (selection_state.data_mime_type == mime_type && selection_state.data &&
+ !selection_state.data->data().empty())) {
data_map->emplace(mime_type, selection_state.data);
std::move(callback).Run(selection_state.data);
return;
@@ -394,4 +405,8 @@ void X11ClipboardOzone::SetSequenceNumberUpdateCb(
update_sequence_cb_ = std::move(cb);
}
+bool X11ClipboardOzone::IsSelectionBufferAvailable() const {
+ return true;
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h
index 478205cd472..a8c0c96f470 100644
--- a/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h
+++ b/chromium/ui/ozone/platform/x11/x11_clipboard_ozone.h
@@ -49,6 +49,7 @@ class X11ClipboardOzone : public PlatformClipboard, public XEventDispatcher {
bool IsSelectionOwner(ClipboardBuffer buffer) override;
void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override;
+ bool IsSelectionBufferAvailable() const override;
private:
struct SelectionState;
diff --git a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc
index 7ede65f0e9c..4c3b56653dd 100644
--- a/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc
+++ b/chromium/ui/ozone/platform/x11/x11_os_exchange_data_provider_ozone.cc
@@ -9,6 +9,7 @@
#include "base/check.h"
#include "base/notreached.h"
#include "ui/base/x/selection_utils.h"
+#include "ui/gfx/x/xproto.h"
namespace ui {
@@ -35,17 +36,11 @@ std::unique_ptr<OSExchangeDataProvider> X11OSExchangeDataProviderOzone::Clone()
return std::move(ret);
}
-bool X11OSExchangeDataProviderOzone::DispatchXEvent(x11::Event* x11_event) {
- XEvent* xev = &x11_event->xlib_event();
- if (xev->xany.window != static_cast<uint32_t>(x_window()))
- return false;
-
- switch (xev->type) {
- case x11::SelectionRequestEvent::opcode:
- selection_owner().OnSelectionRequest(*x11_event);
- return true;
- default:
- NOTIMPLEMENTED();
+bool X11OSExchangeDataProviderOzone::DispatchXEvent(x11::Event* xev) {
+ auto* selection_request = xev->As<x11::SelectionRequestEvent>();
+ if (selection_request && selection_request->owner == x_window()) {
+ selection_owner().OnSelectionRequest(*xev);
+ return true;
}
return false;
}
diff --git a/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
index 5b2ac245341..d98637079cd 100644
--- a/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
+++ b/chromium/ui/ozone/platform/x11/x11_screen_ozone_unittest.cc
@@ -55,8 +55,8 @@ class X11ScreenOzoneTest : public testing::Test {
~X11ScreenOzoneTest() override = default;
void SetUp() override {
- XDisplay* display = gfx::GetXDisplay();
- event_source_ = std::make_unique<X11EventSource>(display);
+ auto* connection = x11::Connection::Get();
+ event_source_ = std::make_unique<X11EventSource>(connection);
primary_display_ = std::make_unique<display::Display>(
NextDisplayId(), kPrimaryDisplayBounds);
screen_ = std::make_unique<X11ScreenOzone>();
diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc
index d747464e40f..0778871f45a 100644
--- a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc
+++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -7,6 +7,8 @@
#include <memory>
#include "gpu/vulkan/buildflags.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/gl/gl_surface_egl.h"
@@ -73,11 +75,13 @@ class GLOzoneEGLX11 : public GLOzoneEGL {
} // namespace
-X11SurfaceFactory::X11SurfaceFactory()
+X11SurfaceFactory::X11SurfaceFactory(
+ std::unique_ptr<x11::Connection> connection)
: glx_implementation_(std::make_unique<GLOzoneGLX>()),
- egl_implementation_(std::make_unique<GLOzoneEGLX11>()) {}
+ egl_implementation_(std::make_unique<GLOzoneEGLX11>()),
+ connection_(std::move(connection)) {}
-X11SurfaceFactory::~X11SurfaceFactory() {}
+X11SurfaceFactory::~X11SurfaceFactory() = default;
std::vector<gl::GLImplementation>
X11SurfaceFactory::GetAllowedGLImplementations() {
@@ -107,9 +111,16 @@ X11SurfaceFactory::CreateVulkanImplementation(bool allow_protected_memory,
#endif
std::unique_ptr<SurfaceOzoneCanvas> X11SurfaceFactory::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
- return std::make_unique<X11CanvasSurface>(widget, std::move(task_runner));
+ gfx::AcceleratedWidget widget) {
+ // X11SoftwareBitmapPresenter (created via X11CanvasSurface) requres a
+ // Connection TLS instance and a PlatformEventSource.
+ if (connection_) {
+ auto* connection = connection_.get();
+ x11::Connection::Set(std::move(connection_));
+ connection->platform_event_source =
+ std::make_unique<X11EventSource>(connection);
+ }
+ return std::make_unique<X11CanvasSurface>(widget);
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.h b/chromium/ui/ozone/platform/x11/x11_surface_factory.h
index 33dbb696253..fb5431bf47a 100644
--- a/chromium/ui/ozone/platform/x11/x11_surface_factory.h
+++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "gpu/vulkan/buildflags.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gl/gl_surface.h"
#include "ui/ozone/public/gl_ozone.h"
#include "ui/ozone/public/surface_factory_ozone.h"
@@ -19,7 +20,7 @@ namespace ui {
// Handles GL initialization and surface/context creation for X11.
class X11SurfaceFactory : public SurfaceFactoryOzone {
public:
- X11SurfaceFactory();
+ explicit X11SurfaceFactory(std::unique_ptr<x11::Connection> connection);
~X11SurfaceFactory() override;
// SurfaceFactoryOzone:
@@ -31,13 +32,14 @@ class X11SurfaceFactory : public SurfaceFactoryOzone {
bool enforce_protected_memory) override;
#endif
std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+ gfx::AcceleratedWidget widget) override;
private:
std::unique_ptr<GLOzone> glx_implementation_;
std::unique_ptr<GLOzone> egl_implementation_;
+ std::unique_ptr<x11::Connection> connection_;
+
DISALLOW_COPY_AND_ASSIGN(X11SurfaceFactory);
};
diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
index ddf6448c669..07c43384e1a 100644
--- a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
+++ b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
@@ -70,8 +70,8 @@ class X11WindowOzoneTest : public testing::Test {
~X11WindowOzoneTest() override = default;
void SetUp() override {
- XDisplay* display = gfx::GetXDisplay();
- event_source_ = std::make_unique<X11EventSource>(display);
+ auto* connection = x11::Connection::Get();
+ event_source_ = std::make_unique<X11EventSource>(connection);
test_screen_ = new TestScreen();
display::Screen::SetScreenInstance(test_screen_);
@@ -93,12 +93,9 @@ class X11WindowOzoneTest : public testing::Test {
}
void DispatchXEvent(x11::Event* event, gfx::AcceleratedWidget widget) {
- DCHECK_EQ(x11::GeGenericEvent::opcode, event->xlib_event().type);
- XIDeviceEvent* device_event =
- static_cast<XIDeviceEvent*>(event->xlib_event().xcookie.data);
- device_event->event = widget;
- event->As<x11::Input::DeviceEvent>()->event =
- static_cast<x11::Window>(widget);
+ auto* device_event = event->As<x11::Input::DeviceEvent>();
+ DCHECK(device_event);
+ device_event->event = static_cast<x11::Window>(widget);
event_source_->ProcessXEvent(event);
}
diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc
index 6b12bb5dff6..43a27b43952 100644
--- a/chromium/ui/ozone/public/input_controller.cc
+++ b/chromium/ui/ozone/public/input_controller.cc
@@ -67,6 +67,10 @@ class StubInputController : public InputController {
void GetGesturePropertiesService(
mojo::PendingReceiver<ui::ozone::mojom::GesturePropertiesService>
receiver) override {}
+ void PlayVibrationEffect(int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) override {}
+ void StopVibration(int id) override {}
private:
DISALLOW_COPY_AND_ASSIGN(StubInputController);
diff --git a/chromium/ui/ozone/public/input_controller.h b/chromium/ui/ozone/public/input_controller.h
index 6de884cb29f..cbf94825a32 100644
--- a/chromium/ui/ozone/public/input_controller.h
+++ b/chromium/ui/ozone/public/input_controller.h
@@ -93,6 +93,15 @@ class COMPONENT_EXPORT(OZONE_BASE) InputController {
virtual void SetTouchscreensEnabled(bool enabled) = 0;
+ // Controls vibration for the gamepad device with the corresponding |id|.
+ // |amplitude| determines the strength of the vibration, where 0 is no
+ // vibration and 255 is maximum vibration, and |duration_millis|
+ // determines the duration of the vibration in milliseconds.
+ virtual void PlayVibrationEffect(int id,
+ uint8_t amplitude,
+ uint16_t duration_millis) = 0;
+ virtual void StopVibration(int id) = 0;
+
// If |enable_filter| is true, all keys on the internal keyboard except
// |allowed_keys| are disabled.
virtual void SetInternalKeyboardFilter(bool enable_filter,
diff --git a/chromium/ui/ozone/public/mojom/drm_device.mojom b/chromium/ui/ozone/public/mojom/drm_device.mojom
index 9f67565431c..6504ea5dddd 100644
--- a/chromium/ui/ozone/public/mojom/drm_device.mojom
+++ b/chromium/ui/ozone/public/mojom/drm_device.mojom
@@ -51,10 +51,11 @@ interface DrmDevice {
// Instructs the GPU to abandon a DRM device.
RemoveGraphicsDevice(mojo_base.mojom.FilePath path);
- // Configures (Enables/Disables) a DRM display, returning true on success.
- ConfigureNativeDisplay(
- display.mojom.DisplayConfigurationParams display_config_params) =>
- (int64 display_id, bool success);
+ // Configures (Enables/Disables) DRM displays, returns a map: each configured
+ // display ID to its status, true on success.
+ ConfigureNativeDisplays(
+ array<display.mojom.DisplayConfigurationParams> config_requests) =>
+ (map<int64, bool> statuses);
// Gets or sets high-definition content protection (HDCP) (DRM as in
// digital rights management) state.
diff --git a/chromium/ui/ozone/public/mojom/wayland/BUILD.gn b/chromium/ui/ozone/public/mojom/wayland/BUILD.gn
index fb7f809ebd4..1371a3eafe6 100644
--- a/chromium/ui/ozone/public/mojom/wayland/BUILD.gn
+++ b/chromium/ui/ozone/public/mojom/wayland/BUILD.gn
@@ -5,7 +5,10 @@
import("//mojo/public/tools/bindings/mojom.gni")
mojom("wayland_mojom") {
- sources = [ "wayland_buffer_manager.mojom" ]
+ sources = [
+ "wayland_buffer_manager.mojom",
+ "wayland_overlay_config.mojom",
+ ]
public_deps = [
"//mojo/public/mojom/base",
diff --git a/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom b/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
index 46d6a84e024..37fcaa56ace 100644
--- a/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
+++ b/chromium/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
@@ -7,9 +7,10 @@ module ui.ozone.mojom;
import "mojo/public/mojom/base/file_path.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/mojom/accelerated_widget.mojom";
+import "ui/gfx/mojom/buffer_types.mojom";
import "ui/gfx/mojom/presentation_feedback.mojom";
import "ui/gfx/mojom/swap_result.mojom";
-import "ui/gfx/mojom/buffer_types.mojom";
+import "ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom";
// Used by the GPU for communication with the WaylandBufferManagerHost in
// the browser process.
@@ -75,6 +76,11 @@ interface WaylandBufferManagerHost {
// the ones on the gpu process.
CommitBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id,
gfx.mojom.Rect damage_region);
+
+ // Send overlay configurations for a frame to a WaylandWindow with the
+ // following |widget|.
+ CommitOverlays(gfx.mojom.AcceleratedWidget widget,
+ array<WaylandOverlayConfig> overlays);
};
@@ -92,7 +98,7 @@ interface WaylandBufferManagerGpu {
Initialize(pending_remote<WaylandBufferManagerHost> remote_host,
map<gfx.mojom.BufferFormat,
array<uint64>> buffer_formats_with_modifiers,
- bool supports_dma_buf);
+ bool supports_dma_buf);
// Signals about swap completion.
OnSubmission(gfx.mojom.AcceleratedWidget widget,
diff --git a/chromium/ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom b/chromium/ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom
new file mode 100644
index 00000000000..ab0f0cff02f
--- /dev/null
+++ b/chromium/ui/ozone/public/mojom/wayland/wayland_overlay_config.mojom
@@ -0,0 +1,46 @@
+// 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.
+
+module ui.ozone.mojom;
+
+import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/mojom/gpu_fence_handle.mojom";
+import "ui/gfx/mojom/overlay_transform.mojom";
+
+// This is not a mirror of ui::OverlayPlane, it only contains things that are
+// useful to ozone/wayland.
+struct WaylandOverlayConfig {
+ // Specifies the stacking order of this overlay plane, relative to primary
+ // plane.
+ int32 z_order;
+
+ // Specifies how the buffer is to be transformed during composition.
+ gfx.mojom.OverlayTransform transform;
+
+ // A unique id for the buffer, which is used to identify imported wl_buffers
+ // on the browser process.
+ uint32 buffer_id;
+
+ // Specifies where it is supposed to be on the display in physical pixels.
+ // This, after scaled by buffer_scale sets the destination rectangle of
+ // Wayland Viewport.
+ gfx.mojom.Rect bounds_rect;
+
+ // Specifies the region within the buffer to be placed inside |bounds_rect|.
+ // This sets the source rectangle of Wayland Viewport.
+ gfx.mojom.RectF crop_rect;
+
+ // Describes the changed region of the buffer. Optional to hint a partial
+ // swap.
+ gfx.mojom.Rect damage_region;
+
+ // Specifies if alpha blending, with premultiplied alpha should be applied at
+ // scanout.
+ bool enable_blend;
+
+ // Specifies a GpuFenceHandle to be waited on before content of the buffer can
+ // be accessed by the display controller for overlay, or by the gpu for
+ // compositing.
+ gfx.mojom.GpuFenceHandle? access_fence_handle;
+};
diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc
index f08c81e9fc3..c7027b208f0 100644
--- a/chromium/ui/ozone/public/ozone_platform.cc
+++ b/chromium/ui/ozone/public/ozone_platform.cc
@@ -95,6 +95,11 @@ PlatformGLEGLUtility* OzonePlatform::GetPlatformGLEGLUtility() {
return nullptr;
}
+int OzonePlatform::GetKeyModifiers() const {
+ // Platform may override this to provide the current state of modifier keys.
+ return 0;
+}
+
bool OzonePlatform::IsNativePixmapConfigSupported(
gfx::BufferFormat format,
gfx::BufferUsage usage) const {
diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h
index f1bbc12cf0c..797c79d057e 100644
--- a/chromium/ui/ozone/public/ozone_platform.h
+++ b/chromium/ui/ozone/public/ozone_platform.h
@@ -87,12 +87,24 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform {
base::MessagePumpType message_pump_type_for_gpu =
base::MessagePumpType::DEFAULT;
+ // Determines the type of message pump that should be used for viz
+ // compositor thread.
+ base::MessagePumpType message_pump_type_for_viz_compositor =
+ base::MessagePumpType::DEFAULT;
+
// Determines if the platform supports vulkan swap chain.
bool supports_vulkan_swap_chain = false;
// Wayland only: determines if the client must ignore the screen bounds when
// calculating bounds of menu windows.
bool ignore_screen_bounds_for_menus = false;
+
+ // If true, the platform shows and updates the drag image.
+ bool platform_shows_drag_image = true;
+
+ // Linux only, but see a TODO in BrowserDesktopWindowTreeHostLinux.
+ // Determines whether the platform supports the global application menu.
+ bool supports_global_application_menus = false;
};
// Properties available in the host process after initialization.
@@ -168,6 +180,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform {
gfx::AcceleratedWidget widget) = 0;
virtual PlatformGLEGLUtility* GetPlatformGLEGLUtility();
+ // Returns a bitmask of EventFlags showing the state of Alt, Shift and Ctrl
+ // keys that came with the most recent UI event.
+ virtual int GetKeyModifiers() const;
+
// Returns true if the specified buffer format is supported.
virtual bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
gfx::BufferUsage usage) const;
@@ -223,7 +239,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform {
bool initialized_gpu_ = false;
bool prearly_initialized_ = false;
- bool single_process_ = false;
+ // This value is checked on multiple threads. Declaring it volatile makes
+ // modifications to |single_process_| visible by other threads. Mutex is not
+ // needed since it's set before other threads are started.
+ volatile bool single_process_ = false;
DISALLOW_COPY_AND_ASSIGN(OzonePlatform);
};
diff --git a/chromium/ui/ozone/public/ozone_switches.cc b/chromium/ui/ozone/public/ozone_switches.cc
index 46c48204470..b9754475467 100644
--- a/chromium/ui/ozone/public/ozone_switches.cc
+++ b/chromium/ui/ozone/public/ozone_switches.cc
@@ -18,4 +18,8 @@ const char kEnableWaylandIme[] = "enable-wayland-ime";
// Disable explicit DMA-fences
const char kDisableExplicitDmaFences[] = "disable-explicit-dma-fences";
+// Disable running as system compositor.
+const char kDisableRunningAsSystemCompositor[] =
+ "disable-running-as-system-compositor";
+
} // namespace switches
diff --git a/chromium/ui/ozone/public/ozone_switches.h b/chromium/ui/ozone/public/ozone_switches.h
index 32f929d7fe3..12dd34e2b51 100644
--- a/chromium/ui/ozone/public/ozone_switches.h
+++ b/chromium/ui/ozone/public/ozone_switches.h
@@ -18,6 +18,9 @@ COMPONENT_EXPORT(OZONE_BASE) extern const char kEnableWaylandIme[];
COMPONENT_EXPORT(OZONE_BASE) extern const char kDisableExplicitDmaFences[];
+COMPONENT_EXPORT(OZONE_BASE)
+extern const char kDisableRunningAsSystemCompositor[];
+
} // namespace switches
#endif // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_
diff --git a/chromium/ui/ozone/public/platform_clipboard.h b/chromium/ui/ozone/public/platform_clipboard.h
index 5ef3fb3d939..941e2377307 100644
--- a/chromium/ui/ozone/public/platform_clipboard.h
+++ b/chromium/ui/ozone/public/platform_clipboard.h
@@ -12,6 +12,7 @@
#include "base/callback_forward.h"
#include "base/component_export.h"
#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/optional.h"
#include "ui/base/clipboard/clipboard_buffer.h"
@@ -30,7 +31,7 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformClipboard {
// DataMap is a map from "mime type" to associated data, whereas
// the data can be organized differently for each mime type.
- using Data = std::vector<uint8_t>;
+ using Data = scoped_refptr<base::RefCountedBytes>;
using DataMap = std::unordered_map<std::string, Data>;
// SequenceNumberUpdateCb is a repeating callback, which can be used to tell
@@ -63,7 +64,7 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformClipboard {
// RequestDataClosure is invoked to acknowledge that the requested clipboard
// data has been read and stored into 'data_map'.
using RequestDataClosure =
- base::OnceCallback<void(const base::Optional<std::vector<uint8_t>>&)>;
+ base::OnceCallback<void(const base::Optional<Data>&)>;
virtual void RequestClipboardData(ClipboardBuffer buffer,
const std::string& mime_type,
DataMap* data_map,
@@ -89,6 +90,9 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformClipboard {
// See comment above SequenceNumberUpdateCb. Can be called once.
virtual void SetSequenceNumberUpdateCb(SequenceNumberUpdateCb cb) = 0;
+
+ // Returns whether the kSelection buffer is available.
+ virtual bool IsSelectionBufferAvailable() const = 0;
};
} // namespace ui
diff --git a/chromium/ui/ozone/public/platform_gl_egl_utility.h b/chromium/ui/ozone/public/platform_gl_egl_utility.h
index 704dc178404..1923a3808af 100644
--- a/chromium/ui/ozone/public/platform_gl_egl_utility.h
+++ b/chromium/ui/ozone/public/platform_gl_egl_utility.h
@@ -27,6 +27,10 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformGLEGLUtility {
// Chooses alpha and buffer size values.
virtual void ChooseEGLAlphaAndBufferSize(EGLint* alpha_size,
EGLint* buffer_size) = 0;
+
+ // Returns whether the platform supports setting transparent background for
+ // windows.
+ virtual bool IsTransparentBackgroundSupported() const = 0;
};
} // namespace ui
diff --git a/chromium/ui/ozone/public/platform_screen.h b/chromium/ui/ozone/public/platform_screen.h
index 9ce293c0aff..ba20597d8c5 100644
--- a/chromium/ui/ozone/public/platform_screen.h
+++ b/chromium/ui/ozone/public/platform_screen.h
@@ -7,6 +7,7 @@
#include <set>
#include <string>
+#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
diff --git a/chromium/ui/ozone/public/surface_factory_ozone.cc b/chromium/ui/ozone/public/surface_factory_ozone.cc
index ec4751ef186..e297254340f 100644
--- a/chromium/ui/ozone/public/surface_factory_ozone.cc
+++ b/chromium/ui/ozone/public/surface_factory_ozone.cc
@@ -66,8 +66,7 @@ std::unique_ptr<OverlaySurface> SurfaceFactoryOzone::CreateOverlaySurface(
}
std::unique_ptr<SurfaceOzoneCanvas> SurfaceFactoryOzone::CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ gfx::AcceleratedWidget widget) {
return nullptr;
}
diff --git a/chromium/ui/ozone/public/surface_factory_ozone.h b/chromium/ui/ozone/public/surface_factory_ozone.h
index fde22b2d998..773537d40e4 100644
--- a/chromium/ui/ozone/public/surface_factory_ozone.h
+++ b/chromium/ui/ozone/public/surface_factory_ozone.h
@@ -108,14 +108,12 @@ class COMPONENT_EXPORT(OZONE_BASE) SurfaceFactoryOzone {
virtual std::unique_ptr<OverlaySurface> CreateOverlaySurface(
gfx::AcceleratedWidget window);
- // Create SurfaceOzoneCanvas for the specified gfx::AcceleratedWidget. The
- // |task_runner| may be null if the gpu service runs in a host process.
+ // Create SurfaceOzoneCanvas for the specified gfx::AcceleratedWidget.
//
// Note: The platform must support creation of SurfaceOzoneCanvas from the
// Browser Process using only the handle contained in gfx::AcceleratedWidget.
virtual std::unique_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
- gfx::AcceleratedWidget widget,
- scoped_refptr<base::SequencedTaskRunner> task_runner);
+ gfx::AcceleratedWidget widget);
// Create a single native buffer to be used for overlay planes or zero copy
// for |widget| representing a particular display controller or default
diff --git a/chromium/ui/ozone/test/mock_platform_window_delegate.h b/chromium/ui/ozone/test/mock_platform_window_delegate.h
index a44afac163c..f63d932acfc 100644
--- a/chromium/ui/ozone/test/mock_platform_window_delegate.h
+++ b/chromium/ui/ozone/test/mock_platform_window_delegate.h
@@ -26,6 +26,7 @@ class MockPlatformWindowDelegate : public PlatformWindowDelegate {
MOCK_METHOD0(OnLostCapture, void());
MOCK_METHOD1(OnAcceleratedWidgetAvailable,
void(gfx::AcceleratedWidget widget));
+ MOCK_METHOD0(OnWillDestroyAcceleratedWidget, void());
MOCK_METHOD0(OnAcceleratedWidgetDestroyed, void());
MOCK_METHOD1(OnActivationChanged, void(bool active));
MOCK_METHOD0(GetMinimumSizeForWindow, base::Optional<gfx::Size>());
diff --git a/chromium/ui/platform_window/BUILD.gn b/chromium/ui/platform_window/BUILD.gn
index f06af0fb93c..a2d20c96d66 100644
--- a/chromium/ui/platform_window/BUILD.gn
+++ b/chromium/ui/platform_window/BUILD.gn
@@ -6,6 +6,9 @@ import("//build/config/ui.gni")
component("platform_window") {
sources = [
+ "extensions/workspace_extension.cc",
+ "extensions/workspace_extension.h",
+ "extensions/workspace_extension_delegate.h",
"platform_window.cc",
"platform_window.h",
"platform_window_delegate.cc",
@@ -37,6 +40,16 @@ component("platform_window") {
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
]
}
+
+ if (is_linux || is_chromeos) {
+ sources += [
+ "extensions/wayland_extension.cc",
+ "extensions/wayland_extension.h",
+ "extensions/x11_extension.cc",
+ "extensions/x11_extension.h",
+ "extensions/x11_extension_delegate.h",
+ ]
+ }
}
group("platform_impls") {
diff --git a/chromium/ui/platform_window/common/BUILD.gn b/chromium/ui/platform_window/common/BUILD.gn
index 54f3c8b83ea..8239901fb53 100644
--- a/chromium/ui/platform_window/common/BUILD.gn
+++ b/chromium/ui/platform_window/common/BUILD.gn
@@ -4,7 +4,7 @@
import("//build/config/ui.gni")
-assert(is_linux)
+assert(is_linux || is_chromeos)
component("common") {
output_name = "platform_window_common"
diff --git a/chromium/ui/platform_window/extensions/BUILD.gn b/chromium/ui/platform_window/extensions/BUILD.gn
deleted file mode 100644
index 5da275db565..00000000000
--- a/chromium/ui/platform_window/extensions/BUILD.gn
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/ui.gni")
-
-source_set("extensions") {
- sources = [
- "workspace_extension.cc",
- "workspace_extension.h",
- "workspace_extension_delegate.h",
- ]
-
- defines = [ "IS_EXTENSIONS_IMPL" ]
-
- deps = [
- "//base",
- "//ui/base",
- "//ui/platform_window",
- ]
-
- if (is_linux) {
- sources += [
- "x11_extension.cc",
- "x11_extension.h",
- "x11_extension_delegate.h",
- ]
- }
-}
diff --git a/chromium/ui/platform_window/extensions/wayland_extension.cc b/chromium/ui/platform_window/extensions/wayland_extension.cc
new file mode 100644
index 00000000000..33d51d70cf6
--- /dev/null
+++ b/chromium/ui/platform_window/extensions/wayland_extension.cc
@@ -0,0 +1,27 @@
+// 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 "ui/platform_window/extensions/wayland_extension.h"
+
+#include "ui/base/class_property.h"
+#include "ui/platform_window/platform_window.h"
+
+DEFINE_UI_CLASS_PROPERTY_TYPE(ui::WaylandExtension*)
+
+namespace ui {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(WaylandExtension*, kWaylandExtensionKey, nullptr)
+
+WaylandExtension::~WaylandExtension() = default;
+
+void WaylandExtension::SetWaylandExtension(PlatformWindow* window,
+ WaylandExtension* extension) {
+ window->SetProperty(kWaylandExtensionKey, extension);
+}
+
+WaylandExtension* GetWaylandExtension(const PlatformWindow& window) {
+ return window.GetProperty(kWaylandExtensionKey);
+}
+
+} // namespace ui
diff --git a/chromium/ui/platform_window/extensions/wayland_extension.h b/chromium/ui/platform_window/extensions/wayland_extension.h
new file mode 100644
index 00000000000..36e34420ddb
--- /dev/null
+++ b/chromium/ui/platform_window/extensions/wayland_extension.h
@@ -0,0 +1,33 @@
+// 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 UI_PLATFORM_WINDOW_EXTENSIONS_WAYLAND_EXTENSION_H_
+#define UI_PLATFORM_WINDOW_EXTENSIONS_WAYLAND_EXTENSION_H_
+
+#include "base/component_export.h"
+
+namespace ui {
+
+class PlatformWindow;
+
+class COMPONENT_EXPORT(PLATFORM_WINDOW) WaylandExtension {
+ public:
+ // Starts a window dragging session for the owning platform window, if
+ // it is not running yet. Under Wayland, window dragging is backed by a
+ // platform drag-and-drop session.
+ virtual void StartWindowDraggingSessionIfNeeded() = 0;
+
+ protected:
+ virtual ~WaylandExtension();
+
+ // Sets the pointer to the extension as a property of the PlatformWindow.
+ void SetWaylandExtension(PlatformWindow* window, WaylandExtension* extension);
+};
+
+COMPONENT_EXPORT(PLATFORM_WINDOW)
+WaylandExtension* GetWaylandExtension(const PlatformWindow& window);
+
+} // namespace ui
+
+#endif // UI_PLATFORM_WINDOW_EXTENSIONS_WAYLAND_EXTENSION_H_
diff --git a/chromium/ui/platform_window/extensions/workspace_extension.h b/chromium/ui/platform_window/extensions/workspace_extension.h
index e36b1464661..83c806a18a1 100644
--- a/chromium/ui/platform_window/extensions/workspace_extension.h
+++ b/chromium/ui/platform_window/extensions/workspace_extension.h
@@ -19,7 +19,7 @@ class WorkspaceExtensionDelegate;
// owned by a PlatformWindow owner. To avoid casts from the PlatformWindow to
// the WorkspaceExtension, a pointer to this interface must be set through
// "SetWorkspaceExtension".
-class COMPONENT_EXPORT(EXTENSIONS) WorkspaceExtension {
+class COMPONENT_EXPORT(PLATFORM_WINDOW) WorkspaceExtension {
public:
// Returns the workspace the PlatformWindow is located in.
virtual std::string GetWorkspace() const = 0;
@@ -44,7 +44,7 @@ class COMPONENT_EXPORT(EXTENSIONS) WorkspaceExtension {
WorkspaceExtension* workspace_extension);
};
-COMPONENT_EXPORT(EXTENSIONS)
+COMPONENT_EXPORT(PLATFORM_WINDOW)
WorkspaceExtension* GetWorkspaceExtension(
const PlatformWindow& platform_window);
diff --git a/chromium/ui/platform_window/extensions/workspace_extension_delegate.h b/chromium/ui/platform_window/extensions/workspace_extension_delegate.h
index 8b1688ecbd1..2c2107383d2 100644
--- a/chromium/ui/platform_window/extensions/workspace_extension_delegate.h
+++ b/chromium/ui/platform_window/extensions/workspace_extension_delegate.h
@@ -11,7 +11,7 @@ namespace ui {
// Notifies the delegate about changed workspace. The delegate must be set in
// WorkspaceExtension to be able to receive these changes.
-class COMPONENT_EXPORT(EXTENSIONS) WorkspaceExtensionDelegate {
+class COMPONENT_EXPORT(PLATFORM_WINDOW) WorkspaceExtensionDelegate {
public:
// Notifies the delegate if the window has changed the workspace it is
// located in.
diff --git a/chromium/ui/platform_window/extensions/x11_extension.h b/chromium/ui/platform_window/extensions/x11_extension.h
index d1525ba4987..b7bb72d897a 100644
--- a/chromium/ui/platform_window/extensions/x11_extension.h
+++ b/chromium/ui/platform_window/extensions/x11_extension.h
@@ -17,7 +17,7 @@ class X11ExtensionDelegate;
// APIs. Please refer to README for more details.
//
// The extension mustn't be called until PlatformWindow is initialized.
-class COMPONENT_EXPORT(EXTENSIONS) X11Extension {
+class COMPONENT_EXPORT(PLATFORM_WINDOW) X11Extension {
public:
// Returns whether an XSync extension is available at the current platform.
virtual bool IsSyncExtensionAvailable() const = 0;
@@ -55,7 +55,7 @@ class COMPONENT_EXPORT(EXTENSIONS) X11Extension {
X11Extension* x11_extensions);
};
-COMPONENT_EXPORT(EXTENSIONS)
+COMPONENT_EXPORT(PLATFORM_WINDOW)
X11Extension* GetX11Extension(const PlatformWindow& platform_window);
} // namespace ui
diff --git a/chromium/ui/platform_window/extensions/x11_extension_delegate.h b/chromium/ui/platform_window/extensions/x11_extension_delegate.h
index e5ad90bf7b2..86ec59d2f14 100644
--- a/chromium/ui/platform_window/extensions/x11_extension_delegate.h
+++ b/chromium/ui/platform_window/extensions/x11_extension_delegate.h
@@ -20,7 +20,7 @@ class Size;
namespace ui {
-class COMPONENT_EXPORT(EXTENSIONS) X11ExtensionDelegate {
+class COMPONENT_EXPORT(PLATFORM_WINDOW) X11ExtensionDelegate {
public:
// Notifies if the PlatformWindow looses a mouse grab. This can be useful
// for Wayland or X11. Both of them provide pointer enter and leave
@@ -35,7 +35,12 @@ class COMPONENT_EXPORT(EXTENSIONS) X11ExtensionDelegate {
virtual void GetWindowMask(const gfx::Size& size, SkPath* window_mask) = 0;
#if BUILDFLAG(USE_ATK)
- virtual bool OnAtkKeyEvent(AtkKeyEventStruct* atk_key_event) = 0;
+ // Notifies an ATK key event to be processed. The transient parameter will be
+ // true if the event target is a transient window (e.g. a modal dialog)
+ // "hanging" from our window. Return true to stop propagation of the original
+ // key event.
+ virtual bool OnAtkKeyEvent(AtkKeyEventStruct* atk_key_event,
+ bool transient) = 0;
#endif
// Returns true if this window should be in a forced override-redirect state
diff --git a/chromium/ui/platform_window/platform_window.cc b/chromium/ui/platform_window/platform_window.cc
index 42e99978ed8..19b5fc55ade 100644
--- a/chromium/ui/platform_window/platform_window.cc
+++ b/chromium/ui/platform_window/platform_window.cc
@@ -4,6 +4,8 @@
#include "ui/platform_window/platform_window.h"
+#include <string>
+
#include "ui/gfx/geometry/rect.h"
namespace ui {
@@ -48,4 +50,8 @@ void PlatformWindow::SetOpacity(float opacity) {}
void PlatformWindow::SetVisibilityChangedAnimationsEnabled(bool enabled) {}
+std::string PlatformWindow::GetWindowUniqueId() const {
+ return std::string();
+}
+
} // namespace ui
diff --git a/chromium/ui/platform_window/platform_window.h b/chromium/ui/platform_window/platform_window.h
index 96b7690c88d..eeeda584667 100644
--- a/chromium/ui/platform_window/platform_window.h
+++ b/chromium/ui/platform_window/platform_window.h
@@ -6,6 +6,7 @@
#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_H_
#include <memory>
+#include <string>
#include "base/component_export.h"
#include "base/strings/string16.h"
@@ -140,6 +141,10 @@ class COMPONENT_EXPORT(PLATFORM_WINDOW) PlatformWindow
// Enables or disables platform provided animations of the PlatformWindow.
// If |enabled| is set to false, animations are disabled.
virtual void SetVisibilityChangedAnimationsEnabled(bool enabled);
+
+ // Returns a unique ID for the window. The interpretation of the ID is
+ // platform specific. Overriding this method is optional.
+ virtual std::string GetWindowUniqueId() const;
};
} // namespace ui
diff --git a/chromium/ui/platform_window/platform_window_delegate.h b/chromium/ui/platform_window/platform_window_delegate.h
index 88b791cf6b3..6ffdaa15b90 100644
--- a/chromium/ui/platform_window/platform_window_delegate.h
+++ b/chromium/ui/platform_window/platform_window_delegate.h
@@ -49,6 +49,9 @@ class COMPONENT_EXPORT(PLATFORM_WINDOW) PlatformWindowDelegate {
virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) = 0;
+ // Notifies the delegate that the widget is about to be destroyed.
+ virtual void OnWillDestroyAcceleratedWidget() = 0;
+
// Notifies the delegate that the widget cannot be used anymore until
// a new widget is made available through OnAcceleratedWidgetAvailable().
// Must not be called when the PlatformWindow is being destroyed.
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_drag_handler.h b/chromium/ui/platform_window/platform_window_handler/wm_drag_handler.h
deleted file mode 100644
index d1f7510e0c2..00000000000
--- a/chromium/ui/platform_window/platform_window_handler/wm_drag_handler.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
-#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
-
-#include "base/bind.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
-
-namespace ui {
-class PlatformWindow;
-class OSExchangeData;
-
-class WM_PLATFORM_EXPORT WmDragHandler {
- public:
- // During the drag operation, the handler may send updates
- class Delegate {
- public:
- // Called every time when the drag location has changed.
- virtual void OnDragLocationChanged(const gfx::Point& screen_point_px) = 0;
- // Called when the currently negotiated operation has changed.
- virtual void OnDragOperationChanged(
- DragDropTypes::DragOperation operation) = 0;
- // Called once when the operation has finished.
- virtual void OnDragFinished(int operation) = 0;
-
- protected:
- virtual ~Delegate();
- };
-
- // Starts dragging with |data| which it wants to deliver to the destination.
- // |operation| is the suggested operation which is bitmask of DRAG_NONE,
- // DRAG_MOVE, DRAG_COPY and DRAG_LINK in DragDropTypes::DragOperation to the
- // destination and the destination sets the final operation when the drop
- // action is performed. In progress updates on the drag operation come back
- // through the |delegate|.
- virtual void StartDrag(const OSExchangeData& data,
- int operation,
- gfx::NativeCursor cursor,
- Delegate* delegate) = 0;
-
- protected:
- virtual ~WmDragHandler() {}
-};
-
-WM_PLATFORM_EXPORT void SetWmDragHandler(PlatformWindow* platform_window,
- WmDragHandler* drag_handler);
-WM_PLATFORM_EXPORT WmDragHandler* GetWmDragHandler(
- const PlatformWindow& platform_window);
-
-} // namespace ui
-
-#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DRAG_HANDLER_H_
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_drop_handler.h b/chromium/ui/platform_window/platform_window_handler/wm_drop_handler.h
deleted file mode 100644
index 26745afbb26..00000000000
--- a/chromium/ui/platform_window/platform_window_handler/wm_drop_handler.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
-#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
-
-#include <memory>
-
-#include "ui/gfx/native_widget_types.h"
-#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
-
-namespace gfx {
-class PointF;
-}
-
-namespace ui {
-class PlatformWindow;
-class OSExchangeData;
-
-class WM_PLATFORM_EXPORT WmDropHandler {
- public:
- // Notifies that dragging is entered to the window. |point| is in the
- // coordinate space of the PlatformWindow.
- virtual void OnDragEnter(const gfx::PointF& point,
- std::unique_ptr<OSExchangeData> data,
- int operation) = 0;
-
- // Notifies that dragging is moved. |widget_out| will be set with the
- // widget located at |point|. |point| is in the coordinate space of the
- // PlatformWindow. It returns the operation selected by client and the
- // returned value should be from ui::DragDropTypes.
- virtual int OnDragMotion(const gfx::PointF& point, int operation) = 0;
-
- // Notifies that dragged data is dropped. When it doesn't deliver
- // the dragged data on OnDragEnter, it should put it to |data|. The location
- // of the drop is the location of the latest DragEnter/DragMotion. If
- // OSExchangeData is provided on OnDragEnter, the |data| should be same as it.
- virtual void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) = 0;
-
- // Notifies that dragging is left.
- virtual void OnDragLeave() = 0;
-
- protected:
- virtual ~WmDropHandler() {}
-};
-
-WM_PLATFORM_EXPORT void SetWmDropHandler(PlatformWindow* platform_window,
- WmDropHandler* drop_handler);
-WM_PLATFORM_EXPORT WmDropHandler* GetWmDropHandler(
- const PlatformWindow& platform_window);
-
-} // namespace ui
-
-#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_DROP_HANDLER_H_
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h b/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h
deleted file mode 100644
index 37165fb71ef..00000000000
--- a/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
-#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
-
-// Defines WM_PLATFORM_EXPORT so that functionality implemented by the
-// wm_platform module can be exported to consumers.
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION)
-#define WM_PLATFORM_EXPORT __declspec(dllexport)
-#else
-#define WM_PLATFORM_EXPORT __declspec(dllimport)
-#endif // defined(WM_PLATFORM_IMPLEMENTATION)
-
-#else // defined(WIN32)
-#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION)
-#define WM_PLATFORM_EXPORT __attribute__((visibility("default")))
-#else
-#define WM_PLATFORM_EXPORT
-#endif
-#endif
-
-#else // defined(COMPONENT_BUILD)
-#define WM_PLATFORM_EXPORT
-#endif
-
-#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_
diff --git a/chromium/ui/platform_window/platform_window_init_properties.h b/chromium/ui/platform_window/platform_window_init_properties.h
index d1a5cc0a890..be16c9672a8 100644
--- a/chromium/ui/platform_window/platform_window_init_properties.h
+++ b/chromium/ui/platform_window/platform_window_init_properties.h
@@ -41,7 +41,7 @@ enum class PlatformWindowOpacity {
class WorkspaceExtensionDelegate;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
class X11ExtensionDelegate;
#endif
@@ -82,7 +82,7 @@ struct COMPONENT_EXPORT(PLATFORM_WINDOW) PlatformWindowInitProperties {
WorkspaceExtensionDelegate* workspace_extension_delegate = nullptr;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
bool prefer_dark_theme = false;
gfx::ImageSkia* icon = nullptr;
base::Optional<int> background_color;
diff --git a/chromium/ui/platform_window/stub/BUILD.gn b/chromium/ui/platform_window/stub/BUILD.gn
index 55cff46168e..fd53cfe9258 100644
--- a/chromium/ui/platform_window/stub/BUILD.gn
+++ b/chromium/ui/platform_window/stub/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("stub") {
+component("stub") {
output_name = "stub_window"
deps = [
diff --git a/chromium/ui/platform_window/win/BUILD.gn b/chromium/ui/platform_window/win/BUILD.gn
index d8bbfd5a13c..519c51c2821 100644
--- a/chromium/ui/platform_window/win/BUILD.gn
+++ b/chromium/ui/platform_window/win/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("win") {
+component("win") {
output_name = "win_window"
deps = [
diff --git a/chromium/ui/platform_window/platform_window_handler/BUILD.gn b/chromium/ui/platform_window/wm/BUILD.gn
index bcb0f855ab3..96937273558 100644
--- a/chromium/ui/platform_window/platform_window_handler/BUILD.gn
+++ b/chromium/ui/platform_window/wm/BUILD.gn
@@ -2,10 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("platform_window_handler") {
- output_name = "platform_window_handler_libs"
+component("wm") {
+ output_name = "platform_window_wm"
sources = [
"wm_drag_handler.cc",
@@ -16,10 +14,9 @@ jumbo_component("platform_window_handler") {
"wm_move_loop_handler.h",
"wm_move_resize_handler.cc",
"wm_move_resize_handler.h",
- "wm_platform_export.h",
]
- defines = [ "PLATFORM_WINDOW_HANDLER_IMPLEMENTATION" ]
+ defines = [ "IS_WM_IMPL" ]
deps = [
"//ui/base",
diff --git a/chromium/ui/platform_window/platform_window_handler/DEPS b/chromium/ui/platform_window/wm/DEPS
index 381a2e478f2..381a2e478f2 100644
--- a/chromium/ui/platform_window/platform_window_handler/DEPS
+++ b/chromium/ui/platform_window/wm/DEPS
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_drag_handler.cc b/chromium/ui/platform_window/wm/wm_drag_handler.cc
index 5dafa35363a..ea8b1dfc198 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_drag_handler.cc
+++ b/chromium/ui/platform_window/wm/wm_drag_handler.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
#include "ui/base/class_property.h"
#include "ui/platform_window/platform_window.h"
diff --git a/chromium/ui/platform_window/wm/wm_drag_handler.h b/chromium/ui/platform_window/wm/wm_drag_handler.h
new file mode 100644
index 00000000000..769554c4005
--- /dev/null
+++ b/chromium/ui/platform_window/wm/wm_drag_handler.h
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_PLATFORM_WINDOW_WM_WM_DRAG_HANDLER_H_
+#define UI_PLATFORM_WINDOW_WM_WM_DRAG_HANDLER_H_
+
+#include "base/component_export.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class PlatformWindow;
+class OSExchangeData;
+
+class COMPONENT_EXPORT(WM) WmDragHandler {
+ public:
+ // During the drag operation, the handler may send updates
+ class Delegate {
+ public:
+ // Called every time when the drag location has changed.
+ virtual void OnDragLocationChanged(const gfx::Point& screen_point_px) = 0;
+ // Called when the currently negotiated operation has changed.
+ virtual void OnDragOperationChanged(
+ DragDropTypes::DragOperation operation) = 0;
+ // Called once when the operation has finished.
+ virtual void OnDragFinished(int operation) = 0;
+
+ protected:
+ virtual ~Delegate();
+ };
+
+ // Starts dragging |data|.
+ // |operation| is bitmask of DRAG_NONE, DRAG_MOVE, DRAG_COPY and DRAG_LINK
+ // in DragDropTypes::DragOperation that defines operations possible for the
+ // drag source. The destination sets the resulting operation when the drop
+ // action is performed.
+ // |can_grab_pointer| indicates whether the implementation can grab the mouse
+ // pointer (some platforms may need this).
+ // In progress updates on the drag operation come back through the |delegate|.
+ //
+ // This method runs a nested message loop, returning when the drag operation
+ // is done. Care must be taken when calling this as it's entirely possible
+ // that when this returns this object (and the calling object) have been
+ // destroyed.
+ //
+ // Returns whether the operation ended well (i.e., had not been canceled).
+ virtual bool StartDrag(const OSExchangeData& data,
+ int operation,
+ gfx::NativeCursor cursor,
+ bool can_grab_pointer,
+ Delegate* delegate) = 0;
+
+ // Cancels the drag.
+ virtual void CancelDrag() = 0;
+};
+
+COMPONENT_EXPORT(WM)
+void SetWmDragHandler(PlatformWindow* platform_window,
+ WmDragHandler* drag_handler);
+COMPONENT_EXPORT(WM)
+WmDragHandler* GetWmDragHandler(const PlatformWindow& platform_window);
+
+} // namespace ui
+
+#endif // UI_PLATFORM_WINDOW_WM_WM_DRAG_HANDLER_H_
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_drop_handler.cc b/chromium/ui/platform_window/wm/wm_drop_handler.cc
index 64ff6bd5289..87ea997370d 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_drop_handler.cc
+++ b/chromium/ui/platform_window/wm/wm_drop_handler.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
#include "ui/base/class_property.h"
#include "ui/platform_window/platform_window.h"
diff --git a/chromium/ui/platform_window/wm/wm_drop_handler.h b/chromium/ui/platform_window/wm/wm_drop_handler.h
new file mode 100644
index 00000000000..2096843093c
--- /dev/null
+++ b/chromium/ui/platform_window/wm/wm_drop_handler.h
@@ -0,0 +1,64 @@
+// 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 UI_PLATFORM_WINDOW_WM_WM_DROP_HANDLER_H_
+#define UI_PLATFORM_WINDOW_WM_WM_DROP_HANDLER_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+
+namespace gfx {
+class PointF;
+}
+
+namespace ui {
+class PlatformWindow;
+class OSExchangeData;
+
+class COMPONENT_EXPORT(WM) WmDropHandler {
+ public:
+ // Notifies that drag has entered the window.
+ // |point| is in the coordinate space of the PlatformWindow.
+ // |operation| contains bitmask of ui::DragDropTypes suggested by the source.
+ // |modifiers| contains bitmask of ui::EventFlags that accompany the event.
+ virtual void OnDragEnter(const gfx::PointF& point,
+ std::unique_ptr<OSExchangeData> data,
+ int operation,
+ int modifiers) = 0;
+
+ // Notifies that drag location has changed.
+ // |point| is in the coordinate space of the PlatformWindow.
+ // |operation| contains bitmask of ui::DragDropTypes suggested by the source.
+ // |modifiers| contains bitmask of ui::EventFlags that accompany the event.
+ // Returns one of ui::DragDropTypes values selected by the client.
+ virtual int OnDragMotion(const gfx::PointF& point,
+ int operation,
+ int modifiers) = 0;
+
+ // Notifies that dragged data is dropped. When it doesn't deliver
+ // the dragged data on OnDragEnter, it should put it to |data|. The location
+ // of the drop is the location of the latest DragEnter/DragMotion. If
+ // OSExchangeData is provided on OnDragEnter, the |data| should be same as it.
+ // |modifiers| contains bitmask of ui::EventFlags that accompany the event.
+ virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data,
+ int modifiers) = 0;
+
+ // Notifies that dragging is left. Must be called before
+ // WmDragHandler::OnDragFinished when the drag session gets cancelled.
+ virtual void OnDragLeave() = 0;
+
+ protected:
+ virtual ~WmDropHandler() = default;
+};
+
+COMPONENT_EXPORT(WM)
+void SetWmDropHandler(PlatformWindow* platform_window,
+ WmDropHandler* drop_handler);
+COMPONENT_EXPORT(WM)
+WmDropHandler* GetWmDropHandler(const PlatformWindow& platform_window);
+
+} // namespace ui
+
+#endif // UI_PLATFORM_WINDOW_WM_WM_DROP_HANDLER_H_
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.cc b/chromium/ui/platform_window/wm/wm_move_loop_handler.cc
index 94192b3ebff..d5a9170939b 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.cc
+++ b/chromium/ui/platform_window/wm/wm_move_loop_handler.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
+#include "ui/platform_window/wm/wm_move_loop_handler.h"
#include "ui/base/class_property.h"
#include "ui/platform_window/platform_window.h"
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.h b/chromium/ui/platform_window/wm/wm_move_loop_handler.h
index 98b1fc405c9..f7c311c6de9 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_move_loop_handler.h
+++ b/chromium/ui/platform_window/wm/wm_move_loop_handler.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_LOOP_HANDLER_H_
-#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_LOOP_HANDLER_H_
+#ifndef UI_PLATFORM_WINDOW_WM_WM_MOVE_LOOP_HANDLER_H_
+#define UI_PLATFORM_WINDOW_WM_WM_MOVE_LOOP_HANDLER_H_
-#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
+#include "base/component_export.h"
namespace gfx {
class Vector2d;
@@ -16,7 +16,7 @@ namespace ui {
class PlatformWindow;
// Handler that starts interactive move loop for the PlatformWindow.
-class WM_PLATFORM_EXPORT WmMoveLoopHandler {
+class COMPONENT_EXPORT(WM) WmMoveLoopHandler {
public:
// Starts a move loop for tab drag controller. Returns true on success or
// false on fail/cancel.
@@ -29,11 +29,12 @@ class WM_PLATFORM_EXPORT WmMoveLoopHandler {
virtual ~WmMoveLoopHandler() {}
};
-WM_PLATFORM_EXPORT void SetWmMoveLoopHandler(PlatformWindow* platform_window,
- WmMoveLoopHandler* drag_handler);
-WM_PLATFORM_EXPORT WmMoveLoopHandler* GetWmMoveLoopHandler(
- const PlatformWindow& platform_window);
+COMPONENT_EXPORT(WM)
+void SetWmMoveLoopHandler(PlatformWindow* platform_window,
+ WmMoveLoopHandler* drag_handler);
+COMPONENT_EXPORT(WM)
+WmMoveLoopHandler* GetWmMoveLoopHandler(const PlatformWindow& platform_window);
} // namespace ui
-#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_LOOP_HANDLER_H_
+#endif // UI_PLATFORM_WINDOW_WM_WM_MOVE_LOOP_HANDLER_H_
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc b/chromium/ui/platform_window/wm/wm_move_resize_handler.cc
index a79ad70a726..d3b286208f4 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc
+++ b/chromium/ui/platform_window/wm/wm_move_resize_handler.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
#include "ui/base/class_property.h"
#include "ui/platform_window/platform_window.h"
diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h b/chromium/ui/platform_window/wm/wm_move_resize_handler.h
index ac301603180..8f0bb008d3c 100644
--- a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
+++ b/chromium/ui/platform_window/wm/wm_move_resize_handler.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
-#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
+#ifndef UI_PLATFORM_WINDOW_WM_WM_MOVE_RESIZE_HANDLER_H_
+#define UI_PLATFORM_WINDOW_WM_WM_MOVE_RESIZE_HANDLER_H_
-#include "ui/platform_window/platform_window_handler/wm_platform_export.h"
+#include "base/component_export.h"
namespace gfx {
class Point;
@@ -15,7 +15,7 @@ namespace ui {
class PlatformWindow;
-class WmMoveResizeHandler {
+class COMPONENT_EXPORT(WM) WmMoveResizeHandler {
public:
// A system window manager starts interactive drag or resize of a window based
// on the |hittest| value. The |hittest| value identifies in which direction
@@ -52,12 +52,13 @@ class WmMoveResizeHandler {
virtual ~WmMoveResizeHandler() {}
};
-WM_PLATFORM_EXPORT void SetWmMoveResizeHandler(
- PlatformWindow* platform_window,
- WmMoveResizeHandler* move_resize_handler);
-WM_PLATFORM_EXPORT WmMoveResizeHandler* GetWmMoveResizeHandler(
+COMPONENT_EXPORT(WM)
+void SetWmMoveResizeHandler(PlatformWindow* platform_window,
+ WmMoveResizeHandler* move_resize_handler);
+COMPONENT_EXPORT(WM)
+WmMoveResizeHandler* GetWmMoveResizeHandler(
const PlatformWindow& platform_window);
} // namespace ui
-#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
+#endif // UI_PLATFORM_WINDOW_WM_WM_MOVE_RESIZE_HANDLER_H_
diff --git a/chromium/ui/platform_window/x11/BUILD.gn b/chromium/ui/platform_window/x11/BUILD.gn
index a9d6bc31204..f222eb7cc9a 100644
--- a/chromium/ui/platform_window/x11/BUILD.gn
+++ b/chromium/ui/platform_window/x11/BUILD.gn
@@ -2,14 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
import("//ui/ozone/ozone.gni")
assert(use_x11 || ozone_platform_x11)
-jumbo_component("x11") {
+component("x11") {
output_name = "x11_window"
deps = [
@@ -25,8 +24,7 @@ jumbo_component("x11") {
"//ui/gfx/x",
"//ui/platform_window",
"//ui/platform_window/common",
- "//ui/platform_window/extensions",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
]
defines = [ "X11_WINDOW_IMPLEMENTATION" ]
@@ -52,7 +50,12 @@ jumbo_component("x11") {
}
test("x11_unittests") {
- sources = [ "test/x11_window_unittest.cc" ]
+ sources = [
+ "test/device_data_manager_x11_unittest.cc",
+ "test/events_x_unittest.cc",
+ "test/x11_event_translation_unittest.cc",
+ "test/x11_window_unittest.cc",
+ ]
deps = [
":x11",
"//base/test:run_all_unittests",
@@ -60,6 +63,7 @@ test("x11_unittests") {
"//skia",
"//testing/gmock",
"//testing/gtest",
+ "//ui/base:features",
"//ui/base/x",
"//ui/base/x:test_support",
"//ui/events:test_support",
@@ -68,7 +72,6 @@ test("x11_unittests") {
"//ui/gfx:test_support",
"//ui/gfx/x",
"//ui/platform_window",
- "//ui/platform_window/extensions",
]
configs += [ "//build/config:precompiled_headers" ]
diff --git a/chromium/ui/platform_window/x11/atk_event_conversion.cc b/chromium/ui/platform_window/x11/atk_event_conversion.cc
index 6b5a82dbfe7..4138f9c5544 100644
--- a/chromium/ui/platform_window/x11/atk_event_conversion.cc
+++ b/chromium/ui/platform_window/x11/atk_event_conversion.cc
@@ -7,30 +7,31 @@
#include "base/check.h"
#include "base/notreached.h"
#include "ui/events/x/events_x_utils.h"
+#include "ui/gfx/x/xproto.h"
+#include "ui/gfx/x/xproto_types.h"
namespace ui {
std::unique_ptr<AtkKeyEventStruct> AtkKeyEventFromXEvent(
x11::Event* x11_event) {
DCHECK(x11_event);
- XEvent* xevent = &x11_event->xlib_event();
auto atk_key_event = std::make_unique<AtkKeyEventStruct>();
- if (xevent->type == x11::KeyEvent::Press)
- atk_key_event->type = ATK_KEY_EVENT_PRESS;
- else if (xevent->type == x11::KeyEvent::Release)
- atk_key_event->type = ATK_KEY_EVENT_RELEASE;
- else
- NOTREACHED() << xevent->type;
-
- XKeyEvent& xkey = xevent->xkey;
- KeySym keysym = NoSymbol;
- XLookupString(&xkey, nullptr, 0, &keysym, nullptr);
-
- atk_key_event->state = xkey.state;
- atk_key_event->keyval = keysym;
- atk_key_event->keycode = xkey.keycode;
- atk_key_event->timestamp = xkey.time;
+ auto* xkey = x11_event->As<x11::KeyEvent>();
+ DCHECK(xkey);
+
+ atk_key_event->type = xkey->opcode == x11::KeyEvent::Press
+ ? ATK_KEY_EVENT_PRESS
+ : ATK_KEY_EVENT_RELEASE;
+
+ auto state = static_cast<int>(xkey->state);
+ auto keycode = static_cast<int>(xkey->detail);
+ auto keysym = x11::Connection::Get()->KeycodeToKeysym(keycode, state);
+
+ atk_key_event->state = state;
+ atk_key_event->keyval = static_cast<uint32_t>(keysym);
+ atk_key_event->keycode = keycode;
+ atk_key_event->timestamp = static_cast<uint32_t>(xkey->time);
// This string property matches the one that was removed from GdkEventKey. In
// the future, ATK clients should no longer rely on it, so we set it to null.
diff --git a/chromium/ui/platform_window/x11/x11_window.cc b/chromium/ui/platform_window/x11/x11_window.cc
index bc2c18233b7..c0ba5c3a156 100644
--- a/chromium/ui/platform_window/x11/x11_window.cc
+++ b/chromium/ui/platform_window/x11/x11_window.cc
@@ -7,34 +7,30 @@
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "ui/base/buildflags.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/ui_base_features.h"
#include "ui/base/x/x11_cursor.h"
#include "ui/base/x/x11_desktop_window_move_client.h"
+#include "ui/base/x/x11_os_exchange_data_provider.h"
+#include "ui/base/x/x11_pointer_grab.h"
+#include "ui/base/x/x11_topmost_window_finder.h"
#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_util_internal.h"
#include "ui/display/screen.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
+#include "ui/events/ozone/events_ozone.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/events/x/x11_event_translation.h"
+#include "ui/events/x/x11_window_event_manager.h"
#include "ui/gfx/x/x11.h"
#include "ui/platform_window/common/platform_window_defaults.h"
#include "ui/platform_window/extensions/workspace_extension_delegate.h"
#include "ui/platform_window/extensions/x11_extension_delegate.h"
-#include "ui/platform_window/x11/x11_window_manager.h"
-
-#if defined(USE_OZONE)
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/x/x11_os_exchange_data_provider.h"
-#include "ui/base/x/x11_pointer_grab.h"
-#include "ui/base/x/x11_topmost_window_finder.h"
-#include "ui/events/ozone/events_ozone.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
#include "ui/platform_window/x11/x11_topmost_window_finder.h"
-#else
-#include "ui/base/dragdrop/os_exchange_data_provider_x11.h"
-#endif // defined(USE_OZONE)
+#include "ui/platform_window/x11/x11_window_manager.h"
#if BUILDFLAG(USE_ATK)
#include "ui/platform_window/x11/atk_event_conversion.h"
@@ -103,19 +99,24 @@ ui::XWindow::Configuration ConvertInitPropertiesToXWindowConfig(
}
// Coalesce touch/mouse events if needed
-bool CoalesceEventsIfNeeded(x11::Event* const x11_event,
+bool CoalesceEventsIfNeeded(x11::Event* const xev,
EventType type,
x11::Event* out) {
- XEvent* xev = &x11_event->xlib_event();
- if (xev->type == x11::MotionNotifyEvent::opcode ||
- (xev->type == x11::GeGenericEvent::opcode &&
+ if (xev->As<x11::MotionNotifyEvent>() ||
+ (xev->As<x11::Input::DeviceEvent>() &&
(type == ui::ET_TOUCH_MOVED || type == ui::ET_MOUSE_MOVED ||
type == ui::ET_MOUSE_DRAGGED))) {
- return ui::CoalescePendingMotionEvents(x11_event, out) > 0;
+ return ui::CoalescePendingMotionEvents(xev, out) > 0;
}
return false;
}
+int GetKeyModifiers(const XDragDropClient* client) {
+ if (!client)
+ return ui::XGetMaskAsEventFlags();
+ return client->current_modifier_state();
+}
+
} // namespace
X11Window::X11Window(PlatformWindowDelegate* platform_window_delegate)
@@ -161,11 +162,9 @@ void X11Window::Initialize(PlatformWindowInitProperties properties) {
SetOpacity(kDragWidgetOpacity);
}
-#if defined(USE_OZONE)
SetWmDragHandler(this, this);
drag_drop_client_ = std::make_unique<XDragDropClient>(this, window());
-#endif
}
void X11Window::SetXEventDelegate(XEventDelegate* delegate) {
@@ -390,7 +389,7 @@ bool X11Window::ShouldUseNativeFrame() const {
}
void X11Window::SetCursor(PlatformCursor cursor) {
- XWindow::SetCursor(static_cast<X11Cursor*>(cursor)->xcursor());
+ XWindow::SetCursor(static_cast<X11Cursor*>(cursor));
}
void X11Window::MoveCursorTo(const gfx::Point& location) {
@@ -528,36 +527,40 @@ void X11Window::SetX11ExtensionDelegate(X11ExtensionDelegate* delegate) {
x11_extension_delegate_ = delegate;
}
-bool X11Window::HandleAsAtkEvent(x11::Event* x11_event) {
+bool X11Window::HandleAsAtkEvent(x11::Event* x11_event, bool transient) {
#if !BUILDFLAG(USE_ATK)
// TODO(crbug.com/1014934): Support ATK in Ozone/X11.
NOTREACHED();
return false;
#else
- XEvent* xev = &x11_event->xlib_event();
- DCHECK(xev);
- if (!x11_extension_delegate_ || (xev->type != x11::KeyEvent::Press &&
- xev->type != x11::KeyEvent::Release)) {
+ DCHECK(x11_event);
+ if (!x11_extension_delegate_ || !x11_event->As<x11::KeyEvent>())
return false;
- }
auto atk_key_event = AtkKeyEventFromXEvent(x11_event);
- return x11_extension_delegate_->OnAtkKeyEvent(atk_key_event.get());
+ return x11_extension_delegate_->OnAtkKeyEvent(atk_key_event.get(), transient);
#endif
}
-// CheckCanDispatchNextPlatformEvent is called by X11EventSourceLibevent to
-// determine whether X11Window instance (XEventDispatcher implementation) is
-// able to process next translated event sent by it. So, it's done through
-// |handle_next_event_| internal flag, used in subsequent CanDispatchEvent
-// call.
+// CheckCanDispatchNextPlatformEvent is called by X11EventSource so that
+// X11Window (XEventDispatcher implementation) can inspect |xev| and determine
+// whether it should be dispatched by this window once it gets translated into a
+// PlatformEvent.
void X11Window::CheckCanDispatchNextPlatformEvent(x11::Event* xev) {
if (is_shutting_down_)
return;
- current_xevent_ = XWindow::IsTargetedBy(*xev) ? xev : nullptr;
+ if (XWindow::IsTargetedBy(*xev)) {
+ current_xevent_ = xev;
+ return;
+ }
+ if (XWindow::IsTransientWindowTargetedBy(*xev)) {
+ current_xevent_ = xev;
+ current_xevent_target_transient_ = true;
+ }
}
void X11Window::PlatformEventDispatchFinished() {
current_xevent_ = nullptr;
+ current_xevent_target_transient_ = false;
}
PlatformEventDispatcher* X11Window::GetPlatformEventDispatcher() {
@@ -565,6 +568,13 @@ PlatformEventDispatcher* X11Window::GetPlatformEventDispatcher() {
}
bool X11Window::DispatchXEvent(x11::Event* xev) {
+ auto* prop = xev->As<x11::PropertyNotifyEvent>();
+ auto* target_current_context = drag_drop_client_->target_current_context();
+ if (prop && target_current_context &&
+ prop->window == target_current_context->source_window()) {
+ return target_current_context->DispatchPropertyNotifyEvent(*prop);
+ }
+
if (!XWindow::IsTargetedBy(*xev))
return false;
XWindow::ProcessEvent(xev);
@@ -588,7 +598,7 @@ uint32_t X11Window::DispatchEvent(const PlatformEvent& event) {
X11WindowManager::GetInstance()->MouseOnWindow(this);
#if BUILDFLAG(USE_ATK)
// TODO(crbug.com/1014934): Support ATK in Ozone/X11.
- if (HandleAsAtkEvent(current_xevent_))
+ if (HandleAsAtkEvent(current_xevent_, current_xevent_target_transient_))
return POST_DISPATCH_STOP_PROPAGATION;
#endif
@@ -600,9 +610,6 @@ void X11Window::DispatchUiEvent(ui::Event* event, x11::Event* xev) {
auto* window_manager = X11WindowManager::GetInstance();
DCHECK(window_manager);
- if (DispatchDraggingUiEvent(event))
- return;
-
// Process X11-specific bits
if (XWindow::IsTargetedBy(*xev))
XWindow::ProcessEvent(xev);
@@ -640,12 +647,18 @@ void X11Window::DispatchUiEvent(ui::Event* event, x11::Event* xev) {
// data. See more discussion in https://crrev.com/c/853953
if (event) {
XWindow::UpdateWMUserTime(event);
+ bool event_dispatched = false;
#if defined(USE_OZONE)
- DispatchEventFromNativeUiEvent(
- event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
- base::Unretained(platform_window_delegate())));
-#else
- platform_window_delegate_->DispatchEvent(event);
+ if (features::IsUsingOzonePlatform()) {
+ event_dispatched = true;
+ DispatchEventFromNativeUiEvent(
+ event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
+ base::Unretained(platform_window_delegate())));
+ }
+#endif
+#if defined(USE_X11)
+ if (!event_dispatched)
+ platform_window_delegate_->DispatchEvent(event);
#endif
}
}
@@ -753,19 +766,15 @@ void X11Window::OnXWindowLostPointerGrab() {
void X11Window::OnXWindowSelectionEvent(x11::Event* xev) {
if (x_event_delegate_)
x_event_delegate_->OnXWindowSelectionEvent(xev);
-#if defined(USE_OZONE)
DCHECK(drag_drop_client_);
drag_drop_client_->OnSelectionNotify(*xev->As<x11::SelectionNotifyEvent>());
-#endif
}
void X11Window::OnXWindowDragDropEvent(x11::Event* xev) {
if (x_event_delegate_)
x_event_delegate_->OnXWindowDragDropEvent(xev);
-#if defined(USE_OZONE)
DCHECK(drag_drop_client_);
drag_drop_client_->HandleXdndEvent(*xev->As<x11::ClientMessageEvent>());
-#endif
}
base::Optional<gfx::Size> X11Window::GetMinimumSizeForXWindow() {
@@ -796,10 +805,10 @@ void X11Window::EndMoveLoop() {
x11_window_move_client_->EndMoveLoop();
}
-#if defined(USE_OZONE)
-void X11Window::StartDrag(const OSExchangeData& data,
+bool X11Window::StartDrag(const OSExchangeData& data,
int operation,
gfx::NativeCursor cursor,
+ bool can_grab_pointer,
WmDragHandler::Delegate* delegate) {
DCHECK(drag_drop_client_);
DCHECK(!drag_handler_delegate_);
@@ -809,9 +818,21 @@ void X11Window::StartDrag(const OSExchangeData& data,
drag_operation_ = 0;
notified_enter_ = false;
- SetCapture();
+ drag_loop_ = std::make_unique<X11WholeScreenMoveLoop>(this);
+
+ auto alive = weak_ptr_factory_.GetWeakPtr();
+ const bool dropped =
+ drag_loop_->RunMoveLoop(can_grab_pointer, last_cursor(), last_cursor());
+ if (!alive)
+ return false;
- dragging_ = true;
+ drag_loop_.reset();
+ drag_handler_delegate_ = nullptr;
+ return dropped;
+}
+
+void X11Window::CancelDrag() {
+ QuitDragLoop();
}
std::unique_ptr<XTopmostWindowFinder> X11Window::CreateWindowFinder() {
@@ -822,22 +843,36 @@ int X11Window::UpdateDrag(const gfx::Point& screen_point) {
WmDropHandler* drop_handler = GetWmDropHandler(*this);
if (!drop_handler)
return DragDropTypes::DRAG_NONE;
+
+ DCHECK(drag_drop_client_);
+ auto* target_current_context = drag_drop_client_->target_current_context();
+ DCHECK(target_current_context);
+
+ auto data = std::make_unique<OSExchangeData>(
+ std::make_unique<XOSExchangeDataProvider>(
+ drag_drop_client_->xwindow(),
+ target_current_context->fetched_targets()));
+ int suggested_operations = target_current_context->GetDragOperation();
+ // KDE-based file browsers such as Dolphin change the drag operation depending
+ // on whether alt/ctrl/shift was pressed. However once Chromium gets control
+ // over the X11 events, the source application does no longer receive X11
+ // events for key modifier changes, so the dnd operation gets stuck in an
+ // incorrect state. Blink can only dnd-open files of type DRAG_COPY, so the
+ // DRAG_COPY mask is added if the dnd object is a file.
+ if (data->HasFile() && (suggested_operations & (DragDropTypes::DRAG_MOVE |
+ DragDropTypes::DRAG_LINK))) {
+ suggested_operations |= DragDropTypes::DRAG_COPY;
+ }
+
if (!notified_enter_) {
- DCHECK(drag_drop_client_);
- auto* target_current_context = drag_drop_client_->target_current_context();
- DCHECK(target_current_context);
drop_handler->OnDragEnter(
- gfx::PointF(screen_point),
- std::make_unique<ui::OSExchangeData>(
- std::make_unique<ui::XOSExchangeDataProvider>(
- drag_drop_client_->xwindow(),
- target_current_context->fetched_targets())),
- ui::DragDropTypes::DRAG_COPY);
+ gfx::PointF(screen_point), std::move(data), suggested_operations,
+ GetKeyModifiers(target_current_context->source_client()));
notified_enter_ = true;
}
-
- drag_operation_ = drop_handler->OnDragMotion(gfx::PointF(screen_point),
- ui::DragDropTypes::DRAG_COPY);
+ drag_operation_ = drop_handler->OnDragMotion(
+ gfx::PointF(screen_point), suggested_operations,
+ GetKeyModifiers(target_current_context->source_client()));
return drag_operation_;
}
@@ -848,11 +883,12 @@ void X11Window::UpdateCursor(
}
void X11Window::OnBeginForeignDrag(x11::Window window) {
- NOTIMPLEMENTED_LOG_ONCE();
+ source_window_events_ =
+ std::make_unique<ui::XScopedEventSelector>(window, PropertyChangeMask);
}
void X11Window::OnEndForeignDrag() {
- NOTIMPLEMENTED_LOG_ONCE();
+ source_window_events_.reset();
}
void X11Window::OnBeforeDragLeave() {
@@ -872,58 +908,39 @@ int X11Window::PerformDrop() {
// The drop data has been supplied on entering the window. The drop handler
// should have it since then.
- drop_handler->OnDragDrop({});
+ auto* target_current_context = drag_drop_client_->target_current_context();
+ DCHECK(target_current_context);
+ drop_handler->OnDragDrop(
+ {}, GetKeyModifiers(target_current_context->source_client()));
notified_enter_ = false;
return drag_operation_;
}
void X11Window::EndDragLoop() {
DCHECK(drag_handler_delegate_);
+
drag_handler_delegate_->OnDragFinished(drag_operation_);
- drag_handler_delegate_ = nullptr;
+ drag_loop_->EndMoveLoop();
}
-#endif // defined(USE_OZONE)
-bool X11Window::DispatchDraggingUiEvent(Event* event) {
-#if defined(USE_OZONE)
- // Drag and drop have a priority over other processing.
- if (dragging_) {
- DCHECK(drag_drop_client_);
-
- switch (event->type()) {
- case ET_MOUSE_MOVED:
- case ET_MOUSE_DRAGGED: {
- drag_handler_delegate_->OnDragLocationChanged(
- event->AsLocatedEvent()->root_location());
- drag_drop_client_->HandleMouseMovement(
- event->AsLocatedEvent()->root_location(),
- event->AsMouseEvent()->flags(),
- event->AsMouseEvent()->time_stamp());
- return true;
- }
- case ET_MOUSE_RELEASED:
- if (!event->AsMouseEvent()->IsLeftMouseButton())
- break;
- // Assume that drags are being done with the left mouse button. Only
- // break the drag if the left mouse button was released.
- drag_drop_client_->HandleMouseReleased();
- dragging_ = false;
- ReleaseCapture();
- return true;
- case ET_KEY_PRESSED:
- if (event->AsKeyEvent()->key_code() != VKEY_ESCAPE)
- break;
- EndMoveLoop();
- drag_drop_client_->HandleMoveLoopEnded();
- dragging_ = false;
- ReleaseCapture();
- return true;
- default:
- break;
- }
- }
-#endif // defined(USE_OZONE)
- return false;
+void X11Window::OnMouseMovement(const gfx::Point& screen_point,
+ int flags,
+ base::TimeTicks event_time) {
+ drag_handler_delegate_->OnDragLocationChanged(screen_point);
+ drag_drop_client_->HandleMouseMovement(screen_point, flags, event_time);
+}
+
+void X11Window::OnMouseReleased() {
+ drag_drop_client_->HandleMouseReleased();
+}
+
+void X11Window::OnMoveLoopEnded() {
+ drag_drop_client_->HandleMoveLoopEnded();
+}
+
+void X11Window::QuitDragLoop() {
+ DCHECK(drag_loop_);
+ drag_loop_->EndMoveLoop();
}
gfx::Size X11Window::AdjustSizeForDisplay(
diff --git a/chromium/ui/platform_window/x11/x11_window.h b/chromium/ui/platform_window/x11/x11_window.h
index 011fbee55c3..69705f960ae 100644
--- a/chromium/ui/platform_window/x11/x11_window.h
+++ b/chromium/ui/platform_window/x11/x11_window.h
@@ -6,6 +6,8 @@
#define UI_PLATFORM_WINDOW_X11_X11_WINDOW_H_
#include "base/macros.h"
+#include "ui/base/x/x11_drag_drop_client.h"
+#include "ui/base/x/x11_move_loop_delegate.h"
#include "ui/base/x/x11_window.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/x11/x11_event_source.h"
@@ -13,21 +15,18 @@
#include "ui/platform_window/extensions/workspace_extension.h"
#include "ui/platform_window/extensions/x11_extension.h"
#include "ui/platform_window/platform_window.h"
-#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
#include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_move_loop_handler.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
#include "ui/platform_window/x11/x11_window_export.h"
-#if defined(USE_OZONE)
-#include "ui/base/x/x11_drag_drop_client.h"
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
-#endif
-
namespace ui {
class PlatformWindowDelegate;
class X11ExtensionDelegate;
class X11DesktopWindowMoveClient;
+class X11MoveLoop;
class LocatedEvent;
class WorkspaceExtensionDelegate;
@@ -51,10 +50,9 @@ class X11_WINDOW_EXPORT X11Window : public PlatformWindow,
public XEventDispatcher,
public WorkspaceExtension,
public X11Extension,
-#if defined(USE_OZONE)
public WmDragHandler,
public XDragDropClient::Delegate,
-#endif
+ public X11MoveLoopDelegate,
public WmMoveLoopHandler {
public:
explicit X11Window(PlatformWindowDelegate* platform_window_delegate);
@@ -146,8 +144,6 @@ class X11_WINDOW_EXPORT X11Window : public PlatformWindow,
// XWindow:
void OnXWindowCreated() override;
- virtual bool DispatchDraggingUiEvent(ui::Event* event);
-
// XWindow:
void OnXWindowStateChanged() override;
void OnXWindowDamageEvent(const gfx::Rect& damage_rect) override;
@@ -179,27 +175,35 @@ class X11_WINDOW_EXPORT X11Window : public PlatformWindow,
bool RunMoveLoop(const gfx::Vector2d& drag_offset) override;
void EndMoveLoop() override;
-#if defined(USE_OZONE)
// WmDragHandler
- void StartDrag(const ui::OSExchangeData& data,
+ bool StartDrag(const OSExchangeData& data,
int operation,
gfx::NativeCursor cursor,
+ bool can_grab_pointer,
WmDragHandler::Delegate* delegate) override;
+ void CancelDrag() override;
- // ui::XDragDropClient::Delegate
- std::unique_ptr<ui::XTopmostWindowFinder> CreateWindowFinder() override;
+ // XDragDropClient::Delegate
+ std::unique_ptr<XTopmostWindowFinder> CreateWindowFinder() override;
int UpdateDrag(const gfx::Point& screen_point) override;
- void UpdateCursor(
- ui::DragDropTypes::DragOperation negotiated_operation) override;
+ void UpdateCursor(DragDropTypes::DragOperation negotiated_operation) override;
void OnBeginForeignDrag(x11::Window window) override;
void OnEndForeignDrag() override;
void OnBeforeDragLeave() override;
int PerformDrop() override;
void EndDragLoop() override;
-#endif // defined(USE_OZONE)
+
+ // X11MoveLoopDelegate
+ void OnMouseMovement(const gfx::Point& screen_point,
+ int flags,
+ base::TimeTicks event_time) override;
+ void OnMouseReleased() override;
+ void OnMoveLoopEnded() override;
+
+ void QuitDragLoop();
// Handles |xevent| as a Atk Key Event
- bool HandleAsAtkEvent(x11::Event* xevent);
+ bool HandleAsAtkEvent(x11::Event* xevent, bool transient);
// Adjusts |requested_size_in_pixels| to avoid the WM "feature" where setting
// the window size to the monitor size causes the WM to set the EWMH for
@@ -239,11 +243,12 @@ class X11_WINDOW_EXPORT X11Window : public PlatformWindow,
// x11::Window target.
x11::Event* current_xevent_ = nullptr;
+ // True if the current_xevent_ target is not this window but a transient
+ // window that hangs from this one.
+ bool current_xevent_target_transient_ = false;
+
std::unique_ptr<X11DesktopWindowMoveClient> x11_window_move_client_;
-#if defined(USE_OZONE)
- // True while the drag initiated in this window is in progress.
- bool dragging_ = false;
// Whether the drop handler has notified that the drag has entered.
bool notified_enter_ = false;
// Keeps the last negotiated operation returned by the drop handler.
@@ -252,7 +257,14 @@ class X11_WINDOW_EXPORT X11Window : public PlatformWindow,
// Handles XDND events going through this window.
std::unique_ptr<XDragDropClient> drag_drop_client_;
WmDragHandler::Delegate* drag_handler_delegate_ = nullptr;
-#endif // defined(USE_OZONE)
+
+ // Run loop used while dragging from this window.
+ std::unique_ptr<X11MoveLoop> drag_loop_;
+
+ // Events that we have selected on the source window of the incoming drag.
+ std::unique_ptr<ui::XScopedEventSelector> source_window_events_;
+
+ base::WeakPtrFactory<X11Window> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(X11Window);
};
diff --git a/chromium/ui/resources/BUILD.gn b/chromium/ui/resources/BUILD.gn
index e6482b170c0..0d1ad112bcb 100644
--- a/chromium/ui/resources/BUILD.gn
+++ b/chromium/ui/resources/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/grit/repack.gni")
import("//ui/webui/webui_features.gni")
@@ -41,7 +40,7 @@ grit("webui_resources_grd") {
deps = [ "//ui/webui/resources:modulize" ]
if (is_chromeos) {
- deps += [ "//ui/webui/resources/css:cros_colors_css" ]
+ deps += [ "//ui/chromeos/colors:cros_colors_css" ]
}
outputs = [
@@ -66,7 +65,7 @@ if (!is_mac) {
# On iOS and Mac the string resources need to go into a locale subfolder, which
# introduces an extra dependency.
-if (is_ios || is_mac) {
+if (is_apple) {
group("ui_test_pak") {
public_deps = [
":repack_ui_test_mac_locale_pack",
diff --git a/chromium/ui/resources/default_100_percent/common/fingerprint.png b/chromium/ui/resources/default_100_percent/common/fingerprint.png
deleted file mode 100644
index 9e471fc0bde..00000000000
--- a/chromium/ui/resources/default_100_percent/common/fingerprint.png
+++ /dev/null
Binary files differ
diff --git a/chromium/ui/resources/default_100_percent/common/tick.png b/chromium/ui/resources/default_100_percent/common/tick.png
deleted file mode 100644
index 5eca882ed03..00000000000
--- a/chromium/ui/resources/default_100_percent/common/tick.png
+++ /dev/null
Binary files differ
diff --git a/chromium/ui/resources/default_200_percent/common/fingerprint.png b/chromium/ui/resources/default_200_percent/common/fingerprint.png
deleted file mode 100644
index 9f6f07cee2c..00000000000
--- a/chromium/ui/resources/default_200_percent/common/fingerprint.png
+++ /dev/null
Binary files differ
diff --git a/chromium/ui/resources/default_200_percent/common/tick.png b/chromium/ui/resources/default_200_percent/common/tick.png
deleted file mode 100644
index 4c8b6b89609..00000000000
--- a/chromium/ui/resources/default_200_percent/common/tick.png
+++ /dev/null
Binary files differ
diff --git a/chromium/ui/resources/ui_resources.grd b/chromium/ui/resources/ui_resources.grd
index 5f88d78bfde..7c4d37b3b60 100644
--- a/chromium/ui/resources/ui_resources.grd
+++ b/chromium/ui/resources/ui_resources.grd
@@ -114,8 +114,9 @@
<structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_HOVER" file="common/easy_unlock_unlocked_hover.png" />
<structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_PRESSED" file="common/easy_unlock_unlocked_pressed.png" />
<if expr="not is_android">
- <structure type="chrome_scaled_image" name="IDR_FINGERPRINT_COMPLETE_TICK" file="common/tick.png" />
- <structure type="chrome_scaled_image" name="IDR_FINGERPRINT_ICON_ANIMATION" file="common/fingerprint.png" />
+ <structure type="chrome_html" name="IDR_FINGERPRINT_COMPLETE_TICK" file="vector/common/tick.json" compress="gzip" />
+ <structure type="chrome_html" name="IDR_FINGERPRINT_COMPLETE_TICK_DARK" file="vector/common/tick_dark.json" compress="gzip" />
+ <structure type="chrome_html" name="IDR_FINGERPRINT_ICON_ANIMATION" file="vector/common/fingerprint_enrollment.json" compress="gzip" />
</if>
<if expr="is_macosx or is_ios">
<structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED" file="mac/folder.png" />
diff --git a/chromium/ui/resources/vector/common/fingerprint_enrollment.json b/chromium/ui/resources/vector/common/fingerprint_enrollment.json
new file mode 100644
index 00000000000..6c2e292adee
--- /dev/null
+++ b/chromium/ui/resources/vector/common/fingerprint_enrollment.json
@@ -0,0 +1 @@
+{"v":"5.5.3","fr":25,"ip":0,"op":76,"w":107,"h":119,"nm":"FINGERPRINT 1X","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"finger print 3","sr":1,"ks":{"o":{"a":0,"k":65,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[118,122,0],"ix":2},"a":{"a":0,"k":[196,3,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.737,8.296],[21.214,0.479],[0,0]],"o":[[-11.926,-14.685],[-37.158,-0.838],[0,0]],"v":[[241.801,-21.44],[194.235,-46.102],[142.633,-21.241]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":17.5,"s":[0]},{"t":69,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.14],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":59,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.905882358551,0.254901975393,0.20000000298,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.061,10.634],[27.044,-15.782],[-11.168,-9.022]],"o":[[1.382,-13.849],[-26.362,15.384],[8.232,6.651]],"v":[[222.618,10.474],[175.456,-13.218],[178.668,49.022]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":17.5,"s":[0]},{"t":69,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.14],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":59,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.196078434587,0.654901981354,0.32549020648,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-11.848,-9.468],[0,0]],"o":[[0,0],[9.127,7.294],[0,0]],"v":[[191.329,10.595],[203.348,33.468],[227.347,37.995]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":17.5,"s":[0]},{"t":69,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.14],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":59,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.976470589638,0.733333349228,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-18.76,17.634],[-19.182,-12.375],[12.739,-6.558],[0.808,12.798],[10.284,-8.313],[-9.031,-10.18],[0,0]],"o":[[0,0],[17.265,-16.229],[18.364,11.847],[-12.327,6.346],[-0.654,-10.37],[-10.001,8.084],[10.638,11.991],[0,0]],"v":[[150.664,32.671],[157.985,-18.021],[218.182,-24.125],[230.386,22.433],[206.279,8.87],[182.091,-0.312],[185.906,36.055],[208.611,50.423]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[0]},{"t":69,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.14],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":69,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.258823543787,0.521568655968,0.956862747669,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.686,6.225],[-12.924,-0.356],[0,0]],"o":[[9.57,-6.859],[17.386,0.479],[0,0]],"v":[[158.429,-51.762],[194.054,-60.332],[225.92,-52.004]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":17.5,"s":[0]},{"t":69,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.14],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0]},{"t":59,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.976470588235,0.733333333333,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":76,"st":-21,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"finger print 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[118,122,0],"ix":2},"a":{"a":0,"k":[196,3,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.737,8.296],[21.214,0.479],[0,0]],"o":[[-11.926,-14.685],[-37.158,-0.838],[0,0]],"v":[[241.801,-21.44],[194.235,-46.102],[142.633,-21.241]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.061,10.634],[27.044,-15.782],[-11.168,-9.022]],"o":[[1.382,-13.849],[-26.362,15.384],[8.232,6.651]],"v":[[222.618,10.474],[175.456,-13.218],[178.668,49.022]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-11.848,-9.468],[0,0]],"o":[[0,0],[9.127,7.294],[0,0]],"v":[[191.329,10.595],[203.348,33.468],[227.347,37.995]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-18.76,17.634],[-19.182,-12.375],[12.739,-6.558],[0.808,12.798],[10.284,-8.313],[-9.031,-10.18],[0,0]],"o":[[0,0],[17.265,-16.229],[18.364,11.847],[-12.327,6.346],[-0.654,-10.37],[-10.001,8.084],[10.638,11.991],[0,0]],"v":[[150.664,32.671],[157.985,-18.021],[218.182,-24.125],[230.386,22.433],[206.279,8.87],[182.091,-0.312],[185.906,36.055],[208.611,50.423]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.686,6.225],[-12.924,-0.356],[0,0]],"o":[[9.57,-6.859],[17.386,0.479],[0,0]],"v":[[158.429,-51.762],[194.054,-60.332],[225.92,-52.004]],"c":false},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.917647058824,0.929411764706,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":76,"st":18,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"circle big","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[114,114,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[63.84,0],[0,-60.48],[-54.32,0],[0,66.64]],"o":[[-67.76,0],[0,64.96],[54.32,0],[0,-62.72]],"v":[[1.24,-111.503],[-112,1.735],[-0.62,111.68],[112,1.735]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.854901960784,0.862745098039,0.878431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":76,"st":26,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"ENROLLMENT 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[53.5,59.5,0],"ix":2},"a":{"a":0,"k":[114,114,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":228,"h":228,"ip":0,"op":76,"st":0,"bm":0}],"markers":[]}
diff --git a/chromium/ui/resources/vector/common/tick.json b/chromium/ui/resources/vector/common/tick.json
new file mode 100644
index 00000000000..3257c334190
--- /dev/null
+++ b/chromium/ui/resources/vector/common/tick.json
@@ -0,0 +1 @@
+{"v":"5.5.3","fr":25,"ip":0,"op":100,"w":60,"h":60,"nm":"TICK 1X","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"tick","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[63.25,77.438],[74.562,88.25],[93.875,69.062]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[0]},{"t":22,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.196078431373,0.654901960784,0.325490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":8,"op":100,"st":-24,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"tick circle ","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30.5,30.75,0],"ix":2},"a":{"a":0,"k":[79.25,79.75,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-0.425,0],[0,0.403],[0.362,0],[0,-0.444]],"o":[[0.451,0],[0,-0.433],[-0.362,0],[0,0.418]],"v":[[78.743,79.75],[79.496,78.985],[78.754,78.25],[78.004,78.985]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[{"i":[[15.03,0],[0,-14.239],[-12.789,0],[0,15.689]],"o":[[-15.953,0],[0,15.294],[12.789,0],[0,-14.766]],"v":[[79.014,52.5],[52.382,79.527],[78.618,105.5],[105.118,79.527]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":16,"s":[{"i":[[14.179,0],[0,-13.433],[-12.065,0],[0,14.801]],"o":[[-15.05,0],[0,14.428],[12.065,0],[0,-13.93]],"v":[[78.999,54],[53.874,79.498],[78.626,104],[103.626,79.498]],"c":true}]},{"t":23,"s":[{"i":[[14.25,0],[0,-13.5],[-12.125,0],[0,14.875]],"o":[[-15.125,0],[0,14.5],[12.125,0],[0,-14]],"v":[[79,53.875],[53.75,79.5],[78.625,104.125],[103.75,79.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.196078431373,0.654901960784,0.325490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":-22,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/chromium/ui/resources/vector/common/tick_dark.json b/chromium/ui/resources/vector/common/tick_dark.json
new file mode 100644
index 00000000000..22cad0e7d4f
--- /dev/null
+++ b/chromium/ui/resources/vector/common/tick_dark.json
@@ -0,0 +1 @@
+{"v":"5.5.3","fr":25,"ip":0,"op":100,"w":60,"h":60,"nm":"TICK 1X","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"tick","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[63.25,77.438],[74.562,88.25],[93.875,69.062]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[0]},{"t":22,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.196078431373,0.654901960784,0.325490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.14,0.14,0.14,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":8,"op":100,"st":-24,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"tick circle ","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30.5,30.75,0],"ix":2},"a":{"a":0,"k":[79.25,79.75,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-0.425,0],[0,0.403],[0.362,0],[0,-0.444]],"o":[[0.451,0],[0,-0.433],[-0.362,0],[0,0.418]],"v":[[78.743,79.75],[79.496,78.985],[78.754,78.25],[78.004,78.985]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[{"i":[[15.03,0],[0,-14.239],[-12.789,0],[0,15.689]],"o":[[-15.953,0],[0,15.294],[12.789,0],[0,-14.766]],"v":[[79.014,52.5],[52.382,79.527],[78.618,105.5],[105.118,79.527]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":16,"s":[{"i":[[14.179,0],[0,-13.433],[-12.065,0],[0,14.801]],"o":[[-15.05,0],[0,14.428],[12.065,0],[0,-13.93]],"v":[[78.999,54],[53.874,79.498],[78.626,104],[103.626,79.498]],"c":true}]},{"t":23,"s":[{"i":[[14.25,0],[0,-13.5],[-12.125,0],[0,14.875]],"o":[[-15.125,0],[0,14.5],[12.125,0],[0,-14]],"v":[[79,53.875],[53.75,79.5],[78.625,104.125],[103.75,79.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.196078431373,0.654901960784,0.325490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.14,0.14,0.14,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":-22,"bm":0}],"markers":[]}
diff --git a/chromium/ui/shell_dialogs/BUILD.gn b/chromium/ui/shell_dialogs/BUILD.gn
index 84a9a652ea9..fa4fbd628f2 100644
--- a/chromium/ui/shell_dialogs/BUILD.gn
+++ b/chromium/ui/shell_dialogs/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//build/config/chromeos/ui_mode.gni")
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
if (is_android) {
@@ -19,7 +18,7 @@ if (is_mac) {
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("shell_dialogs") {
+component("shell_dialogs") {
sources = [
"base_shell_dialog.cc",
"base_shell_dialog.h",
@@ -34,7 +33,7 @@ jumbo_component("shell_dialogs") {
"shell_dialogs_export.h",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"shell_dialog_linux.cc",
"shell_dialog_linux.h",
@@ -92,7 +91,7 @@ jumbo_component("shell_dialogs") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"CoreServices.framework",
"Foundation.framework",
"AppKit.framework",
@@ -114,6 +113,12 @@ jumbo_component("shell_dialogs") {
"select_file_dialog_lacros.cc",
"select_file_dialog_lacros.h",
]
+ deps += [
+ "//chromeos/crosapi/mojom",
+ "//chromeos/lacros",
+ "//mojo/public/cpp/bindings",
+ "//ui/platform_window",
+ ]
}
}
diff --git a/chromium/ui/shell_dialogs/DEPS b/chromium/ui/shell_dialogs/DEPS
index 2cff6e24a31..c13f289ab68 100644
--- a/chromium/ui/shell_dialogs/DEPS
+++ b/chromium/ui/shell_dialogs/DEPS
@@ -1,4 +1,6 @@
include_rules = [
+ "+chromeos/crosapi",
+ "+chromeos/lacros",
"+components/remote_cocoa",
"+mojo/core/embedder",
"+mojo/public/cpp/bindings",
@@ -6,5 +8,6 @@ include_rules = [
"+ui/aura",
"+ui/base",
"+ui/gfx",
+ "+ui/platform_window",
"+ui/strings/grit/ui_strings.h",
]
diff --git a/chromium/ui/shell_dialogs/OWNERS b/chromium/ui/shell_dialogs/OWNERS
index 5fc4ca6502b..7179f1f92fe 100644
--- a/chromium/ui/shell_dialogs/OWNERS
+++ b/chromium/ui/shell_dialogs/OWNERS
@@ -1,5 +1,7 @@
per-file *android*=miguelg@chromium.org
per-file *android*=qinmin@chromium.org
+per-file *lacros*=file://chromeos/LACROS_OWNERS
+
per-file *win*=robliao@chromium.org
# COMPONENT: Internals>PlatformIntegration
diff --git a/chromium/ui/shell_dialogs/run_all_unittests.cc b/chromium/ui/shell_dialogs/run_all_unittests.cc
index bb522a2d179..89ed9695f08 100644
--- a/chromium/ui/shell_dialogs/run_all_unittests.cc
+++ b/chromium/ui/shell_dialogs/run_all_unittests.cc
@@ -12,7 +12,7 @@
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "base/test/mock_chrome_application_mac.h"
#endif
@@ -37,7 +37,7 @@ ShellDialogsTestSuite::ShellDialogsTestSuite(int argc, char** argv)
void ShellDialogsTestSuite::Initialize() {
base::TestSuite::Initialize();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
mock_cr_app::RegisterMockCrApp();
#endif
diff --git a/chromium/ui/shell_dialogs/select_file_dialog.h b/chromium/ui/shell_dialogs/select_file_dialog.h
index 28e1288ea18..832c12fb4a6 100644
--- a/chromium/ui/shell_dialogs/select_file_dialog.h
+++ b/chromium/ui/shell_dialogs/select_file_dialog.h
@@ -137,13 +137,20 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog
// Specifies whether there will be a filter added for all files (i.e. *.*).
bool include_all_files;
- // Specifies which type of paths the caller can handle. If it is
- // NATIVE_PATH, the dialog creates a native replica of the non-native file
- // and returns its path, so that the caller can use it without any
- // difference than when it were local.
+ // Specifies which type of paths the caller can handle.
enum AllowedPaths {
+ // Any type of path, whether on a local/native volume or a remote/virtual
+ // volume. Excludes files that can only be opened by URL; for those use
+ // ANY_PATH_OR_URL below.
ANY_PATH,
+ // Set when the caller cannot handle virtual volumes (e.g. File System
+ // Provider [FSP] volumes like "File System for Dropbox"). When opening
+ // files, the dialog will create a native replica of the file and return
+ // its path. When saving files, the dialog will hide virtual volumes.
NATIVE_PATH,
+ // Set when the caller can open files via URL. For example, when opening a
+ // .gdoc file from Google Drive the file is opened by navigating to a
+ // docs.google.com URL.
ANY_PATH_OR_URL
};
AllowedPaths allowed_paths;
diff --git a/chromium/ui/shell_dialogs/select_file_dialog_lacros.cc b/chromium/ui/shell_dialogs/select_file_dialog_lacros.cc
index 53d0cdbe8a3..61c38ce0444 100644
--- a/chromium/ui/shell_dialogs/select_file_dialog_lacros.cc
+++ b/chromium/ui/shell_dialogs/select_file_dialog_lacros.cc
@@ -4,12 +4,76 @@
#include "ui/shell_dialogs/select_file_dialog_lacros.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/notreached.h"
-#include "base/task/thread_pool.h"
+#include "chromeos/crosapi/mojom/select_file.mojom.h"
+#include "chromeos/lacros/lacros_chrome_service_impl.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host_platform.h"
+#include "ui/platform_window/platform_window.h"
#include "ui/shell_dialogs/select_file_policy.h"
+#include "ui/shell_dialogs/selected_file_info.h"
namespace ui {
+namespace {
+
+crosapi::mojom::SelectFileDialogType GetMojoType(SelectFileDialog::Type type) {
+ switch (type) {
+ case SelectFileDialog::Type::SELECT_FOLDER:
+ return crosapi::mojom::SelectFileDialogType::kFolder;
+ case SelectFileDialog::Type::SELECT_UPLOAD_FOLDER:
+ return crosapi::mojom::SelectFileDialogType::kUploadFolder;
+ case SelectFileDialog::Type::SELECT_EXISTING_FOLDER:
+ return crosapi::mojom::SelectFileDialogType::kExistingFolder;
+ case SelectFileDialog::Type::SELECT_OPEN_FILE:
+ return crosapi::mojom::SelectFileDialogType::kOpenFile;
+ case SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE:
+ return crosapi::mojom::SelectFileDialogType::kOpenMultiFile;
+ case SelectFileDialog::Type::SELECT_SAVEAS_FILE:
+ return crosapi::mojom::SelectFileDialogType::kSaveAsFile;
+ case SelectFileDialog::Type::SELECT_NONE:
+ NOTREACHED();
+ return crosapi::mojom::SelectFileDialogType::kOpenFile;
+ }
+}
+
+crosapi::mojom::AllowedPaths GetMojoAllowedPaths(
+ SelectFileDialog::FileTypeInfo::AllowedPaths allowed_paths) {
+ switch (allowed_paths) {
+ case SelectFileDialog::FileTypeInfo::ANY_PATH:
+ return crosapi::mojom::AllowedPaths::kAnyPath;
+ case SelectFileDialog::FileTypeInfo::NATIVE_PATH:
+ return crosapi::mojom::AllowedPaths::kNativePath;
+ case SelectFileDialog::FileTypeInfo::ANY_PATH_OR_URL:
+ return crosapi::mojom::AllowedPaths::kAnyPathOrUrl;
+ }
+}
+
+SelectedFileInfo ConvertSelectedFileInfo(
+ crosapi::mojom::SelectedFileInfoPtr mojo_file) {
+ SelectedFileInfo file;
+ file.file_path = std::move(mojo_file->file_path);
+ file.local_path = std::move(mojo_file->local_path);
+ file.display_name = std::move(mojo_file->display_name);
+ file.url = std::move(mojo_file->url);
+ return file;
+}
+
+// Returns the ID of the Wayland shell surface that contains |window|.
+std::string GetShellWindowUniqueId(aura::Window* window) {
+ // On desktop aura there is one WindowTreeHost per top-level window.
+ aura::WindowTreeHost* window_tree_host = window->GetRootWindow()->GetHost();
+ DCHECK(window_tree_host);
+ // Lacros is based on Ozone/Wayland, which uses PlatformWindow and
+ // aura::WindowTreeHostPlatform.
+ aura::WindowTreeHostPlatform* window_tree_host_platform =
+ static_cast<aura::WindowTreeHostPlatform*>(window_tree_host);
+ return window_tree_host_platform->platform_window()->GetWindowUniqueId();
+}
+
+} // namespace
SelectFileDialogLacros::Factory::Factory() = default;
SelectFileDialogLacros::Factory::~Factory() = default;
@@ -44,17 +108,52 @@ void SelectFileDialogLacros::SelectFileImpl(
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) {
- // TODO(https://crbug.com/1090587): Proxy the request over IPC to ash-chrome.
- NOTIMPLEMENTED();
- // Until we have an implementation, pretend the user cancelled the dialog.
- // Post a task to avoid reentrancy issues. |this| is ref-counted.
- base::ThreadPool::PostTask(
- FROM_HERE, base::BindOnce(&SelectFileDialogLacros::Cancel, this, params));
+ params_ = params;
+
+ crosapi::mojom::SelectFileOptionsPtr options =
+ crosapi::mojom::SelectFileOptions::New();
+ options->type = GetMojoType(type);
+ options->title = title;
+ options->default_path = default_path;
+ if (file_types) {
+ options->file_types = crosapi::mojom::SelectFileTypeInfo::New();
+ options->file_types->extensions = file_types->extensions;
+ options->file_types->extension_description_overrides =
+ file_types->extension_description_overrides;
+ // NOTE: Index is 1-based, 0 means "no selection".
+ options->file_types->default_file_type_index = file_type_index;
+ options->file_types->include_all_files = file_types->include_all_files;
+ options->file_types->allowed_paths =
+ GetMojoAllowedPaths(file_types->allowed_paths);
+ }
+ options->owning_shell_window_id = GetShellWindowUniqueId(owning_window);
+
+ // Send request to ash-chrome.
+ chromeos::LacrosChromeServiceImpl::Get()->select_file_remote()->Select(
+ std::move(options),
+ base::BindOnce(&SelectFileDialogLacros::OnSelected, this));
}
-void SelectFileDialogLacros::Cancel(void* params) {
- if (listener_)
- listener_->FileSelectionCanceled(params);
+void SelectFileDialogLacros::OnSelected(
+ crosapi::mojom::SelectFileResult result,
+ std::vector<crosapi::mojom::SelectedFileInfoPtr> mojo_files,
+ int file_type_index) {
+ if (!listener_)
+ return;
+ if (mojo_files.empty()) {
+ listener_->FileSelectionCanceled(params_);
+ return;
+ }
+ if (mojo_files.size() == 1) {
+ SelectedFileInfo file = ConvertSelectedFileInfo(std::move(mojo_files[0]));
+ listener_->FileSelectedWithExtraInfo(file, file_type_index, params_);
+ return;
+ }
+ std::vector<SelectedFileInfo> files;
+ for (auto& mojo_file : mojo_files) {
+ files.push_back(ConvertSelectedFileInfo(std::move(mojo_file)));
+ }
+ listener_->MultiFilesSelectedWithExtraInfo(files, params_);
}
} // namespace ui
diff --git a/chromium/ui/shell_dialogs/select_file_dialog_lacros.h b/chromium/ui/shell_dialogs/select_file_dialog_lacros.h
index 145781b7a7e..9527c314b8f 100644
--- a/chromium/ui/shell_dialogs/select_file_dialog_lacros.h
+++ b/chromium/ui/shell_dialogs/select_file_dialog_lacros.h
@@ -5,6 +5,9 @@
#ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_LACROS_H_
#define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_LACROS_H_
+#include <vector>
+
+#include "chromeos/crosapi/mojom/select_file.mojom-forward.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
#include "ui/shell_dialogs/shell_dialogs_export.h"
@@ -14,7 +17,7 @@ namespace ui {
// SelectFileDialogLacros implements file open and save dialogs for the
// lacros-chrome binary. The dialog itself is handled by the file manager in
// ash-chrome.
-class SelectFileDialogLacros : public SelectFileDialog {
+class SHELL_DIALOGS_EXPORT SelectFileDialogLacros : public SelectFileDialog {
public:
class SHELL_DIALOGS_EXPORT Factory : public SelectFileDialogFactory {
public:
@@ -51,8 +54,13 @@ class SelectFileDialogLacros : public SelectFileDialog {
// Private because SelectFileDialog is ref-counted.
~SelectFileDialogLacros() override;
- // Calls the listener's cancel method with |params|.
- void Cancel(void* params);
+ // Callback for file selection.
+ void OnSelected(crosapi::mojom::SelectFileResult result,
+ std::vector<crosapi::mojom::SelectedFileInfoPtr> files,
+ int file_type_index);
+
+ // Cached parameters from the call to SelectFileImpl.
+ void* params_ = nullptr;
};
} // namespace ui
diff --git a/chromium/ui/shell_dialogs/selected_file_info.h b/chromium/ui/shell_dialogs/selected_file_info.h
index 6f76bef664b..01ad4b2ed71 100644
--- a/chromium/ui/shell_dialogs/selected_file_info.h
+++ b/chromium/ui/shell_dialogs/selected_file_info.h
@@ -35,7 +35,8 @@ struct SHELL_DIALOGS_EXPORT SelectedFileInfo {
// If set, this URL may be used to access the file. If the user is capable of
// using a URL to access the file, it should be used in preference to
- // |local_path|.
+ // |local_path|. For example, when opening a .gdoc file from Google Drive the
+ // file is opened by navigating to a docs.google.com URL.
base::Optional<GURL> url;
SelectedFileInfo();
diff --git a/chromium/ui/snapshot/BUILD.gn b/chromium/ui/snapshot/BUILD.gn
index b3aa01143d4..bdda1ebc10a 100644
--- a/chromium/ui/snapshot/BUILD.gn
+++ b/chromium/ui/snapshot/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -12,7 +11,7 @@ import("//testing/test.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("snapshot") {
+component("snapshot") {
sources = [
"screenshot_grabber.cc",
"screenshot_grabber.h",
@@ -79,19 +78,23 @@ jumbo_component("snapshot") {
}
if (is_mac) {
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreGraphics.framework",
]
}
}
-jumbo_source_set("snapshot_export") {
+source_set("snapshot_export") {
sources = [ "snapshot_export.h" ]
visibility = [ ":*" ]
}
test("snapshot_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [ "test/run_all_unittests.cc" ]
if (is_mac) {
diff --git a/chromium/ui/snapshot/screenshot_grabber.cc b/chromium/ui/snapshot/screenshot_grabber.cc
index 2145955efbe..5401712a5a2 100644
--- a/chromium/ui/snapshot/screenshot_grabber.cc
+++ b/chromium/ui/snapshot/screenshot_grabber.cc
@@ -14,8 +14,8 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/single_thread_task_runner.h"
+#include "base/task/current_thread.h"
#include "base/task/post_task.h"
#include "base/task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -73,7 +73,7 @@ ScreenshotGrabber::~ScreenshotGrabber() {
void ScreenshotGrabber::TakeScreenshot(gfx::NativeWindow window,
const gfx::Rect& rect,
ScreenshotCallback callback) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
last_screenshot_timestamp_ = base::TimeTicks::Now();
bool is_partial = true;
@@ -106,7 +106,7 @@ void ScreenshotGrabber::GrabWindowSnapshotAsyncCallback(
bool is_partial,
ScreenshotCallback callback,
scoped_refptr<base::RefCountedMemory> png_data) {
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
#if defined(USE_AURA)
cursor_hider_.reset();
diff --git a/chromium/ui/snapshot/snapshot_aura_unittest.cc b/chromium/ui/snapshot/snapshot_aura_unittest.cc
index 08b4053148f..0537792f9c3 100644
--- a/chromium/ui/snapshot/snapshot_aura_unittest.cc
+++ b/chromium/ui/snapshot/snapshot_aura_unittest.cc
@@ -200,12 +200,12 @@ INSTANTIATE_TEST_SUITE_P(All, SnapshotAuraTest, ::testing::Bool());
#define MAYBE_FullScreenWindow FullScreenWindow
#endif
TEST_P(SnapshotAuraTest, MAYBE_FullScreenWindow) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// TODO(https://crbug.com/1002716): Fix this test to run in < action_timeout()
// on the Linux Debug & TSAN bots.
const base::test::ScopedRunLoopTimeout increased_run_timeout(
FROM_HERE, TestTimeouts::action_max_timeout());
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
#if defined(OS_WIN)
// TODO(https://crbug.com/850556): Make work on Win10.
diff --git a/chromium/ui/strings/translations/ui_strings_af.xtb b/chromium/ui/strings/translations/ui_strings_af.xtb
index 62da5987a30..d8b7f0cb0d2 100644
--- a/chromium/ui/strings/translations/ui_strings_af.xtb
+++ b/chromium/ui/strings/translations/ui_strings_af.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">kies</translation>
<translation id="1859234291848436338">Skryfrigting</translation>
<translation id="1860796786778352021">Kennisgewing toemaak</translation>
+<translation id="186476001994626254">Webslimplak-inhoud</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Kies almal</translation>
+<translation id="19085484004813472">Dit is 'n nuwe kenmerk</translation>
<translation id="2006524834898217237">Maak seker dat hierdie toestel aan die internet gekoppel is.</translation>
<translation id="208586643495776849">Probeer asseblief weer</translation>
<translation id="2090963878406559571">Om 'n nommer van <ph name="ORIGIN" /> af na jou Android-foon toe te stuur, moet jy vir albei toestelle <ph name="TROUBLESHOOT_LINK" /> in instellings.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Bestuur instellings</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{oor 1 u.}other{oor # u.}}</translation>
<translation id="2583543531130364912">Kalibreer jou raakskerm</translation>
+<translation id="2586657967955657006">Knipbord</translation>
<translation id="2666092431469916601">Top</translation>
<translation id="2701330563083355633">Gedeel vanaf <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nuut</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Maak vouer oop</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 jaar gelede}other{# jaar gelede}}</translation>
+<translation id="2878511608894704031">Vee alles uit</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{oor 1 d.}other{oor # d.}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Wys tans voorstelle</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Begin praat</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> is geskuif na vouer <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLE PROGRAMME</translation>
+<translation id="4491109536499578614">Prent</translation>
<translation id="4588090240171750605">Rollees na regs</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, stergradering <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, programaanbeveling</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dag en }other{# dae en }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{oor 1 j.}other{oor # j.}}</translation>
+<translation id="588258955323874662">Volskerm</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekonde oor}other{# sekondes oor}}</translation>
<translation id="5941711191222866238">Maak kleiner</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 uur}other{# uur}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Blokkeer alle kennisgewings van hierdie program af</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 sekonde gelede}other{# sekondes gelede}}</translation>
+<translation id="6503257047630241175">RTF-inhoud</translation>
<translation id="6539092367496845964">Iets is fout. Probeer later weer.</translation>
<translation id="654149438358937226">Blokkeer alle kennisgewings</translation>
<translation id="6567071839949112727">klik voorsaat</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_am.xtb b/chromium/ui/strings/translations/ui_strings_am.xtb
index f6f6635df42..6fb8b55411d 100644
--- a/chromium/ui/strings/translations/ui_strings_am.xtb
+++ b/chromium/ui/strings/translations/ui_strings_am.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ምረጥ</translation>
<translation id="1859234291848436338">የአፃፃፍ አቅጣጫ</translation>
<translation id="1860796786778352021">ማሳወቂያ ዝጋ</translation>
+<translation id="186476001994626254">የድር ዘመናዊ ለጥፍ ይዘት</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;ሁሉንም ምረጥ</translation>
+<translation id="19085484004813472">ይህ አዲስ ባህሪ ነው</translation>
<translation id="2006524834898217237">ይህ መሣሪያ ወደ በይነ መረብ እንደተገናኘ እርግጠኛ ይሁኑ።</translation>
<translation id="208586643495776849">እባክዎ እንደገና ይሞክሩ</translation>
<translation id="2090963878406559571">አንድ ቁጥር ከ<ph name="ORIGIN" /> ወደ የእርስዎ Android ስልክ ለመላክ በቅንብሮች ውስጥ ለሁሉም መሣሪያዎች <ph name="TROUBLESHOOT_LINK" />።</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ቅንብሮችን ያቀናብሩ</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{በ 1ሰ ውስጥ}one{በ #ሰ ውስጥ}other{በ #ሰ ውስጥ}}</translation>
<translation id="2583543531130364912">የእርስዎን ማያንካ ልኬት ያስተካክሉ</translation>
+<translation id="2586657967955657006">የቅንጥብ ሰሌዳ</translation>
<translation id="2666092431469916601">ላይ</translation>
<translation id="2701330563083355633">በ <ph name="DEVICE_NAME" /> የተጋራ</translation>
<translation id="271033894570825754">አዲስ</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />፣ <ph name="PRICE" /></translation>
<translation id="2803313416453193357">አቃፊ ክፈት</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{ከ1 ዓመት በፊት}one{ከ# ዓመቶች በፊት}other{ከ# ዓመቶች በፊት}}</translation>
+<translation id="2878511608894704031">ሁሉንም ሰርዝ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{በ 1ቀ ውስጥ}one{በ #ቀ ውስጥ}other{በ #ቀ ውስጥ}}</translation>
<translation id="2931838996092594335">ጠቅ ያድርጉ</translation>
<translation id="2981684127883932071">ጥቆማ ሐሳቦችን በማሳየት ላይ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">መናገር ይጀምሩ</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ወደ <ph name="FOLDER_NAME" /> አቃፊ ተንቀሳቅሷል።</translation>
<translation id="4316910396681052118">ሁሉም መተግበሪያዎች</translation>
+<translation id="4491109536499578614">ምስል</translation>
<translation id="4588090240171750605">ወደ ቀኝ ሸብልል</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />፣ ኮከብ ደረጃ አሰጣጥ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />፣ የመተግበሪያ ምክር</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ቀን እና }one{# ቀኖች እና }other{# ቀኖች እና }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{በ 1ዓ ውስጥ}one{በ #ዓ ውስጥ}other{በ #ዓ ውስጥ}}</translation>
+<translation id="588258955323874662">ሙሉ ገጽ ዕይታ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 ሰከንድ ቀርቷል}one{# ሰከንዶች ቀርቷል}other{# ሰከንዶች ቀርቷል}}</translation>
<translation id="5941711191222866238">አሳንስ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ሰዓት}one{# ሰዓቶች}other{# ሰዓቶች}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google ረዳት</translation>
<translation id="6430678249303439055">ከዚህ መተግበሪያ የሚመጡ ሁሉንም ማሳወቂያዎች አግድ</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{ከ1 ሰከንድ በፊት}one{ከ# ሰከንዶች በፊት}other{ከ# ሰከንዶች በፊት}}</translation>
+<translation id="6503257047630241175">የአርቲኤፍ ይዘት</translation>
<translation id="6539092367496845964">የሆነ ችግር ተፈጥሯል። ቆይተው እንደገና ይሞክሩ።</translation>
<translation id="654149438358937226">ሁሉንም ማሳወቂያዎች አግድ</translation>
<translation id="6567071839949112727">ዘር ማንዘርን ጠቅ አድርግ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ar.xtb b/chromium/ui/strings/translations/ui_strings_ar.xtb
index 7e276400199..0e123ee2308 100644
--- a/chromium/ui/strings/translations/ui_strings_ar.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ar.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">الاختيار</translation>
<translation id="1859234291848436338">اتجاه الكتابة</translation>
<translation id="1860796786778352021">إغلاق الإشعار</translation>
+<translation id="186476001994626254">‏محتوى Web Smart Paste</translation>
<translation id="1871244248791675517">‏مفتاح Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">تح&amp;ديد الكلّ</translation>
+<translation id="19085484004813472">هذه ميزة جديدة</translation>
<translation id="2006524834898217237">عليك التأكّد من اتصال الجهاز بالإنترنت.</translation>
<translation id="208586643495776849">يُرجى إعادة المحاولة</translation>
<translation id="2090963878406559571">‏لإرسال رقم من <ph name="ORIGIN" /> إلى هاتفك الذي يعمل بنظام التشغيل Android، يُرجى <ph name="TROUBLESHOOT_LINK" /> لكلا الجهازَين في الإعدادات.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">إدارة الإعدادات</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{خلال ساعة واحدة}zero{خلال # ساعة}two{خلال ساعتين (#)}few{خلال # ساعات}many{خلال # ساعة}other{خلال # ساعة}}</translation>
<translation id="2583543531130364912">معايرة الشاشة التي تعمل باللمس</translation>
+<translation id="2586657967955657006">الحافظة</translation>
<translation id="2666092431469916601">أعلى</translation>
<translation id="2701330563083355633">تمت المشاركة من <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">جديدة</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925">التطبيق <ph name="APP_NAME_INFO" />، <ph name="PRICE" /></translation>
<translation id="2803313416453193357">فتح المجلد</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{قبل عام واحد}zero{قبل # عام}two{قبل عامين (#)}few{قبل # أعوام}many{قبل # عامًا}other{قبل # عام}}</translation>
+<translation id="2878511608894704031">حذف الكل</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{خلال يوم واحد}zero{خلال # يوم}two{خلال يومين (#)}few{خلال # أيام}many{خلال # يومًا}other{خلال # يوم}}</translation>
<translation id="2931838996092594335">النقر</translation>
<translation id="2981684127883932071">عرض الاقتراحات</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">بدء التحدث</translation>
<translation id="430191667033048642">تم نقل <ph name="MOVED_APP_NAME" /> إلى المجلد <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">جميع التطبيقات</translation>
+<translation id="4491109536499578614">صورة</translation>
<translation id="4588090240171750605">التمرير إلى اليسار</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />، التقييم بالنجوم <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />، اقتراح تطبيق</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{يوم واحد و }zero{# من الأيام و }two{يومان (#) و }few{# أيام و }many{# يومًا و }other{# من الأيام و }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{خلال عام واحد}zero{خلال # عام}two{خلال عامين (#)}few{خلال # أعوام}many{خلال # عامًا}other{خلال # عام}}</translation>
+<translation id="588258955323874662">ملء الشاشة</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{يتبقى ثانية واحدة}zero{يتبقى # من الثواني}two{يتبقى ثانيتان (#)}few{يتبقى # ثانية}many{يتبقى # ثانية}other{يتبقى # من الثواني}}</translation>
<translation id="5941711191222866238">تصغير</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{ساعة واحدة}zero{# من الساعات}two{ساعتان (#)}few{# ساعات}many{# ساعةً}other{# من الساعات}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">‏مساعد Google</translation>
<translation id="6430678249303439055">حظر جميع الإشعارات الواردة من هذا التطبيق</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{قبل ثانية واحدة}zero{قبل # ثانية}two{قبل ثانيتين (#)}few{قبل # ثوانٍ}many{قبل # ثانية}other{قبل # ثانية}}</translation>
+<translation id="6503257047630241175">‏محتوى RTF</translation>
<translation id="6539092367496845964">حدث خطأ. يُرجى إعادة المحاولة لاحقًا.</translation>
<translation id="654149438358937226">حظر جميع الإشعارات</translation>
<translation id="6567071839949112727">النقر على العنصر الأصل</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_as.xtb b/chromium/ui/strings/translations/ui_strings_as.xtb
index 1415e565576..4a6b243dc1f 100644
--- a/chromium/ui/strings/translations/ui_strings_as.xtb
+++ b/chromium/ui/strings/translations/ui_strings_as.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">বাছনি কৰক</translation>
<translation id="1859234291848436338">লিখাৰ নির্দেশনা</translation>
<translation id="1860796786778352021">জাননী বন্ধ কৰক</translation>
+<translation id="186476001994626254">ৱেব স্মাৰ্ট পে’ষ্ট সমল</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;সকলো বাছনি কৰক</translation>
+<translation id="19085484004813472">এইটো এটা নতুন সুবিধা</translation>
<translation id="2006524834898217237">নিশ্চিত হৈ লওক যে এই ডিভাইচটো ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ আছে।</translation>
<translation id="208586643495776849">অনুগ্ৰহ কৰি পুনৰ চেষ্টা কৰক</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" />ৰ পৰা আপোনাৰ Android ফ’নলৈ এটা নম্বৰ পঠিয়াবলৈ দুয়োটা ডিভাইচৰ ছেটিংসমূহত <ph name="TROUBLESHOOT_LINK" />।</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ছেটিংসমূহ পৰিচালনা কৰক</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{১ ঘণ্টাত}one{# ঘণ্টাত}other{# ঘণ্টাত}}</translation>
<translation id="2583543531130364912">আপোনাৰ টাচ্চ স্ক্ৰীণ কেলিব্ৰেট কৰক</translation>
+<translation id="2586657967955657006">ক্লিপব'ৰ্ড</translation>
<translation id="2666092431469916601">শীর্ষ</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" />ৰ পৰা শ্বেয়াৰ কৰা হৈছে</translation>
<translation id="271033894570825754">নতুন</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ফ'ল্ডাৰ খোলক</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{১ বছৰ পূর্বে}one{# বছৰ পূর্বে}other{# বছৰ পূর্বে}}</translation>
+<translation id="2878511608894704031">সকলো মচক</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{১ দিনত}one{# দিনত}other{# দিনত}}</translation>
<translation id="2931838996092594335">ক্লিক কৰক</translation>
<translation id="2981684127883932071">পৰামৰ্শ দেখুৱাই থকা হৈছে</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">কথা কওক</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" />ক <ph name="FOLDER_NAME" /> ফ’ল্ডাৰলৈ স্থানান্তৰ কৰা হ’ল।</translation>
<translation id="4316910396681052118">সকলো এপ্</translation>
+<translation id="4491109536499578614">প্ৰতিচ্ছবি</translation>
<translation id="4588090240171750605">সোঁফাললৈ স্ক্ৰ’ল কৰক</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, তৰাযুক্ত মূল্যাংকন <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, এপৰ চুপাৰিছ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{১দিন আৰু }one{# দিন আৰু }other{# দিন আৰু }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{১ বছৰত}one{# বছৰত}other{# বছৰত}}</translation>
+<translation id="588258955323874662">পূৰ্ণস্ক্ৰীণ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{১ ছেকেণ্ড বাকী আছে}one{# ছেকেণ্ড বাকী আছে}other{# ছেকেণ্ড বাকী আছে}}</translation>
<translation id="5941711191222866238">সৰু কৰক</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}</translation>
@@ -169,6 +175,7 @@
<translation id="6404817160109697034">{SECONDS,plural, =1{১ ছেকেণ্ড পূর্বে}one{# ছেকেণ্ড পূর্বে}other{# ছেকেণ্ড পূর্বে}}</translation>
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">এই এপ্‌‌টোৰ পৰা সকলো জাননী অৱৰোধ কৰক</translation>
+<translation id="6503257047630241175">RTF সমল</translation>
<translation id="6539092367496845964">কিবা ভুল হ’ল। পাছত পুনৰ চেষ্টা কৰক।</translation>
<translation id="654149438358937226">সকলো জাননী অৱৰোধ কৰক</translation>
<translation id="6567071839949112727">এনচেষ্টৰ-ত ক্লিক কৰক</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_az.xtb b/chromium/ui/strings/translations/ui_strings_az.xtb
index 6d7dbb99723..42f229d1aac 100644
--- a/chromium/ui/strings/translations/ui_strings_az.xtb
+++ b/chromium/ui/strings/translations/ui_strings_az.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seçin</translation>
<translation id="1859234291848436338">Yazı istiqaməti</translation>
<translation id="1860796786778352021">Bildiriş qapatma</translation>
+<translation id="186476001994626254">Veb Ağıllı Əlavə Etmə Məzmunu</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Hamısını seçin</translation>
+<translation id="19085484004813472">Bu, yeni funksiyadır</translation>
<translation id="2006524834898217237">Bu cihazın internetə qoşulduğuna əmin olun.</translation>
<translation id="208586643495776849">Yenidən cəhd edin</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> cihazından Android telefonunuza nömrə göndərmək üçün ayarlarda hər iki cihaz üçün <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Ayarları idarə edin</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 saatda}other{# saatda}}</translation>
<translation id="2583543531130364912">Sensor ekranı kalibrləyin</translation>
+<translation id="2586657967955657006">Mübadilə buferi</translation>
<translation id="2666092431469916601">Yuxarı</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> adlı cihazdan paylaşıldı</translation>
<translation id="271033894570825754">Yeni</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Qovluğu açın</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 il əvvəl}other{# il əvvəl}}</translation>
+<translation id="2878511608894704031">Hamısını Silin</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 gündə}other{# gündə}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Təkliflərin göstərilməsi</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Danışmağa başlayın</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> <ph name="FOLDER_NAME" /> qovluğuna köçürülüb.</translation>
<translation id="4316910396681052118">BÜTÜN TƏTBİQLƏR</translation>
+<translation id="4491109536499578614">Şəkil</translation>
<translation id="4588090240171750605">Sağa Sürüşdürün</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Ulduz reytinqi <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Tətbiq tövsiyəsi</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 gün və }other{# gün }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 ildə}other{# ildə}}</translation>
+<translation id="588258955323874662">Tam ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 saniyə qalıb}other{# saniyə qalıb}}</translation>
<translation id="5941711191222866238">Minimallaşdırın</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 saat}other{# saat}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Bu tətbiqin bütün bildirişlərini blok edin</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 saniyə əvvəl}other{# saniyə əvvəl}}</translation>
+<translation id="6503257047630241175">RTF Məzmunu</translation>
<translation id="6539092367496845964">Xəta baş verdi. Sonra cəhd edin.</translation>
<translation id="654149438358937226">Bütün bildirişləri blok edin</translation>
<translation id="6567071839949112727">əvvəlki versiyaya klikləyin</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_be.xtb b/chromium/ui/strings/translations/ui_strings_be.xtb
index 467e6d4ac72..d53565a04dc 100644
--- a/chromium/ui/strings/translations/ui_strings_be.xtb
+++ b/chromium/ui/strings/translations/ui_strings_be.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">выбраць</translation>
<translation id="1859234291848436338">Напрамак пісьма</translation>
<translation id="1860796786778352021">Закрыць апавяшчэнне</translation>
+<translation id="186476001994626254">Змесціва разумнай вэб-устаўкі</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Выбраць &amp;усё</translation>
+<translation id="19085484004813472">Гэта новая функцыя</translation>
<translation id="2006524834898217237">Праверце, ці падключана прылада да інтэрнэту.</translation>
<translation id="208586643495776849">Паўтарыце спробу</translation>
<translation id="2090963878406559571">Каб адпраўляць нумары з <ph name="ORIGIN" /> на свой тэлефон Android, <ph name="TROUBLESHOOT_LINK" /> для абедзвюх прылад у наладах.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Кіраваць наладамі</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{праз 1 гадз}one{праз # гадз}few{праз # гадз}many{праз # гадз}other{праз # гадз}}</translation>
<translation id="2583543531130364912">Адкалібруйце сэнсарны экран</translation>
+<translation id="2586657967955657006">Буфер абмену</translation>
<translation id="2666092431469916601">Зверху</translation>
<translation id="2701330563083355633">Абагулена з прылады <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Новае</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Адкрыць папку</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 год таму}one{# год таму}few{# гады таму}many{# гадоў таму}other{# года таму}}</translation>
+<translation id="2878511608894704031">Выдаліць усё</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{праз 1 сут}one{праз # сут}few{праз # сут}many{праз # сут}other{праз # сут}}</translation>
<translation id="2931838996092594335">націснуць кнопку мышы</translation>
<translation id="2981684127883932071">Паказваюцца прапановы</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Пачаць галасавы ўвод</translation>
<translation id="430191667033048642">Праграма <ph name="MOVED_APP_NAME" /> перамешчана ў папку <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">УСЕ ПРАГРАМЫ</translation>
+<translation id="4491109536499578614">Відарыс</translation>
<translation id="4588090240171750605">Прагартаць управа</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ацэнка ў зорках: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485">Рэкамендаваная праграма (<ph name="APP_NAME" />)</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 дзень і }one{# дзень і }few{# дні і }many{# дзён і }other{# дня і }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{праз 1 г.}one{праз # г.}few{праз # г.}many{праз # г.}other{праз # г.}}</translation>
+<translation id="588258955323874662">Поўнаэкранны рэжым</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Засталася 1 секунда}one{Засталася # секунда}few{Засталося # секунды}many{Засталося # секунд}other{Засталося # секунды}}</translation>
<translation id="5941711191222866238">Згарнуць</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Памочнік Google</translation>
<translation id="6430678249303439055">Блакіраваць усе апавяшчэнні гэтай праграмы</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 секунду таму}one{# секунду таму}few{# секунды таму}many{# секунд таму}other{# секунды таму}}</translation>
+<translation id="6503257047630241175">Змесціва ў фармаце RTF</translation>
<translation id="6539092367496845964">Нешта пайшло не так. Паўтарыце спробу пазней.</translation>
<translation id="654149438358937226">Блакіраваць усе апавяшчэнні</translation>
<translation id="6567071839949112727">націснуць на бацькоўскі элемент</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_bg.xtb b/chromium/ui/strings/translations/ui_strings_bg.xtb
index d70a6ba77b2..0ebfae40d62 100644
--- a/chromium/ui/strings/translations/ui_strings_bg.xtb
+++ b/chromium/ui/strings/translations/ui_strings_bg.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">Избиране</translation>
<translation id="1859234291848436338">Посока на писане</translation>
<translation id="1860796786778352021">Затваряне на известието</translation>
+<translation id="186476001994626254">Уеб съдържание с възможност за интелигентно поставяне</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">&amp;Избиране на всички</translation>
+<translation id="19085484004813472">Това е нова функция</translation>
<translation id="2006524834898217237">Проверете дали устройството е свързано с интернет.</translation>
<translation id="208586643495776849">Моля, опитайте отново</translation>
<translation id="2090963878406559571">За да изпратите номер от <ph name="ORIGIN" /> до телефона си с Android, <ph name="TROUBLESHOOT_LINK" /> в настройките и на двете устройства.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Управление на настройките</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{след 1 ч}other{след # ч}}</translation>
<translation id="2583543531130364912">Калибриране на сензорния екран</translation>
+<translation id="2586657967955657006">Буферна памет</translation>
<translation id="2666092431469916601">Най-горе</translation>
<translation id="2701330563083355633">Споделено от <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Създаване</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Отваряне на папката</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Преди 1 година}other{Преди # години}}</translation>
+<translation id="2878511608894704031">Изтриване на всички</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{след 1 д}other{след # д}}</translation>
<translation id="2931838996092594335">кликвам</translation>
<translation id="2981684127883932071">Показани са предложения</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642">Приложението <ph name="MOVED_APP_NAME" /> бе преместено в папката <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ВСИЧКИ ПРИЛОЖЕНИЯ</translation>
+<translation id="4491109536499578614">Изображение</translation>
<translation id="4588090240171750605">Превъртане надясно</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, оценка със звезди: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, препоръка за приложение</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ден и }other{# дни и }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{след 1 г.}other{след # г.}}</translation>
+<translation id="588258955323874662">Цял екран</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Остава 1 секунда}other{Остават # секунди}}</translation>
<translation id="5941711191222866238">Намаляване</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 час}other{# часа}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Асистент</translation>
<translation id="6430678249303439055">Блокиране на всички известия от това приложение</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Преди 1 секунда}other{Преди # секунди}}</translation>
+<translation id="6503257047630241175">Съдържание във формат RTF</translation>
<translation id="6539092367496845964">Нещо се обърка. Опитайте пак по-късно.</translation>
<translation id="654149438358937226">Блокиране на всички известия</translation>
<translation id="6567071839949112727">кликване върху елемента предшественик</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Запазване на файл</translation>
<translation id="6656912866303152668">Проверете дали синхронизацията в Chrome е включена за <ph name="TARGET_DEVICE_NAME" /> и опитайте да изпратите отново.</translation>
<translation id="6699343763173986273">Мултимедия, следващият запис</translation>
-<translation id="6710213216561001401">Предишна</translation>
+<translation id="6710213216561001401">Предишно</translation>
<translation id="6779314412797872738">За да изпратите номер оттук до телефона си с Android, <ph name="TROUBLESHOOT_LINK" /> в настройките и на двете устройства.</translation>
<translation id="6786750046913594791">Затваряне на папката</translation>
<translation id="6808150112686056157">Мултимедия, стоп</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_bn.xtb b/chromium/ui/strings/translations/ui_strings_bn.xtb
index d906dea96bb..e9a3026c4f0 100644
--- a/chromium/ui/strings/translations/ui_strings_bn.xtb
+++ b/chromium/ui/strings/translations/ui_strings_bn.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">বেছে নিন</translation>
<translation id="1859234291848436338">লিখন নির্দেশনা</translation>
<translation id="1860796786778352021">বিজ্ঞপ্তি বন্ধ করা হয়েছে</translation>
+<translation id="186476001994626254">ওয়েব স্মার্ট পেস্ট কন্টেন্ট</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;সকল বেছে নিন</translation>
+<translation id="19085484004813472">এটি একটি নতুন ফিচার</translation>
<translation id="2006524834898217237">এই ডিভাইস ইন্টারনেটে কানেক্ট করা আছে কিনা দেখে নিন।</translation>
<translation id="208586643495776849">আবার চেষ্টা করুন</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> থেকে আপনার Android ফোনে কোনও নম্বর পাঠাতে, সেটিংসে গিয়ে দুটি ডিভাইসের জন্য <ph name="TROUBLESHOOT_LINK" />।</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">সেটিংস ম্যানেজ করুন</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{১ ঘণ্টার মধ্যে}one{# ঘণ্টার মধ্যে}other{# ঘণ্টার মধ্যে}}</translation>
<translation id="2583543531130364912">আপনার টাচস্ক্রীন ক্যালিব্রেট করুন</translation>
+<translation id="2586657967955657006">ক্লিপবোর্ড</translation>
<translation id="2666092431469916601">শীর্ষ</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> থেকে শেয়ার করা হয়েছে</translation>
<translation id="271033894570825754">নতুন</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ফোল্ডার খুলুন</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{১ বছর আগে}one{# বছর আগে}other{# বছর আগে}}</translation>
+<translation id="2878511608894704031">সব মুছুন</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{১ দিনের মধ্যে}one{# দিনের মধ্যে}other{# দিনের মধ্যে}}</translation>
<translation id="2931838996092594335">ক্লিক করুন</translation>
<translation id="2981684127883932071">সাজেশন দেখানো হচ্ছে</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">কথা বলা শুরু করুন</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> অ্যাপ <ph name="FOLDER_NAME" /> ফোল্ডারে সরানো হয়েছে।</translation>
<translation id="4316910396681052118">সব অ্যাপ</translation>
+<translation id="4491109536499578614">ছবি</translation>
<translation id="4588090240171750605">ডান দিকে স্ক্রল করুন</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> স্টার রেটিং</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, অ্যাপের সাজেশন</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{১ দিন এবং }one{# দিন এবং }other{# দিন এবং }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{১ বছরের মধ্যে}one{# বছরের মধ্যে}other{# বছরের মধ্যে}}</translation>
+<translation id="588258955323874662">সম্পূর্নস্ক্রীণ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{১ সেকেন্ড বাকি}one{# সেকেন্ড বাকি}other{# সেকেন্ড বাকি}}</translation>
<translation id="5941711191222866238">ছোট করুন</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{১ ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">এই অ্যাপ থেকে সমস্ত বিজ্ঞপ্তি ব্লক করুন</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{১ সেকেন্ড আগে}one{# সেকেন্ড আগে}other{# সেকেন্ড আগে}}</translation>
+<translation id="6503257047630241175">RTF কন্টেন্ট</translation>
<translation id="6539092367496845964">কোনও সমস্যা হয়েছে। পরে আবার চেষ্টা করুন।</translation>
<translation id="654149438358937226">সমস্ত বিজ্ঞপ্তি ব্লক করুন</translation>
<translation id="6567071839949112727">এটি সম্পর্কিত একটি পুরানো আইটেমে ক্লিক করুন</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">ফাইল সেভ করুন</translation>
<translation id="6656912866303152668"><ph name="TARGET_DEVICE_NAME" /> Chrome-এ সিঙ্ক করার ফিচার চালু করে রেখেছে কিনা দেখে নিন।</translation>
<translation id="6699343763173986273">মিডিয়া পরবর্তী ট্র্যাক</translation>
-<translation id="6710213216561001401">পূর্ববর্তী</translation>
+<translation id="6710213216561001401">আগের</translation>
<translation id="6779314412797872738">এখান থেকে আপনার Android ফোনে কোনও নম্বর পাঠাতে, সেটিংসে গিয়ে দুটি ডিভাইসের জন্য <ph name="TROUBLESHOOT_LINK" />।</translation>
<translation id="6786750046913594791">ফোল্ডার বন্ধ করুন</translation>
<translation id="6808150112686056157">মিডিয়া থামান</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_bs.xtb b/chromium/ui/strings/translations/ui_strings_bs.xtb
index 9326f90bada..6b906c6e553 100644
--- a/chromium/ui/strings/translations/ui_strings_bs.xtb
+++ b/chromium/ui/strings/translations/ui_strings_bs.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">odaberi</translation>
<translation id="1859234291848436338">Smjer pisanja</translation>
<translation id="1860796786778352021">Zatvaranje obavještenja</translation>
+<translation id="186476001994626254">Web sadržaj za pametno lijepljenje</translation>
<translation id="1871244248791675517">Umet</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Odaberi &amp;sve</translation>
+<translation id="19085484004813472">Ovo je nova funkcija</translation>
<translation id="2006524834898217237">Provjerite je li ovaj uređaj povezan na internet.</translation>
<translation id="208586643495776849">Pokušajte ponovo</translation>
<translation id="2090963878406559571">Da pošaljete broj s web lokacije <ph name="ORIGIN" /> na Android telefon, <ph name="TROUBLESHOOT_LINK" /> za oba uređaja u postavkama.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Upravljajte postavkama</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{za 1h}one{za # h}few{za # h}other{za # h}}</translation>
<translation id="2583543531130364912">Kalibrirajte dodirni ekran</translation>
+<translation id="2586657967955657006">Međumemorija</translation>
<translation id="2666092431469916601">Gornji</translation>
<translation id="2701330563083355633">Dijeljeno s uređaja: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otvori folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Prije godinu dana}one{Prije # godinu}few{Prije # godine}other{Prije # godina}}</translation>
+<translation id="2878511608894704031">Izbriši sve</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{Za 1 d}one{Za # d}few{Za # d}other{Za # d}}</translation>
<translation id="2931838996092594335">kliknuti</translation>
<translation id="2981684127883932071">Prikaz prijedloga</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Započinjanje govora</translation>
<translation id="430191667033048642">Aplikacija <ph name="MOVED_APP_NAME" /> je premještena u folder <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">SVE APLIKACIJE</translation>
+<translation id="4491109536499578614">Slika</translation>
<translation id="4588090240171750605">Kliznite udesno</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ocjena zvjezdicama <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, preporučena aplikacija</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dan i }one{# dan i }few{# dana i }other{# dan i }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{za 1 g}one{za # g}few{za # g}other{za # g}}</translation>
+<translation id="588258955323874662">Cijeli ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Još 1 sekunda}one{Još # sekunda}few{Još # sekunde}other{Još # sekundi}}</translation>
<translation id="5941711191222866238">Minimiziraj</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Asistent</translation>
<translation id="6430678249303439055">Blokiraj sva obavještenja iz ove aplikacije</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Prije 1 sekundu}one{Prije # sekundu}few{Prije # sekunde}other{Prije # sekundi}}</translation>
+<translation id="6503257047630241175">RTF sadržaj</translation>
<translation id="6539092367496845964">Nešto nije uredu. Pokušajte ponovo kasnije.</translation>
<translation id="654149438358937226">Blokiraj sve obavijesti</translation>
<translation id="6567071839949112727">kliknite na prethodnika</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ca.xtb b/chromium/ui/strings/translations/ui_strings_ca.xtb
index 45b4857abff..7390f4fd816 100644
--- a/chromium/ui/strings/translations/ui_strings_ca.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ca.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">selecciona</translation>
<translation id="1859234291848436338">Direcció de l'escriptura</translation>
<translation id="1860796786778352021">Tanca la notificació</translation>
+<translation id="186476001994626254">Contingut d'enganxada intel·ligent des del web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Selecciona-ho &amp;tot</translation>
+<translation id="19085484004813472">Aquesta funció és nova</translation>
<translation id="2006524834898217237">Comprova que el dispositiu estigui connectat a Internet.</translation>
<translation id="208586643495776849">Torna-ho a provar</translation>
<translation id="2090963878406559571">Per enviar un número des del lloc web <ph name="ORIGIN" /> al telèfon Android, <ph name="TROUBLESHOOT_LINK" /> a la configuració dels dos dispositius.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gestiona la configuració</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{d'aquí a 1 h}other{d'aquí a # h}}</translation>
<translation id="2583543531130364912">Calibra la pantalla tàctil</translation>
+<translation id="2586657967955657006">Porta-retalls</translation>
<translation id="2666092431469916601">Superior</translation>
<translation id="2701330563083355633">S'ha compartit des del dispositiu <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nou</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Obre la carpeta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Fa 1 any}other{Fa # anys}}</translation>
+<translation id="2878511608894704031">Suprimeix-ho tot</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{d'aquí a 1 d}other{d'aquí a # d}}</translation>
<translation id="2931838996092594335">fer clic</translation>
<translation id="2981684127883932071">S'estan mostrant suggeriments</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Comença a parlar</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> s'ha mogut a la carpeta <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TOTES LES APLICACIONS</translation>
+<translation id="4491109536499578614">Imatge</translation>
<translation id="4588090240171750605">Desplaçament a la dreta</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />; puntuació en estrelles: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recomanació d'aplicació</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dia i }other{# dies i }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{d'aquí a 1 a}other{d'aquí a # a}}</translation>
+<translation id="588258955323874662">Pantalla completa</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 segon restant}other{# segons restants}}</translation>
<translation id="5941711191222866238">Minimitza</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hora}other{# hores}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Assistent de Google</translation>
<translation id="6430678249303439055">Bloqueja totes les notificacions d'aquesta aplicació</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Fa 1 segon}other{Fa # segons}}</translation>
+<translation id="6503257047630241175">Contingut en RTF</translation>
<translation id="6539092367496845964">S'ha produït un error. Torna-ho a provar més tard.</translation>
<translation id="654149438358937226">Bloqueja totes les notificacions</translation>
<translation id="6567071839949112727">fes clic a l'antecedent</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_cs.xtb b/chromium/ui/strings/translations/ui_strings_cs.xtb
index f4e3da81a7c..0518203a560 100644
--- a/chromium/ui/strings/translations/ui_strings_cs.xtb
+++ b/chromium/ui/strings/translations/ui_strings_cs.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">zvolit</translation>
<translation id="1859234291848436338">Směr textu</translation>
<translation id="1860796786778352021">Zavřít oznámení</translation>
+<translation id="186476001994626254">Obsah Web Smart Paste</translation>
<translation id="1871244248791675517">Klávesa Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;Vybrat vše</translation>
+<translation id="19085484004813472">Toto je nová funkce</translation>
<translation id="2006524834898217237">Zkontrolujte, zda je toto zařízení připojeno k internetu.</translation>
<translation id="208586643495776849">Zkuste to znovu</translation>
<translation id="2090963878406559571">Chcete-li poslat číslo z webu <ph name="ORIGIN" /> na svůj telefon Android, v nastavení obou zařízení <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Spravovat nastavení</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{za 1 h}few{za # h}many{za # h}other{za # h}}</translation>
<translation id="2583543531130364912">Zkalibrujte dotykovou obrazovku</translation>
+<translation id="2586657967955657006">Schránka</translation>
<translation id="2666092431469916601">Nahoru</translation>
<translation id="2701330563083355633">Sdíleno ze zařízení <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nové</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otevřít složku</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{před 1 rokem}few{před # lety}many{před # roku}other{před # lety}}</translation>
+<translation id="2878511608894704031">Smazat vše</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{za 1 d}few{za # d}many{za # d}other{za # d}}</translation>
<translation id="2931838996092594335">kliknout</translation>
<translation id="2981684127883932071">Jsou zobrazeny návrhy</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Začít mluvit</translation>
<translation id="430191667033048642">Aplikace <ph name="MOVED_APP_NAME" /> byla přesunuta do složky <ph name="FOLDER_NAME" /></translation>
<translation id="4316910396681052118">VŠECHNY APLIKACE</translation>
+<translation id="4491109536499578614">Obrázek</translation>
<translation id="4588090240171750605">Posuv doprava</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, hodnocení hvězdičkami <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, doporučení aplikace</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 den a }few{# dny a }many{# dne a }other{# dnů a }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{za 1 r}few{za # r}many{za # r}other{za # r}}</translation>
+<translation id="588258955323874662">Celá obrazovka</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Zbývá 1 sekunda}few{Zbývají # sekundy}many{Zbývá # sekundy}other{Zbývá # sekund}}</translation>
<translation id="5941711191222866238">Minimalizovat</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodin}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistent Google</translation>
<translation id="6430678249303439055">Blokovat všechna oznámení z této aplikace</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{před sekundou}few{před # sekundami}many{před # sekundy}other{před # sekundami}}</translation>
+<translation id="6503257047630241175">Obsah RTF</translation>
<translation id="6539092367496845964">Něco se pokazilo. Zkuste to později.</translation>
<translation id="654149438358937226">Blokovat všechna oznámení</translation>
<translation id="6567071839949112727">kliknutí na nadřazený prvek</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_da.xtb b/chromium/ui/strings/translations/ui_strings_da.xtb
index 5d947bb43a0..ad15dd24d58 100644
--- a/chromium/ui/strings/translations/ui_strings_da.xtb
+++ b/chromium/ui/strings/translations/ui_strings_da.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">vælg</translation>
<translation id="1859234291848436338">Skriveretning</translation>
<translation id="1860796786778352021">Luk notifikation</translation>
+<translation id="186476001994626254">Web Smart Paste-indhold</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Vælg &amp;alle</translation>
+<translation id="19085484004813472">Dette er en ny funktion</translation>
<translation id="2006524834898217237">Sørg for, at denne enhed har forbindelse til internettet.</translation>
<translation id="208586643495776849">Prøv igen</translation>
<translation id="2090963878406559571"><ph name="TROUBLESHOOT_LINK" /> for begge enheder i indstillingerne, hvis du vil sende et nummer fra <ph name="ORIGIN" /> til din Android-telefon.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Administrer indstillinger</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{om 1 t.}one{om # t.}other{om # t.}}</translation>
<translation id="2583543531130364912">Kalibrer din touchskærm</translation>
+<translation id="2586657967955657006">Udklipsholder</translation>
<translation id="2666092431469916601">Top</translation>
<translation id="2701330563083355633">Delt fra <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nyt</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Åbn mappe</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{For 1 år siden}one{For # år siden}other{For # år siden}}</translation>
+<translation id="2878511608894704031">Slet alt</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{om 1 dag}one{om # dag}other{om # dage}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Viser forslag</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start indtaling</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> blev flyttet til mappen <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLE APPS</translation>
+<translation id="4491109536499578614">Billede</translation>
<translation id="4588090240171750605">Scroll til højre</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, antal stjerner <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, anbefalet app</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dag og }one{# dage og }other{# dage og }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{om 1 år}one{om # år}other{om # år}}</translation>
+<translation id="588258955323874662">Fuld skærm</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekund tilbage}one{# sekunder tilbage}other{# sekunder tilbage}}</translation>
<translation id="5941711191222866238">Minimer</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 time}one{# timer}other{# timer}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Bloker alle notifikationer fra denne app</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{For 1 sekund siden}one{For # sekund siden}other{For # sekunder siden}}</translation>
+<translation id="6503257047630241175">RTF-indhold</translation>
<translation id="6539092367496845964">Der opstod en fejl. Prøv igen senere.</translation>
<translation id="654149438358937226">Bloker alle notifikationer</translation>
<translation id="6567071839949112727">klik på overordnet element</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_de.xtb b/chromium/ui/strings/translations/ui_strings_de.xtb
index d478a9a902d..3d1ac7b0fcb 100644
--- a/chromium/ui/strings/translations/ui_strings_de.xtb
+++ b/chromium/ui/strings/translations/ui_strings_de.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">auswählen</translation>
<translation id="1859234291848436338">Schreibrichtung</translation>
<translation id="1860796786778352021">Benachrichtigung schließen</translation>
+<translation id="186476001994626254">Web Smart Paste-Inhalt</translation>
<translation id="1871244248791675517">Einfg</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;Alles auswählen</translation>
+<translation id="19085484004813472">Dies ist eine neue Funktion</translation>
<translation id="2006524834898217237">Prüfen Sie, ob das Gerät mit dem Internet verbunden ist.</translation>
<translation id="208586643495776849">Versuchen Sie es bitte noch einmal</translation>
<translation id="2090963878406559571">Wenn Sie eine Nummer von <ph name="ORIGIN" /> an Ihr Android-Smartphone senden möchten, müssen Sie für beide Geräte in den Einstellungen die <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Einstellungen verwalten</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{in 1 h}other{in # h}}</translation>
<translation id="2583543531130364912">Touchscreen kalibrieren</translation>
+<translation id="2586657967955657006">Zwischenablage</translation>
<translation id="2666092431469916601">Oben</translation>
<translation id="2701330563083355633">Geteilt von <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Neu</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Ordner öffnen</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{vor 1 Jahr}other{vor # Jahren}}</translation>
+<translation id="2878511608894704031">Alle löschen</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{in 1 T.}other{in # T.}}</translation>
<translation id="2931838996092594335">Klicken</translation>
<translation id="2981684127883932071">Vorschläge werden angezeigt</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Sprachausgabe starten</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> verschoben in <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLE APPS</translation>
+<translation id="4491109536499578614">Bild</translation>
<translation id="4588090240171750605">Nach rechts blättern</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Bewertung von <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, App-Empfehlung</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 Tag und }other{# Tage und }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{in 1 J.}other{in # J.}}</translation>
+<translation id="588258955323874662">Vollbild</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 Sekunde übrig}other{# Sekunden übrig}}</translation>
<translation id="5941711191222866238">Minimieren</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 Stunde}other{# Stunden}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">Alle Benachrichtigungen von dieser App blockieren</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Vor 1 Sekunde}other{Vor # Sekunden}}</translation>
+<translation id="6503257047630241175">RTF-Inhalt</translation>
<translation id="6539092367496845964">Ein Fehler ist aufgetreten. Versuchen Sie es später noch einmal.</translation>
<translation id="654149438358937226">Alle Benachrichtigungen blockieren</translation>
<translation id="6567071839949112727">Auf Vorgängerelement klicken</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Datei speichern</translation>
<translation id="6656912866303152668">Prüfen Sie, ob auf "<ph name="TARGET_DEVICE_NAME" />" die Synchronisierung in Chrome aktiviert ist, und versuchen Sie es dann noch einmal.</translation>
<translation id="6699343763173986273">Medien – nächster Titel</translation>
-<translation id="6710213216561001401">Zurück</translation>
+<translation id="6710213216561001401">Vorherige Einreichung</translation>
<translation id="6779314412797872738">Wenn Sie eine Nummer von diesem Gerät an Ihr Android-Smartphone senden möchten, müssen Sie für beide Geräte in den Einstellungen die <ph name="TROUBLESHOOT_LINK" />.</translation>
<translation id="6786750046913594791">Ordner schließen</translation>
<translation id="6808150112686056157">Medien – Stopp</translation>
@@ -256,7 +263,7 @@
<translation id="9161053988251441839">VORGESCHLAGENE APPS</translation>
<translation id="9170848237812810038">&amp;Rückgängig</translation>
<translation id="9178475906033259337">1 Ergebnis für "<ph name="QUERY" />"</translation>
-<translation id="932327136139879170">Privat</translation>
+<translation id="932327136139879170">Startseite</translation>
<translation id="944069440740578670">Ungelesene Benachrichtigungen</translation>
<translation id="974545358917229949"><ph name="RESULT_COUNT" /> Suchergebnisse für "<ph name="QUERY" />"</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/ui/strings/translations/ui_strings_el.xtb b/chromium/ui/strings/translations/ui_strings_el.xtb
index a523839b9a8..25cb7e54594 100644
--- a/chromium/ui/strings/translations/ui_strings_el.xtb
+++ b/chromium/ui/strings/translations/ui_strings_el.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">επιλογή</translation>
<translation id="1859234291848436338">Κατεύθυνση γραφής</translation>
<translation id="1860796786778352021">Κλείσιμο ειδοποίησης</translation>
+<translation id="186476001994626254">Περιεχόμενο έξυπνης επικόλλησης ιστού</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Επιλογή όλ&amp;ων</translation>
+<translation id="19085484004813472">Αυτή είναι μια νέα λειτουργία</translation>
<translation id="2006524834898217237">Βεβαιωθείτε ότι αυτή η συσκευή είναι συνδεδεμένη στο διαδίκτυο.</translation>
<translation id="208586643495776849">Προσπαθήστε ξανά</translation>
<translation id="2090963878406559571">Για να στείλετε έναν αριθμό από <ph name="ORIGIN" /> σε τηλέφωνο Android, <ph name="TROUBLESHOOT_LINK" /> και για τις δύο συσκευές στις ρυθμίσεις.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Διαχείριση ρυθμίσεων</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{σε 1 ω.}other{σε # ω.}}</translation>
<translation id="2583543531130364912">Βαθμονόμηση οθόνης αφής</translation>
+<translation id="2586657967955657006">Πρόχειρο</translation>
<translation id="2666092431469916601">Κορυφή</translation>
<translation id="2701330563083355633">Κοινοποιήθηκε από <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Νέο</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Άνοιγμα φακέλου</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Πριν από 1 χρόνο}other{Πριν από # χρόνια}}</translation>
+<translation id="2878511608894704031">Διαγραφή όλων</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{σε 1 η.}other{σε # η.}}</translation>
<translation id="2931838996092594335">κάνω κλικ</translation>
<translation id="2981684127883932071">Προβολή προτάσεων</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Έναρξη ομιλίας</translation>
<translation id="430191667033048642">Η εφαρμογή <ph name="MOVED_APP_NAME" /> μεταφέρθηκε στον φάκελο <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ΟΛΕΣ ΟΙ ΕΦΑΡΜΟΓΕΣ</translation>
+<translation id="4491109536499578614">Εικόνα</translation>
<translation id="4588090240171750605">Κύλιση δεξιά</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Αξιολόγηση με αστέρι <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Πρόταση εφαρμογής</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ημέρα και }other{# ημέρες και }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{σε 1 ε.}other{σε # ε.}}</translation>
+<translation id="588258955323874662">Πλήρης οθόνη</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Απομένει 1 δευτερόλεπτο}other{Απομένουν # δευτερόλεπτα}}</translation>
<translation id="5941711191222866238">Ελαχιστοποίηση</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ώρα}other{# ώρες}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Βοηθός Google</translation>
<translation id="6430678249303439055">Αποκλεισμός όλων των ειδοποιήσεων από αυτή την εφαρμογή</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Πριν από 1 δευτερόλεπτο}other{Πριν από # δευτερόλεπτα}}</translation>
+<translation id="6503257047630241175">Περιεχόμενο RTF</translation>
<translation id="6539092367496845964">Παρουσιάστηκε κάποιο πρόβλημα. Δοκιμάστε ξανά αργότερα.</translation>
<translation id="654149438358937226">Αποκλεισμός όλων των ειδοποιήσεων</translation>
<translation id="6567071839949112727">γονικό κλικ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_en-GB.xtb b/chromium/ui/strings/translations/ui_strings_en-GB.xtb
index 8e98369cfb3..1c06bddadb9 100644
--- a/chromium/ui/strings/translations/ui_strings_en-GB.xtb
+++ b/chromium/ui/strings/translations/ui_strings_en-GB.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">select</translation>
<translation id="1859234291848436338">Writing Direction</translation>
<translation id="1860796786778352021">Notification close</translation>
+<translation id="186476001994626254">Web smart paste content</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Select &amp;all</translation>
+<translation id="19085484004813472">This is a new feature</translation>
<translation id="2006524834898217237">Make sure that this device is connected to the Internet.</translation>
<translation id="208586643495776849">Please try again</translation>
<translation id="2090963878406559571">To send a number from <ph name="ORIGIN" /> to your Android phone, <ph name="TROUBLESHOOT_LINK" /> for both devices in settings.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Manage settings</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{in 1 h}other{in # h}}</translation>
<translation id="2583543531130364912">Calibrate your touchscreen</translation>
+<translation id="2586657967955657006">Clipboard</translation>
<translation id="2666092431469916601">Top</translation>
<translation id="2701330563083355633">Shared from <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">New</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Open folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 year ago}other{# years ago}}</translation>
+<translation id="2878511608894704031">Delete all</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{in 1 d}other{in # d}}</translation>
<translation id="2931838996092594335">click</translation>
<translation id="2981684127883932071">Displaying suggestions</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> moved to folder <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALL APPS</translation>
+<translation id="4491109536499578614">Image</translation>
<translation id="4588090240171750605">Scroll Right</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, star rating <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, app recommendation</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 day and }other{# days and }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{in 1 y}other{in # y}}</translation>
+<translation id="588258955323874662">Full screen</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 second left}other{# seconds left}}</translation>
<translation id="5941711191222866238">Minimise</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hour}other{# hours}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">Block all notifications from this app</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 second ago}other{# seconds ago}}</translation>
+<translation id="6503257047630241175">RTF content</translation>
<translation id="6539092367496845964">Something went wrong. Try again later.</translation>
<translation id="654149438358937226">Block all notifications</translation>
<translation id="6567071839949112727">click ancestor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_es-419.xtb b/chromium/ui/strings/translations/ui_strings_es-419.xtb
index 69cdba8bc87..81a71c3765e 100644
--- a/chromium/ui/strings/translations/ui_strings_es-419.xtb
+++ b/chromium/ui/strings/translations/ui_strings_es-419.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seleccionar</translation>
<translation id="1859234291848436338">Sentido de la escritura</translation>
<translation id="1860796786778352021">Cerrar notificación</translation>
+<translation id="186476001994626254">Contenido de Web Smart Paste</translation>
<translation id="1871244248791675517">Insert</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Seleccionar &amp;todo</translation>
+<translation id="19085484004813472">Esta es una función nueva</translation>
<translation id="2006524834898217237">Comprueba que este dispositivo tenga conexión a Internet.</translation>
<translation id="208586643495776849">Vuelve a intentarlo</translation>
<translation id="2090963878406559571">Para enviar un número desde <ph name="ORIGIN" /> a tu teléfono Android, <ph name="TROUBLESHOOT_LINK" /> en la configuración de ambos dispositivos.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Administrar configuración</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{en 1 h}other{en # h}}</translation>
<translation id="2583543531130364912">Calibrar la pantalla táctil</translation>
+<translation id="2586657967955657006">Portapapeles</translation>
<translation id="2666092431469916601">Superior</translation>
<translation id="2701330563083355633">Se compartió de <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nuevo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Abrir carpeta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Hace 1 año}other{Hace # años}}</translation>
+<translation id="2878511608894704031">Borrar todo</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{en 1 día}other{en # días}}</translation>
<translation id="2931838996092594335">hacer clic</translation>
<translation id="2981684127883932071">Mostrando sugerencias</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Empezar a hablar</translation>
<translation id="430191667033048642">Se movió <ph name="MOVED_APP_NAME" /> a la carpeta <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TODAS LAS APPS</translation>
+<translation id="4491109536499578614">Imagen</translation>
<translation id="4588090240171750605">Desplazar a la derecha</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, calificación por estrellas: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recomendación de app</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 día y }other{# días y }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{en 1 año}other{en # años}}</translation>
+<translation id="588258955323874662">Pantalla completa</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Falta 1 segundo.}other{Faltan # segundos.}}</translation>
<translation id="5941711191222866238">Minimizar</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hora}other{# horas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistente de Google</translation>
<translation id="6430678249303439055">No permitir las notificaciones de esta app</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Hace 1 segundo}other{Hace # segundos}}</translation>
+<translation id="6503257047630241175">Contenido del archivo RTF</translation>
<translation id="6539092367496845964">Se produjo un error; vuelve a intentarlo más tarde.</translation>
<translation id="654149438358937226">Bloquear todas las notificaciones</translation>
<translation id="6567071839949112727">hacer clic en principal</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_es.xtb b/chromium/ui/strings/translations/ui_strings_es.xtb
index ff2b36457d0..319070c95d8 100644
--- a/chromium/ui/strings/translations/ui_strings_es.xtb
+++ b/chromium/ui/strings/translations/ui_strings_es.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seleccionar</translation>
<translation id="1859234291848436338">Sentido de la escritura</translation>
<translation id="1860796786778352021">Cerrar notificación</translation>
+<translation id="186476001994626254">Contenido de Web Smart Paste</translation>
<translation id="1871244248791675517">Insert</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Seleccionar &amp;todo</translation>
+<translation id="19085484004813472">Es una nueva función</translation>
<translation id="2006524834898217237">Comprueba que este dispositivo esté conectado a Internet.</translation>
<translation id="208586643495776849">Vuelve a intentarlo</translation>
<translation id="2090963878406559571">Para enviar un número desde <ph name="ORIGIN" /> a tu teléfono Android, <ph name="TROUBLESHOOT_LINK" /> en los ajustes de ambos dispositivos.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gestionar configuración</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{en 1 h.}other{en # h.}}</translation>
<translation id="2583543531130364912">Calibra la pantalla táctil</translation>
+<translation id="2586657967955657006">Portapapeles</translation>
<translation id="2666092431469916601">Superior</translation>
<translation id="2701330563083355633">Compartido desde <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nuevo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Abrir carpeta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Hace 1 año}other{Hace # años}}</translation>
+<translation id="2878511608894704031">Eliminar todo</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{en 1 día}other{en # días}}</translation>
<translation id="2931838996092594335">hacer clic</translation>
<translation id="2981684127883932071">Mostrando sugerencias</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Empezar a hablar</translation>
<translation id="430191667033048642">Se ha movido <ph name="MOVED_APP_NAME" /> a la carpeta <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TODAS LAS APLICACIONES</translation>
+<translation id="4491109536499578614">Imagen</translation>
<translation id="4588090240171750605">Desplazar a la derecha</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, valoración por estrellas de <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, aplicación recomendada</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 día y }other{# días y }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{en 1 año}other{en # años}}</translation>
+<translation id="588258955323874662">Pantalla completa</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Queda 1 segundo}other{Quedan # segundos}}</translation>
<translation id="5941711191222866238">Minimizar</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hora}other{# horas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistente de Google</translation>
<translation id="6430678249303439055">Bloquear todas las notificaciones de esta aplicación</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Hace 1 segundo}other{Hace # segundos}}</translation>
+<translation id="6503257047630241175">Contenido RTF</translation>
<translation id="6539092367496845964">Se ha producido un error. Vuelve a intentarlo más tarde.</translation>
<translation id="654149438358937226">Bloquear todas las notificaciones</translation>
<translation id="6567071839949112727">hacer clic en la superclase</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_et.xtb b/chromium/ui/strings/translations/ui_strings_et.xtb
index e050c6d39e1..ad9e97a9d19 100644
--- a/chromium/ui/strings/translations/ui_strings_et.xtb
+++ b/chromium/ui/strings/translations/ui_strings_et.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">vali</translation>
<translation id="1859234291848436338">Writing Direction (Kirjutamise suund)</translation>
<translation id="1860796786778352021">Märguande sulgemine</translation>
+<translation id="186476001994626254">Web Smart Paste'i sisu</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Vali &amp;kõik</translation>
+<translation id="19085484004813472">See on uus funktsioon</translation>
<translation id="2006524834898217237">Veenduge, et seadmel oleks internetiühendus.</translation>
<translation id="208586643495776849">Proovige uuesti</translation>
<translation id="2090963878406559571">Selleks et number asukohast <ph name="ORIGIN" /> teie Android-telefoni saata, tehke mõlema seadme puhul seadetes järgmist: <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Seadete haldamine</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 h pärast}other{# h pärast}}</translation>
<translation id="2583543531130364912">Kalibreerige puuteekraan</translation>
+<translation id="2586657967955657006">Lõikelaud</translation>
<translation id="2666092431469916601">Üles</translation>
<translation id="2701330563083355633">Jagas <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Uus</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Kausta avamine</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 aasta tagasi}other{# aastat tagasi}}</translation>
+<translation id="2878511608894704031">Kustuta kõik</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 p pärast}other{# p pärast}}</translation>
<translation id="2931838996092594335">klõpsake</translation>
<translation id="2981684127883932071">Soovitused on kuvatud</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Alusta rääkimist</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> teisaldati kausta <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">KÕIK RAKENDUSED</translation>
+<translation id="4491109536499578614">Image</translation>
<translation id="4588090240171750605">Keri paremale</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, tärnhinnang <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, rakenduse soovitus</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 päev ja }other{# päeva ja }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 a pärast}other{# a pärast}}</translation>
+<translation id="588258955323874662">Täisekraan</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekund on jäänud}other{# sekundit on jäänud}}</translation>
<translation id="5941711191222866238">Minimeeri</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 tund}other{# tundi}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google'i assistent</translation>
<translation id="6430678249303439055">Blokeeri kõik märguanded sellelt rakenduselt</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 sekund tagasi}other{# sekundit tagasi}}</translation>
+<translation id="6503257047630241175">RTF-sisu</translation>
<translation id="6539092367496845964">Midagi läks valesti. Proovige hiljem uuesti.</translation>
<translation id="654149438358937226">Blokeeri kõik märguanded</translation>
<translation id="6567071839949112727">kliki eellane</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_eu.xtb b/chromium/ui/strings/translations/ui_strings_eu.xtb
index a9b7305b27c..b9ec3a70491 100644
--- a/chromium/ui/strings/translations/ui_strings_eu.xtb
+++ b/chromium/ui/strings/translations/ui_strings_eu.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">hautatu</translation>
<translation id="1859234291848436338">Idazketa-norabidea</translation>
<translation id="1860796786778352021">Itxi jakinarazpena</translation>
+<translation id="186476001994626254">Sareko itsaste adimendunaren edukia</translation>
<translation id="1871244248791675517">Txertatu</translation>
<translation id="1884435127456172652">% <ph name="NUMBER" /></translation>
<translation id="1901303067676059328">Hautatu &amp;guztiak</translation>
+<translation id="19085484004813472">Eginbide berri bat da hau</translation>
<translation id="2006524834898217237">Ziurtatu gailua Internetera konektatuta dagoela.</translation>
<translation id="208586643495776849">Saiatu berriro</translation>
<translation id="2090963878406559571">Zenbaki bat <ph name="ORIGIN" /> helbidetik Android-eko telefonora bidaltzeko, joan ezarpenetara eta <ph name="TROUBLESHOOT_LINK" /> bi gailuetan.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Kudeatu ezarpenak</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 h barru}other{# h barru}}</translation>
<translation id="2583543531130364912">Kalibratu ukipen-pantaila</translation>
+<translation id="2586657967955657006">Arbela</translation>
<translation id="2666092431469916601">Goiko elementua</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> gailutik partekatu da</translation>
<translation id="271033894570825754">Berria</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" /> (<ph name="PRICE" />)</translation>
<translation id="2803313416453193357">Ireki karpeta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Duela 1 urte}other{Duela # urte}}</translation>
+<translation id="2878511608894704031">Ezabatu dena</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 egun barru}other{# egun barru}}</translation>
<translation id="2931838996092594335">egin klik</translation>
<translation id="2981684127883932071">Iradokizunak daude ikusgai</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Hasi hitz egiten</translation>
<translation id="430191667033048642">Eraman da <ph name="MOVED_APP_NAME" /> aplikazioa <ph name="FOLDER_NAME" /> karpetara.</translation>
<translation id="4316910396681052118">APLIKAZIO GUZTIAK</translation>
+<translation id="4491109536499578614">Irudia</translation>
<translation id="4588090240171750605">Joan eskuinera</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" /> aplikazioa. <ph name="RATING_SCORE" /> izarreko balorazioa dauka.</translation>
<translation id="4648249871170053485">Gomendatutako aplikazioa (<ph name="APP_NAME" />)</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 egun eta }other{# egun eta }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 urte barru}other{# urte barru}}</translation>
+<translation id="588258955323874662">Pantaila osoa</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 segundo gelditzen da}other{# segundo gelditzen dira}}</translation>
<translation id="5941711191222866238">Minimizatu</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ordu}other{# ordu}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google-ren Laguntzailea</translation>
<translation id="6430678249303439055">Blokeatu aplikazio honen jakinarazpen guztiak</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Duela 1 segundo}other{Duela # segundo}}</translation>
+<translation id="6503257047630241175">RTF fitxategiko edukia</translation>
<translation id="6539092367496845964">Arazoren bat izan da. Saiatu berriro geroago.</translation>
<translation id="654149438358937226">Blokeatu jakinarazpen guztiak</translation>
<translation id="6567071839949112727">egin klik aurreko bertsioan</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_fa.xtb b/chromium/ui/strings/translations/ui_strings_fa.xtb
index eb8d03290fc..9744125ab0c 100644
--- a/chromium/ui/strings/translations/ui_strings_fa.xtb
+++ b/chromium/ui/strings/translations/ui_strings_fa.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">انتخاب</translation>
<translation id="1859234291848436338">جهت نوشتن</translation>
<translation id="1860796786778352021">بستن اعلان</translation>
+<translation id="186476001994626254">جای‌گذاری هوشمند محتوا در وب</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />٪</translation>
<translation id="1901303067676059328">انتخاب &amp;همه</translation>
+<translation id="19085484004813472">این ویژگی جدید است</translation>
<translation id="2006524834898217237">مطمئن شوید دستگاه به اینترنت متصل باشد.</translation>
<translation id="208586643495776849">لطفاً دوباره امتحان کنید</translation>
<translation id="2090963878406559571">‏برای ارسال شماره‌ای از <ph name="ORIGIN" /> به تلفن Android خود، برای هر دو دستگاه در «تنظیمات» <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">مدیریت تنظیمات</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{تا ۱ ساعت دیگر}one{تا # ساعت دیگر}other{تا # ساعت دیگر}}</translation>
<translation id="2583543531130364912">کالیبراسیون صفحه لمسی</translation>
+<translation id="2586657967955657006">بریده‌دان</translation>
<translation id="2666092431469916601">بالا</translation>
<translation id="2701330563083355633">هم‌رسانی‌شده از <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">جدید</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />، <ph name="PRICE" /></translation>
<translation id="2803313416453193357">بازکردن پوشه</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{۱ سال قبل}one{# سال قبل}other{# سال قبل}}</translation>
+<translation id="2878511608894704031">حذف همه</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{تا ۱ روز دیگر}one{تا # روز دیگر}other{تا # روز دیگر}}</translation>
<translation id="2931838996092594335">کلیک کنید</translation>
<translation id="2981684127883932071">نمایش پیشنهادها</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">شروع صحبت</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> به پوشه <ph name="FOLDER_NAME" /> منتقل شد.</translation>
<translation id="4316910396681052118">همه برنامه‌ها</translation>
+<translation id="4491109536499578614">تصویر</translation>
<translation id="4588090240171750605">پیمایش به راست</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />، رتبه‌بندی با ستاره <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />، توصیه برنامه</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{۱ روز و }one{# روز و }other{# روز و }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{تا ۱ سال دیگر}one{تا # سال دیگر}other{تا # سال دیگر}}</translation>
+<translation id="588258955323874662">تمام صفحه</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{۱ ثانیه باقی مانده است}one{# ثانیه باقی مانده است}other{# ثانیه باقی مانده است}}</translation>
<translation id="5941711191222866238">کوچک کردن</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{۱ ساعت}one{# ساعت}other{# ساعت}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">‏دستیار Google</translation>
<translation id="6430678249303439055">مسدود کردن همه اعلان‌های این برنامه</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{۱ ثانیه قبل}one{# ثانیه قبل}other{# ثانیه قبل}}</translation>
+<translation id="6503257047630241175">‏محتوای RTF</translation>
<translation id="6539092367496845964">مشکلی پیش آمد. بعداً دوباره امتحان کنید.</translation>
<translation id="654149438358937226">مسدود کردن همه اعلان‌ها</translation>
<translation id="6567071839949112727">کلیک کردن روی نسخه اولیه</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_fi.xtb b/chromium/ui/strings/translations/ui_strings_fi.xtb
index 2e803dc1fb8..fae3e063eca 100644
--- a/chromium/ui/strings/translations/ui_strings_fi.xtb
+++ b/chromium/ui/strings/translations/ui_strings_fi.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">Valitse</translation>
<translation id="1859234291848436338">Kirjoitussuunta</translation>
<translation id="1860796786778352021">Ilmoitus sulje</translation>
+<translation id="186476001994626254">Web Smart Paste ‑sisältö</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Valitse &amp;kaikki</translation>
+<translation id="19085484004813472">Tämä on uusi ominaisuus</translation>
<translation id="2006524834898217237">Varmista, että laite on yhteydessä internetiin.</translation>
<translation id="208586643495776849">Yritä uudelleen</translation>
<translation id="2090963878406559571">Jos haluat lähettää numeron (<ph name="ORIGIN" />) Android-puhelimellesi, <ph name="TROUBLESHOOT_LINK" /> molempien laitteiden asetuksista.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Muuta asetuksia</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 h:n päästä}other{# h:n päästä}}</translation>
<translation id="2583543531130364912">Kalibroi kosketusnäyttö</translation>
+<translation id="2586657967955657006">Leikepöytä</translation>
<translation id="2666092431469916601">Yleisin</translation>
<translation id="2701330563083355633">Jaettu: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Uusi</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Avaa kansio</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 vuosi sitten}other{# vuotta sitten}}</translation>
+<translation id="2878511608894704031">Poista kaikki</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 pv:n päästä}other{# pv:n päästä}}</translation>
<translation id="2931838996092594335">klikata</translation>
<translation id="2981684127883932071">Ehdotuksia näytetään</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Aloita puhuminen</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> siirrettiin kansioon <ph name="FOLDER_NAME" /></translation>
<translation id="4316910396681052118">KAIKKI SOVELLUKSET</translation>
+<translation id="4491109536499578614">Kuva</translation>
<translation id="4588090240171750605">Vieritä oikealle</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, tähtiarvio <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, sovellussuositus</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 päivä ja }other{# päivää ja }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 v:n päästä}other{# v:n päästä}}</translation>
+<translation id="588258955323874662">Koko ruutu</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekunti jäljellä}other{# sekuntia jäljellä}}</translation>
<translation id="5941711191222866238">Pienennä</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 tunti}other{# tuntia}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">Estä kaikki tämän sovelluksen ilmoitukset</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 sekunti sitten}other{# sekuntia sitten}}</translation>
+<translation id="6503257047630241175">RTF-sisältö</translation>
<translation id="6539092367496845964">Jotain meni vikaan. Yritä myöhemmin uudelleen.</translation>
<translation id="654149438358937226">Estä kaikki ilmoitukset</translation>
<translation id="6567071839949112727">klikkaa edeltäjää</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_fil.xtb b/chromium/ui/strings/translations/ui_strings_fil.xtb
index b4993a7b745..758698103e8 100644
--- a/chromium/ui/strings/translations/ui_strings_fil.xtb
+++ b/chromium/ui/strings/translations/ui_strings_fil.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">piliin</translation>
<translation id="1859234291848436338">Pagsulat ng Direksyon</translation>
<translation id="1860796786778352021">Isara ang notification</translation>
+<translation id="186476001994626254">Content mula sa Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Piliin ang &amp;lahat</translation>
+<translation id="19085484004813472">Isa itong bagong feature</translation>
<translation id="2006524834898217237">Siguraduhing nakakonekta ang device na ito sa Internet.</translation>
<translation id="208586643495776849">Pakisubukang muli</translation>
<translation id="2090963878406559571">Para magpadala ng numero sa iyong Android phone mula sa <ph name="ORIGIN" />, <ph name="TROUBLESHOOT_LINK" /> para sa parehong device sa mga setting.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Pamahalaan ang mga setting</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{sa loob ng 1 oras}one{sa loob ng # oras}other{sa loob ng # na oras}}</translation>
<translation id="2583543531130364912">I-calibrate ang iyong touchscreen</translation>
+<translation id="2586657967955657006">Clipboard</translation>
<translation id="2666092431469916601">Tuktok</translation>
<translation id="2701330563083355633">Ibinahagi mula kay <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Bago</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Buksan ang folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 taon ang nakalipas}one{# taon ang nakalipas}other{# na taon ang nakalipas}}</translation>
+<translation id="2878511608894704031">I-delete Lahat</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{sa loob ng 1 araw}one{sa loob ng # araw}other{sa loob ng # na araw}}</translation>
<translation id="2931838996092594335">i-click</translation>
<translation id="2981684127883932071">Ipinapakita ang mga suhestyon</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Simulan ang Pagsasalita</translation>
<translation id="430191667033048642">Inilipat ang <ph name="MOVED_APP_NAME" /> sa folder na <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">LAHAT NG APP</translation>
+<translation id="4491109536499578614">Larawan</translation>
<translation id="4588090240171750605">Mag-scroll Pakanan</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Star na rating <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Rekomendasyon sa app</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 araw at }one{# araw at }other{# na araw at }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{sa loob ng 1 taon}one{sa loob ng # taon}other{sa loob ng # na taon}}</translation>
+<translation id="588258955323874662">Fullscreen</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 segundo na lang ang natitira}one{# segundo na lang ang natitira}other{# na segundo na lang ang natitira}}</translation>
-<translation id="5941711191222866238">Minimize</translation>
+<translation id="5941711191222866238">I-minimize</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 oras}one{# oras}other{# na oras}}</translation>
<translation id="6012623610530968780">Page <ph name="SELECTED_PAGE" /> ng <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">I-clear ang text sa searchbox</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">I-block ang lahat ng notification mula sa app na ito</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 segundo ang nakalipas}one{# segundo ang nakalipas}other{# na segundo ang nakalipas}}</translation>
+<translation id="6503257047630241175">Content na RTF</translation>
<translation id="6539092367496845964">Nagkaproblema. Subukan ulit sa ibang pagkakataon.</translation>
<translation id="654149438358937226">I-block ang lahat ng notification</translation>
<translation id="6567071839949112727">i-click ang ancestor</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">I-save ang File</translation>
<translation id="6656912866303152668">Tiyaking naka-on sa <ph name="TARGET_DEVICE_NAME" /> ang pag-sync sa Chrome, at pagkatapos ay subukang ipadala ulit.</translation>
<translation id="6699343763173986273">Susunod na Track ng Media</translation>
-<translation id="6710213216561001401">Nakaraan</translation>
+<translation id="6710213216561001401">Nauna</translation>
<translation id="6779314412797872738">Para magpadala ng numero sa iyong Android phone mula rito, <ph name="TROUBLESHOOT_LINK" /> para sa parehong device sa mga setting.</translation>
<translation id="6786750046913594791">Isara ang folder</translation>
<translation id="6808150112686056157">Media Ihinto</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_fr-CA.xtb b/chromium/ui/strings/translations/ui_strings_fr-CA.xtb
index 9d7bd6abebd..687722950d8 100644
--- a/chromium/ui/strings/translations/ui_strings_fr-CA.xtb
+++ b/chromium/ui/strings/translations/ui_strings_fr-CA.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">sélectionner</translation>
<translation id="1859234291848436338">Direction de l'écriture</translation>
<translation id="1860796786778352021">Fermeture de la notification</translation>
+<translation id="186476001994626254">Contenu du coller intelligent Web</translation>
<translation id="1871244248791675517">Inser</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;Tout sélectionner</translation>
+<translation id="19085484004813472">Il s'agit d'une nouvelle fonctionnalité</translation>
<translation id="2006524834898217237">Assurez-vous que cet appareil est connecté à Internet.</translation>
<translation id="208586643495776849">Veuillez réessayer</translation>
<translation id="2090963878406559571">Pour envoyer un numéro de <ph name="ORIGIN" /> vers votre téléphone Android, <ph name="TROUBLESHOOT_LINK" /> pour les deux appareils dans les paramètres.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gérer les paramètres</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{dans 1 h}one{dans # h}other{dans # h}}</translation>
<translation id="2583543531130364912">Calibrez votre écran tactile</translation>
+<translation id="2586657967955657006">Presse-papiers</translation>
<translation id="2666092431469916601">Haut</translation>
<translation id="2701330563083355633">Partagé à partir de l'appareil suivant : <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nouveau</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Ouvrir le dossier</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Il y a un an}one{Il y a # an}other{Il y a # ans}}</translation>
+<translation id="2878511608894704031">Tout supprimer</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{dans 1 j}one{dans # j}other{dans # j}}</translation>
<translation id="2931838996092594335">cliquer</translation>
<translation id="2981684127883932071">Affichage des suggestions</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Commencer la lecture</translation>
<translation id="430191667033048642">L'application <ph name="MOVED_APP_NAME" /> a été déplacée dans le dossier <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TOUTES LES APPLICATIONS</translation>
+<translation id="4491109536499578614">Image</translation>
<translation id="4588090240171750605">Défilement vers la droite</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, note <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recommandation d'application</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 jour et }one{# jour et }other{# jours et }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{dans 1 an}one{dans # an}other{dans # ans}}</translation>
+<translation id="588258955323874662">Plein écran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 seconde restante}one{# seconde restante}other{# secondes restantes}}</translation>
<translation id="5941711191222866238">Réduire</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 heure}one{# heure}other{# heures}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Assistant Google</translation>
<translation id="6430678249303439055">Bloquer toutes les notifications de cette application</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Il y a 1 seconde}one{Il y a # seconde}other{Il y a # secondes}}</translation>
+<translation id="6503257047630241175">Contenu en format RTF</translation>
<translation id="6539092367496845964">Une erreur s'est produite. Réessayez plus tard.</translation>
<translation id="654149438358937226">Bloquer toutes les notifications</translation>
<translation id="6567071839949112727">cliquer sur l'ancien objet</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_fr.xtb b/chromium/ui/strings/translations/ui_strings_fr.xtb
index d0b4c41482a..15874b65616 100644
--- a/chromium/ui/strings/translations/ui_strings_fr.xtb
+++ b/chromium/ui/strings/translations/ui_strings_fr.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">sélectionner</translation>
<translation id="1859234291848436338">Sens de l'écriture</translation>
<translation id="1860796786778352021">Fermer la notification</translation>
+<translation id="186476001994626254">Contenu Web collé depuis le presse-papiers intelligent</translation>
<translation id="1871244248791675517">Insér.</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;Tout sélectionner</translation>
+<translation id="19085484004813472">Il s'agit d'une nouvelle fonctionnalité</translation>
<translation id="2006524834898217237">Assurez-vous que cet appareil est bien connecté à Internet.</translation>
<translation id="208586643495776849">Veuillez réessayer</translation>
<translation id="2090963878406559571">Pour envoyer un numéro depuis <ph name="ORIGIN" /> vers votre téléphone Android, <ph name="TROUBLESHOOT_LINK" /> pour les deux appareils dans les paramètres.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gérer les paramètres</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{dans 1 h}one{dans # h}other{dans # h}}</translation>
<translation id="2583543531130364912">Calibrer votre écran tactile</translation>
+<translation id="2586657967955657006">Presse-papiers</translation>
<translation id="2666092431469916601">En haut</translation>
<translation id="2701330563083355633">Partagé par <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nouveau</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Ouvrir le dossier</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Il y a 1 an}one{Il y a # an}other{Il y a # ans}}</translation>
+<translation id="2878511608894704031">Tout supprimer</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{dans 1 j}one{dans # j}other{dans # j}}</translation>
<translation id="2931838996092594335">cliquer</translation>
<translation id="2981684127883932071">Affichage de suggestions</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Commencer à parler</translation>
<translation id="430191667033048642">L'application <ph name="MOVED_APP_NAME" /> a été déplacée vers le dossier "<ph name="FOLDER_NAME" />".</translation>
<translation id="4316910396681052118">TOUTES LES APPLICATIONS</translation>
+<translation id="4491109536499578614">Image</translation>
<translation id="4588090240171750605">Défilement vers la droite</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, note : <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recommandation d'application</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 jour et }one{# jour et }other{# jours et }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{dans 1 a}one{dans # a}other{dans # a}}</translation>
+<translation id="588258955323874662">Plein écran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 seconde restante}one{# seconde restante}other{# secondes restantes}}</translation>
<translation id="5941711191222866238">Réduire</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 heure}one{# heure}other{# heures}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Assistant Google</translation>
<translation id="6430678249303439055">Bloquer toutes les notifications de cette application</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Il y a une seconde}one{Il y a # seconde}other{Il y a # secondes}}</translation>
+<translation id="6503257047630241175">Contenu au format RTF</translation>
<translation id="6539092367496845964">Un problème est survenu. Réessayez plus tard.</translation>
<translation id="654149438358937226">Bloquer toutes les notifications</translation>
<translation id="6567071839949112727">cliquer sur l'ancêtre</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_gl.xtb b/chromium/ui/strings/translations/ui_strings_gl.xtb
index ea317b3e9e4..6d8bfb944bf 100644
--- a/chromium/ui/strings/translations/ui_strings_gl.xtb
+++ b/chromium/ui/strings/translations/ui_strings_gl.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seleccionar</translation>
<translation id="1859234291848436338">Dirección de escritura</translation>
<translation id="1860796786778352021">Peche da notificación</translation>
+<translation id="186476001994626254">Contido da función de pegado intelixente da Web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Seleccion&amp;ar todo</translation>
+<translation id="19085484004813472">Esta é unha función nova</translation>
<translation id="2006524834898217237">Asegúrate de que este dispositivo estea conectado a Internet.</translation>
<translation id="208586643495776849">Téntao de novo</translation>
<translation id="2090963878406559571">Para enviar un número desde <ph name="ORIGIN" /> ao teu teléfono Android, debes <ph name="TROUBLESHOOT_LINK" /> na configuración dos dous dispositivos.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Xestionar configuración</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{en 1 h}other{en # h}}</translation>
<translation id="2583543531130364912">Calibra a pantalla táctil</translation>
+<translation id="2586657967955657006">Portapapeis</translation>
<translation id="2666092431469916601">Parte superior</translation>
<translation id="2701330563083355633">Contido compartido desde <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Abrir cartafol</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Hai un ano}other{Hai # anos}}</translation>
+<translation id="2878511608894704031">Eliminar todo</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{en 1 d}other{en # d}}</translation>
<translation id="2931838996092594335">facer clic</translation>
<translation id="2981684127883932071">Mostrando suxestións</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Comeza a falar</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> moveuse ao cartafol <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TODAS AS APLICACIÓNS</translation>
+<translation id="4491109536499578614">Imaxe</translation>
<translation id="4588090240171750605">Desprazar cara á dereita</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, valoración en estrelas: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recomendación de aplicación</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{Un día e }other{# días e }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{en 1 a}other{en # a}}</translation>
+<translation id="588258955323874662">Pantalla completa</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Queda un segundo}other{Quedan # segundos}}</translation>
<translation id="5941711191222866238">Minimizar</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{Unha hora}other{# horas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistente de Google</translation>
<translation id="6430678249303439055">Bloquear todas as notificacións desta aplicación</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Hai un segundo}other{Hai # segundos}}</translation>
+<translation id="6503257047630241175">Contido en formato RTF</translation>
<translation id="6539092367496845964">Produciuse un erro. Téntao de novo máis tarde.</translation>
<translation id="654149438358937226">Bloquear todas as notificacións</translation>
<translation id="6567071839949112727">fai clic no predecesor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_gu.xtb b/chromium/ui/strings/translations/ui_strings_gu.xtb
index adc23acf5ec..9256d43ae04 100644
--- a/chromium/ui/strings/translations/ui_strings_gu.xtb
+++ b/chromium/ui/strings/translations/ui_strings_gu.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">પસંદ કરો</translation>
<translation id="1859234291848436338">લેખનના દિશાનિર્દેશ</translation>
<translation id="1860796786778352021">સૂચના બંધ છે</translation>
+<translation id="186476001994626254">વેબ સ્માર્ટ પેસ્ટ કન્ટેન્ટ</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;બધા પસંદ કરો</translation>
+<translation id="19085484004813472">આ નવી સુવિધા છે</translation>
<translation id="2006524834898217237">ખાતરી કરો કે આ ડિવાઇસ ઇન્ટરનેટ સાથે કનેક્ટ કરેલું છે.</translation>
<translation id="208586643495776849">કૃપા કરીને ફરી પ્રયાસ કરો</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" />થી તમારા Android ફોન પર નંબર મોકલવા માટે, સેટિંગમાં બંને ડિવાઇસ માટે <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">સેટિંગને મેનેજ કરો</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1ક. માં}one{#ક. માં}other{#ક. માં}}</translation>
<translation id="2583543531130364912">તમારી ટચસ્ક્રીનને કેલિબ્રેટ કરો</translation>
+<translation id="2586657967955657006">ક્લિપબોર્ડ</translation>
<translation id="2666092431469916601">ઉપર</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" />માંથી શેર કર્યું</translation>
<translation id="271033894570825754">નવું</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ફોલ્ડર ખોલો</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 વર્ષ પહેલાં}one{# વર્ષ પહેલાં}other{# વર્ષ પહેલાં}}</translation>
+<translation id="2878511608894704031">બધું ડિલીટ કરો</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1દિ. માં}one{#દિ. માં}other{#દિ. માં}}</translation>
<translation id="2931838996092594335">ક્લિક કરો</translation>
<translation id="2981684127883932071">સૂચનો બતાવી રહ્યાં છીએ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">બોલવાનું પ્રારંભ કરો</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" />ને ફોલ્ડર <ph name="FOLDER_NAME" />માં ખસેડી.</translation>
<translation id="4316910396681052118">બધી ઍપ્લિકેશનો</translation>
+<translation id="4491109536499578614">છબી</translation>
<translation id="4588090240171750605">જમણે સ્ક્રોલ કરો</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, સ્ટાર રેટિંગ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ઍપ માટે સુઝાવ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 દિવસ અને }one{# દિવસ અને }other{# દિવસ અને }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1વ. માં}one{#વ. માં}other{#વ. માં}}</translation>
+<translation id="588258955323874662">પૂર્ણસ્ક્રીન</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 સેકન્ડ બાકી}one{# સેકન્ડ બાકી}other{# સેકન્ડ બાકી}}</translation>
<translation id="5941711191222866238">નાનું કરો</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 કલાક}one{# કલાક}other{# કલાક}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">આ ઍપના બધા નોટિફિકેશનો અવરોધિત કરો</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 સેકંડ પહેલાં}one{# સેકંડ પહેલાં}other{# સેકંડ પહેલાં}}</translation>
+<translation id="6503257047630241175">RTF કન્ટેન્ટ</translation>
<translation id="6539092367496845964">કંઈક ખોટું થયું હતું. થોડીવાર પછી ફરી પ્રયાસ કરો.</translation>
<translation id="654149438358937226">તમામ નોટિફિકેશનને બ્લૉક કરો</translation>
<translation id="6567071839949112727">ઍન્સેસ્ટર પર ક્લિક કરો</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_hi.xtb b/chromium/ui/strings/translations/ui_strings_hi.xtb
index 6a419668fbe..5f0a144ada5 100644
--- a/chromium/ui/strings/translations/ui_strings_hi.xtb
+++ b/chromium/ui/strings/translations/ui_strings_hi.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">चुनें</translation>
<translation id="1859234291848436338">लिखने के निर्देश</translation>
<translation id="1860796786778352021">सूचना बंद करें</translation>
+<translation id="186476001994626254">वेब स्मार्ट पेस्ट कॉन्टेंट</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;सभी को चुनें</translation>
+<translation id="19085484004813472">यह नई सुविधा है</translation>
<translation id="2006524834898217237">पक्का करें कि यह डिवाइस इंटरनेट से कनेक्ट है.</translation>
<translation id="208586643495776849">कृपया दोबारा कोशिश करें</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> से अपने Android फ़ोन पर नंबर भेजने के लिए, सेटिंग में जाकर दोनों डिवाइस के लिए <ph name="TROUBLESHOOT_LINK" /> इस्तेमाल करें.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">सेटिंग को प्रबंधित करें</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 घंटे में}one{#घंटे में}other{#घंटे में}}</translation>
<translation id="2583543531130364912">अपनी टचस्क्रीन कैलिब्रेट करें</translation>
+<translation id="2586657967955657006">क्लिपबोर्ड</translation>
<translation id="2666092431469916601">शीर्ष</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> से शेयर किया</translation>
<translation id="271033894570825754">नया</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">फ़ोल्डर खोलें</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 वर्ष पहले}one{# वर्ष पहले}other{# वर्ष पहले}}</translation>
+<translation id="2878511608894704031">सभी आइटम मिटाएं</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 दिन में}one{# दिन में}other{# दिन में}}</translation>
<translation id="2931838996092594335">क्लिक करें</translation>
<translation id="2981684127883932071">सुझाव दिखाए जा रहे हैं</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">बोलना प्रारंभ करें</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> को <ph name="FOLDER_NAME" /> में ले जाया गया</translation>
<translation id="4316910396681052118">सभी ऐप्लिकेशन</translation>
+<translation id="4491109536499578614">इमेज</translation>
<translation id="4588090240171750605">दाएं स्क्रोल करें</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> स्टार रेटिंग</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ऐप्लिकेशन का सुझाव</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 दिन और }one{# दिन और }other{# दिन और }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1साल में}one{# साल में}other{# साल में}}</translation>
+<translation id="588258955323874662">पूर्णस्‍क्रीन</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 सेकंड शेष}one{# सेकंड शेष}other{# सेकंड शेष}}</translation>
<translation id="5941711191222866238">छोटा करें</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 घंटा}one{# घंटे}other{# घंटे}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">इस ऐप्लिकेशन से सभी सूचनाएं ब्लॉक करें</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 सेकंड पहले}one{# सेकंड पहले}other{# सेकंड पहले}}</translation>
+<translation id="6503257047630241175">RTF कॉन्टेंट</translation>
<translation id="6539092367496845964">कोई गड़बड़ी हुई. बाद में कोशिश करें.</translation>
<translation id="654149438358937226">सभी सूचनाएं ब्लॉक करें</translation>
<translation id="6567071839949112727">पहले वाले पर क्लिक करें</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_hr.xtb b/chromium/ui/strings/translations/ui_strings_hr.xtb
index 3164de3bbfc..7cc39c44935 100644
--- a/chromium/ui/strings/translations/ui_strings_hr.xtb
+++ b/chromium/ui/strings/translations/ui_strings_hr.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">odaberi</translation>
<translation id="1859234291848436338">Smjer pisanja</translation>
<translation id="1860796786778352021">Zatvaranje obavijesti</translation>
+<translation id="186476001994626254">Web-sadržaj za pametno lijepljenje</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Odaberi &amp;sve</translation>
+<translation id="19085484004813472">Ovo je nova značajka</translation>
<translation id="2006524834898217237">Provjerite je li uređaj povezan s internetom.</translation>
<translation id="208586643495776849">Pokušajte ponovo</translation>
<translation id="2090963878406559571">Da biste poslali broj s <ph name="ORIGIN" /> na svoj Android telefon, <ph name="TROUBLESHOOT_LINK" /> za oba uređaja u postavkama.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Upravljajte postavkama</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{za 1 h}one{za # h}few{za # h}other{za # h}}</translation>
<translation id="2583543531130364912">Kalibrirajte dodirni zaslon</translation>
+<translation id="2586657967955657006">Međuspremnik</translation>
<translation id="2666092431469916601">Gornji</translation>
<translation id="2701330563083355633">Podijeljeno s uređaja <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otvori mapu</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Prije godinu dana}one{Prije # godine}few{Prije # godine}other{Prije # godina}}</translation>
+<translation id="2878511608894704031">Izbriši sve</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{za 1 d}one{za # d}few{za # d}other{za # d}}</translation>
<translation id="2931838996092594335">klikanje</translation>
<translation id="2981684127883932071">Prikazuju se prijedlozi</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Počni govoriti</translation>
<translation id="430191667033048642">Aplikacija <ph name="MOVED_APP_NAME" /> premještena je u mapu <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">SVE APLIKACIJE</translation>
+<translation id="4491109536499578614">Slika</translation>
<translation id="4588090240171750605">Pomakni se desno</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, broj zvjezdica <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, preporuka aplikacije</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dan i }one{# dan i }few{# dana i }other{# dana i }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{za 1 g}one{za # g}few{za # g}other{za # g}}</translation>
+<translation id="588258955323874662">Puni zaslon</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Još 1 sekunda}one{Još # sekunda}few{Još # sekunde}other{Još # sekundi}}</translation>
<translation id="5941711191222866238">Minimiziraj</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google asistent</translation>
<translation id="6430678249303439055">Blokiraj sve obavijesti te aplikacije</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Prije 1 sekunde}one{Prije # sekunde}few{Prije # sekunde}other{Prije # sekundi}}</translation>
+<translation id="6503257047630241175">RTF sadržaj</translation>
<translation id="6539092367496845964">Nešto nije u redu. Pokušajte ponovo kasnije.</translation>
<translation id="654149438358937226">Blokiraj sve obavijesti</translation>
<translation id="6567071839949112727">klikni nadređeni element</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_hu.xtb b/chromium/ui/strings/translations/ui_strings_hu.xtb
index 1e3a0267ff3..a430d773e0f 100644
--- a/chromium/ui/strings/translations/ui_strings_hu.xtb
+++ b/chromium/ui/strings/translations/ui_strings_hu.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">Kiválasztás</translation>
<translation id="1859234291848436338">Írás iránya</translation>
<translation id="1860796786778352021">Értesítés bezárása</translation>
+<translation id="186476001994626254">Web Smart Paste-tartalom</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Össz&amp;es kiválasztása</translation>
+<translation id="19085484004813472">Ez a funkció új</translation>
<translation id="2006524834898217237">Ellenőrizze, hogy az eszköz csatlakozik-e az internethez.</translation>
<translation id="208586643495776849">Próbálkozzon újra</translation>
<translation id="2090963878406559571">Ha a(z) <ph name="ORIGIN" /> webhelyről szeretne számot küldeni Android-telefonjára, <ph name="TROUBLESHOOT_LINK" /> a beállításokban mindkét eszközén.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Beállítások kezelése</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 óra múlva}other{# óra múlva}}</translation>
<translation id="2583543531130364912">Az érintőképernyő kalibrálása</translation>
+<translation id="2586657967955657006">Vágólap</translation>
<translation id="2666092431469916601">Felülre</translation>
<translation id="2701330563083355633">Megosztva a következő eszközről: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Új</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Mappa megnyitása</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 éve}other{# éve}}</translation>
+<translation id="2878511608894704031">Az összes törlése</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 nap múlva}other{# nap múlva}}</translation>
<translation id="2931838996092594335">kattintás</translation>
<translation id="2981684127883932071">Javaslatok megjelenítve</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Beszéd megkezdése</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> áthelyezve a következő mappába: <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">MINDEN ALKALMAZÁS</translation>
+<translation id="4491109536499578614">Kép</translation>
<translation id="4588090240171750605">Görgetés jobbra</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> csillagos értékelés</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, javasolt alkalmazás</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 nap és }other{# nap és }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 év múlva}other{# év múlva}}</translation>
+<translation id="588258955323874662">Teljes képernyő</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 másodperc van hátra}other{# másodperc van hátra}}</translation>
-<translation id="5941711191222866238">Kicsinyítés</translation>
+<translation id="5941711191222866238">Kis méret</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 óra}other{# óra}}</translation>
<translation id="6012623610530968780"><ph name="TOTAL_PAGE_NUM" />/<ph name="SELECTED_PAGE" />. oldal</translation>
<translation id="6022924867608035986">A keresőmezőben lévő szöveg törlése</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Segéd</translation>
<translation id="6430678249303439055">Minden értesítés letiltása ettől az alkalmazástól</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 másodperce}other{# másodperce}}</translation>
+<translation id="6503257047630241175">RTF-tartalom</translation>
<translation id="6539092367496845964">Hiba történt. Próbálja újra később.</translation>
<translation id="654149438358937226">Minden értesítés letiltása</translation>
<translation id="6567071839949112727">kattintás az elődre</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_hy.xtb b/chromium/ui/strings/translations/ui_strings_hy.xtb
index f4fcbbd631a..17578f8b055 100644
--- a/chromium/ui/strings/translations/ui_strings_hy.xtb
+++ b/chromium/ui/strings/translations/ui_strings_hy.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ընտրել</translation>
<translation id="1859234291848436338">Գրելու ուղղություն</translation>
<translation id="1860796786778352021">Փակել ծանուցումը</translation>
+<translation id="186476001994626254">Web Smart Paste ձևաչափով բովանդակություն</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Ընտրել &amp;բոլորը</translation>
+<translation id="19085484004813472">Սա նոր գործառույթ է</translation>
<translation id="2006524834898217237">Համոզվեք, որ սարքը միացված է ինտերնետին։</translation>
<translation id="208586643495776849">Նորից փորձեք</translation>
<translation id="2090963878406559571">Համարը <ph name="ORIGIN" /> կայքից ձեր Android հեռախոսին ուղարկելու համար <ph name="TROUBLESHOOT_LINK" /> սարքերի համար կարգավորումներում։</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Կառավարել կարգավորումները</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 ժ-ից}one{# ժ-ից}other{# ժ-ից}}</translation>
<translation id="2583543531130364912">Հպէկրանի չափաբերում</translation>
+<translation id="2586657967955657006">Սեղմատախտակ</translation>
<translation id="2666092431469916601">Վերին</translation>
<translation id="2701330563083355633">Ուղարկվել է <ph name="DEVICE_NAME" />-ի միջոցով</translation>
<translation id="271033894570825754">New</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Բացել պանակը</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 տարի առաջ}one{# years ago}other{# տարի առաջ}}</translation>
+<translation id="2878511608894704031">Ջնջել բոլորը</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 օրից}one{# օրից}other{# օրից}}</translation>
<translation id="2931838996092594335">սեղմել</translation>
<translation id="2981684127883932071">Ցուցադրվում են առաջարկները</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642">«<ph name="MOVED_APP_NAME" />» հավելվածը տեղափոխվեց «<ph name="FOLDER_NAME" />» պանակ։</translation>
<translation id="4316910396681052118">ԲՈԼՈՐ ՀԱՎԵԼՎԱԾՆԵՐԸ</translation>
+<translation id="4491109536499578614">Պատկեր</translation>
<translation id="4588090240171750605">Ոլորել դեպի աջ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, վարկանիշը՝ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, հավելվածի առաջարկ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 օր }one{# օր }other{# օր }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 տ-ուց}one{# տ-ուց}other{# տ-ուց}}</translation>
+<translation id="588258955323874662">Լիաէկրան</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Մնացել է 1 վայրկյան}one{Մնացել է # վայրկյան}other{Մնացել է # վայրկյան}}</translation>
<translation id="5941711191222866238">Նվազեցնել</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ժամ}one{# ժամ}other{# ժամ}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Օգնական</translation>
<translation id="6430678249303439055">Արգելափակել այս հավելվածի բոլոր ծանուցումները</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 վայրկյան առաջ}one{# seconds ago}other{# վայրկյան առաջ}}</translation>
+<translation id="6503257047630241175">RTF ձևաչափով բովանդակություն</translation>
<translation id="6539092367496845964">Սխալ առաջացավ: Փորձեք ավելի ուշ:</translation>
<translation id="654149438358937226">Արգելափակել բոլոր ծանուցումները</translation>
<translation id="6567071839949112727">սեղմեք նախնական օբյեկտի վրա</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_id.xtb b/chromium/ui/strings/translations/ui_strings_id.xtb
index 14fc6f4b7b7..dc6edeb5593 100644
--- a/chromium/ui/strings/translations/ui_strings_id.xtb
+++ b/chromium/ui/strings/translations/ui_strings_id.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">pilih</translation>
<translation id="1859234291848436338">Arah Penulisan</translation>
<translation id="1860796786778352021">Tutup pemberitahuan</translation>
+<translation id="186476001994626254">Konten Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Pilih semu&amp;a</translation>
+<translation id="19085484004813472">Ini adalah fitur baru</translation>
<translation id="2006524834898217237">Pastikan perangkat ini tersambung ke internet.</translation>
<translation id="208586643495776849">Coba lagi</translation>
<translation id="2090963878406559571">Untuk mengirim nomor dari <ph name="ORIGIN" /> ke ponsel Android Anda, <ph name="TROUBLESHOOT_LINK" /> untuk kedua perangkat di setelan.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Kelola setelan</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{dalam 1 jam}other{dalam # jam}}</translation>
<translation id="2583543531130364912">Kalibrasi layar sentuh</translation>
+<translation id="2586657967955657006">Papan klip</translation>
<translation id="2666092431469916601">Atas</translation>
<translation id="2701330563083355633">Dibagikan dari <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Baru</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Buka folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 tahun yang lalu}other{# tahun yang lalu}}</translation>
+<translation id="2878511608894704031">Hapus Semua</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{dalam 1 h}other{dalam # h}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Menampilkan saran</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Mulai Berbicara</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> dipindahkan ke folder <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">SEMUA APLIKASI</translation>
+<translation id="4491109536499578614">Gambar</translation>
<translation id="4588090240171750605">Gulir ke Kanan</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Rating bintang <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Rekomendasi aplikasi</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 hari dan }other{# hari dan }}</translation>
<translation id="5789643057113097023">:</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{dalam 1 t}other{dalam # t}}</translation>
+<translation id="588258955323874662">Layar Penuh</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 detik lagi}other{# detik lagi}}</translation>
<translation id="5941711191222866238">Perkecil</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 jam}other{# jam}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asisten Google</translation>
<translation id="6430678249303439055">Blokir semua notifikasi dari aplikasi ini</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 detik yang lalu}other{# detik yang lalu}}</translation>
+<translation id="6503257047630241175">Konten RTF</translation>
<translation id="6539092367496845964">Terjadi masalah. Coba lagi nanti.</translation>
<translation id="654149438358937226">Blokir semua notifikasi</translation>
<translation id="6567071839949112727">klik ancestor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_is.xtb b/chromium/ui/strings/translations/ui_strings_is.xtb
index 4947b167810..67ba5933a5d 100644
--- a/chromium/ui/strings/translations/ui_strings_is.xtb
+++ b/chromium/ui/strings/translations/ui_strings_is.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">velja</translation>
<translation id="1859234291848436338">Ritunarstefna</translation>
<translation id="1860796786778352021">Loka tilkynningu</translation>
+<translation id="186476001994626254">Snjalllíming efnis fyrir vefinn</translation>
<translation id="1871244248791675517">Insert</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Velja &amp;allt</translation>
+<translation id="19085484004813472">Þetta er nýr eiginleiki</translation>
<translation id="2006524834898217237">Gakktu úr skugga um að tækið sé tengt við internetið.</translation>
<translation id="208586643495776849">Reyndu aftur</translation>
<translation id="2090963878406559571">Til að senda númer frá <ph name="ORIGIN" /> í Android símann þinn skaltu <ph name="TROUBLESHOOT_LINK" /> fyrir bæði tækin í stillingunum.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Stjórna stillingum</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{eftir 1 klst.}one{eftir # klst.}other{eftir # klst.}}</translation>
<translation id="2583543531130364912">Kvarðaðu snertiskjáinn</translation>
+<translation id="2586657967955657006">Klippiborð</translation>
<translation id="2666092431469916601">Efst</translation>
<translation id="2701330563083355633">Deilt úr <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nýtt</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Opna möppu</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Fyrir einu ári}one{Fyrir # ári}other{Fyrir # árum}}</translation>
+<translation id="2878511608894704031">Eyða öllu</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{eftir einn dag}one{eftir # dag}other{eftir # daga}}</translation>
<translation id="2931838996092594335">smella</translation>
<translation id="2981684127883932071">Birtir tillögur</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Byrja að tala</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> var fært í möppuna <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ÖLL FORRIT</translation>
+<translation id="4491109536499578614">Mynd</translation>
<translation id="4588090240171750605">Fletta til hægri</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, stjörnur: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, tillaga að forriti</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dagur og }one{# dagur og }other{# dagar og }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{eftir eitt ár}one{eftir # ár}other{eftir # ár}}</translation>
+<translation id="588258955323874662">Allur skjárinn</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekúnda eftir}one{# sekúnda eftir}other{# sekúndur eftir}}</translation>
<translation id="5941711191222866238">Minnka</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 klukkustund}one{# klukkustund}other{# klukkustundir}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google hjálpari</translation>
<translation id="6430678249303439055">Loka á allar tilkynningar frá þessu forriti</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{fyrir einni sekúndu}one{fyrir # sekúndu}other{fyrir # sekúndum}}</translation>
+<translation id="6503257047630241175">RTF-efni</translation>
<translation id="6539092367496845964">Eitthvað fór úrskeiðis. Reyndu aftur síðar.</translation>
<translation id="654149438358937226">Loka á allar tilkynningar</translation>
<translation id="6567071839949112727">smella á forvera</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_it.xtb b/chromium/ui/strings/translations/ui_strings_it.xtb
index 68407d4adb4..87a2b2d7dd0 100644
--- a/chromium/ui/strings/translations/ui_strings_it.xtb
+++ b/chromium/ui/strings/translations/ui_strings_it.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seleziona</translation>
<translation id="1859234291848436338">Direzione di scrittura</translation>
<translation id="1860796786778352021">Chiusura notifica</translation>
+<translation id="186476001994626254">Contenuti di Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Seleziona &amp;tutto</translation>
+<translation id="19085484004813472">Questa funzionalità è nuova</translation>
<translation id="2006524834898217237">Assicurati che questo dispositivo sia connesso a Internet.</translation>
<translation id="208586643495776849">Riprova</translation>
<translation id="2090963878406559571">Per inviare un numero da <ph name="ORIGIN" /> al tuo telefono Android, <ph name="TROUBLESHOOT_LINK" /> per entrambi i dispositivi nelle impostazioni.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gestisci impostazioni</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{tra 1 h}other{tra # h}}</translation>
<translation id="2583543531130364912">Calibra il touchscreen</translation>
+<translation id="2586657967955657006">Appunti</translation>
<translation id="2666092431469916601">In alto</translation>
<translation id="2701330563083355633">Condivisione da <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nuovo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Apri cartella</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 anno fa}other{# anni fa}}</translation>
+<translation id="2878511608894704031">Elimina tutto</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{tra 1 g}other{tra # g}}</translation>
<translation id="2931838996092594335">fai clic</translation>
<translation id="2981684127883932071">Visualizzazione dei suggerimenti</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Avvia comandi vocali</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> spostata nella cartella <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TUTTE LE APP</translation>
+<translation id="4491109536499578614">Immagine</translation>
<translation id="4588090240171750605">Scorri a destra</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, valutazione a stelle <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, consiglio applicazione</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 giorno e }other{# giorni e }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{tra 1 a}other{tra # a}}</translation>
+<translation id="588258955323874662">Schermo intero</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 secondo rimanente}other{# secondi rimanenti}}</translation>
<translation id="5941711191222866238">Riduci a icona</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ora}other{# ore}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Assistente Google</translation>
<translation id="6430678249303439055">Blocca tutte le notifiche da questa app</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 secondo fa}other{# secondi fa}}</translation>
+<translation id="6503257047630241175">Contenuti RTF</translation>
<translation id="6539092367496845964">Si è verificato un problema. Riprova più tardi.</translation>
<translation id="654149438358937226">Blocca tutte le notifiche</translation>
<translation id="6567071839949112727">fai clic su predecessore</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Salva file</translation>
<translation id="6656912866303152668">Assicurati che in Chrome sia attiva la sincronizzazione di <ph name="TARGET_DEVICE_NAME" />, quindi riprova a inviare.</translation>
<translation id="6699343763173986273">Traccia successiva contenuti multimediali</translation>
-<translation id="6710213216561001401">Indietro</translation>
+<translation id="6710213216561001401">Precedente</translation>
<translation id="6779314412797872738">Per inviare un numero da qui al tuo telefono Android, <ph name="TROUBLESHOOT_LINK" /> per entrambi i dispositivi nelle impostazioni.</translation>
<translation id="6786750046913594791">Chiudi cartella</translation>
<translation id="6808150112686056157">Interrompi contenuti multimediali</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_iw.xtb b/chromium/ui/strings/translations/ui_strings_iw.xtb
index 01756aedac4..8de9a7a93d0 100644
--- a/chromium/ui/strings/translations/ui_strings_iw.xtb
+++ b/chromium/ui/strings/translations/ui_strings_iw.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">בחירה</translation>
<translation id="1859234291848436338">כיוון כתיבה</translation>
<translation id="1860796786778352021">סגירת הודעה</translation>
+<translation id="186476001994626254">‏תוכן מ-Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652">%<ph name="NUMBER" /></translation>
<translation id="1901303067676059328">בחר &amp;הכל</translation>
+<translation id="19085484004813472">זוהי תכונה חדשה</translation>
<translation id="2006524834898217237">יש לוודא שהמכשיר הזה מחובר לאינטרנט.</translation>
<translation id="208586643495776849">יש לנסות שוב</translation>
<translation id="2090963878406559571">‏כדי לשלוח מספר מ-<ph name="ORIGIN" /> לטלפון ה-Android שלך, צריך <ph name="TROUBLESHOOT_LINK" /> בהגדרות של שני המכשירים.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ניהול ההגדרות</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{עוד שעה}two{עוד 2 שעות}many{עוד # שעות}other{עוד # שעות}}</translation>
<translation id="2583543531130364912">כיול מסך המגע</translation>
+<translation id="2586657967955657006">לוח</translation>
<translation id="2666092431469916601">למעלה</translation>
<translation id="2701330563083355633">שותפה מהמכשיר <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">חדש</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />,‏ <ph name="PRICE" /></translation>
<translation id="2803313416453193357">פתח את התיקייה</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{לפני שנה אחת}two{לפני שנתיים}many{לפני # שנים}other{לפני # שנים}}</translation>
+<translation id="2878511608894704031">מחיקת הכול</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{עוד יום אחד}two{עוד 2 ימים}many{עוד # ימים}other{עוד # ימים}}</translation>
<translation id="2931838996092594335">לחץ</translation>
<translation id="2981684127883932071">מוצגות הצעות</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">התחל לדבר</translation>
<translation id="430191667033048642">האפליקציה <ph name="MOVED_APP_NAME" /> הועברה אל התיקייה <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">כל האפליקציות</translation>
+<translation id="4491109536499578614">תמונה</translation>
<translation id="4588090240171750605">גלול ימינה</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, דירוג כוכבים: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, המלצה על אפליקציה</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{יום אחד ו }two{יומיים ו }many{# ימים ו }other{# ימים ו }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{עוד שנה אחת}two{עוד 2 שנים}many{עוד # שנים}other{עוד # שנים}}</translation>
+<translation id="588258955323874662">מסך מלא</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{נותרה שנייה אחת}two{נותרו # שניות}many{נותרו # שניות}other{נותרו # שניות}}</translation>
-<translation id="5941711191222866238">מזער</translation>
+<translation id="5941711191222866238">מזעור</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{שעה אחת}two{שעתיים}many{# שעות}other{# שעות}}</translation>
<translation id="6012623610530968780">דף <ph name="SELECTED_PAGE" /> מתוך <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">ניקוי הטקסט בתיבת החיפוש</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">חסימת כל ההודעות מהאפליקציה הזו</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{לפני שנייה אחת}two{לפני # שניות}many{לפני # שניות}other{לפני # שניות}}</translation>
+<translation id="6503257047630241175">‏תוכן בפורמט RTF</translation>
<translation id="6539092367496845964">משהו השתבש. יש לנסות שוב מאוחר יותר.</translation>
<translation id="654149438358937226">חסימת כל ההודעות</translation>
<translation id="6567071839949112727">לחיצה על אב</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ja.xtb b/chromium/ui/strings/translations/ui_strings_ja.xtb
index 722f1fa07fe..8a433db15de 100644
--- a/chromium/ui/strings/translations/ui_strings_ja.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ja.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">選択</translation>
<translation id="1859234291848436338">文章の方向</translation>
<translation id="1860796786778352021">通知を閉じる</translation>
+<translation id="186476001994626254">ウェブのスマート貼り付けコンテンツ</translation>
<translation id="1871244248791675517">Insert</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">すべて選択(&amp;A)</translation>
+<translation id="19085484004813472">これは新しい機能です</translation>
<translation id="2006524834898217237">このデバイスがインターネットに接続されているかご確認ください。</translation>
<translation id="208586643495776849">もう一度お試しください</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> から Android スマートフォンに番号を送信するには、設定で両方のデバイスの<ph name="TROUBLESHOOT_LINK" />にしてください。</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">設定を管理</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1時間}other{#時間}}</translation>
<translation id="2583543531130364912">タッチスクリーンのキャリブレーション</translation>
+<translation id="2586657967955657006">クリップボード</translation>
<translation id="2666092431469916601">一番上</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> から共有しました</translation>
<translation id="271033894570825754">新しいファイル</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />、<ph name="PRICE" /></translation>
<translation id="2803313416453193357">フォルダを開く</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 年前}other{# 年前}}</translation>
+<translation id="2878511608894704031">すべて削除</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1日}other{#日}}</translation>
<translation id="2931838996092594335">クリック</translation>
<translation id="2981684127883932071">候補を表示しています</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">読み上げを開始</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> をフォルダ <ph name="FOLDER_NAME" /> に移動しました。</translation>
<translation id="4316910396681052118">すべてのアプリ</translation>
+<translation id="4491109536499578614">画像</translation>
<translation id="4588090240171750605">右にスクロール</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />、評価 <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />、おすすめのアプリ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 日と }other{# 日と }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1年}other{#年}}</translation>
+<translation id="588258955323874662">全画面表示</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{残り 1 秒}other{残り # 秒}}</translation>
<translation id="5941711191222866238">最小化</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 時間}other{# 時間}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google アシスタント</translation>
<translation id="6430678249303439055">このアプリからのすべての通知をブロックする</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 秒前}other{# 秒前}}</translation>
+<translation id="6503257047630241175">RTF コンテンツ</translation>
<translation id="6539092367496845964">エラーが発生しました。しばらくしてからもう一度お試しください。</translation>
<translation id="654149438358937226">通知をすべてブロックする</translation>
<translation id="6567071839949112727">上位要素をクリック</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ka.xtb b/chromium/ui/strings/translations/ui_strings_ka.xtb
index d2070179304..855d7b90b86 100644
--- a/chromium/ui/strings/translations/ui_strings_ka.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ka.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">არჩევა</translation>
<translation id="1859234291848436338">წერის მიმართულება</translation>
<translation id="1860796786778352021">შეტყობინების დახურვა</translation>
+<translation id="186476001994626254">Web Smart Paste კონტენტი</translation>
<translation id="1871244248791675517">ჩასმა</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">&amp;ყველას არჩევა</translation>
+<translation id="19085484004813472">ეს ახალი ფუნქციაა</translation>
<translation id="2006524834898217237">დარწმუნდით, რომ ეს მოწყობილობა დაკავშირებულია ინტერნეტთან.</translation>
<translation id="208586643495776849">გთხოვთ, ცადოთ ხელახლა</translation>
<translation id="2090963878406559571">ტელეფონის ნომრის <ph name="ORIGIN" />-დან თქვენს Android ტელეფონზე გასაგზავნად საჭიროა, პარამეტრებიდან ორივე მოწყობილობისთვის <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">პარამეტრების მართვა</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 სთ-ში}other{# სთ-ში}}</translation>
<translation id="2583543531130364912">განახორციელეთ თქვენი სენსორული ეკრანის კალიბრაცია</translation>
+<translation id="2586657967955657006">გაცვლის ბუფერი</translation>
<translation id="2666092431469916601">ზედა</translation>
<translation id="2701330563083355633">გაზიარებულია <ph name="DEVICE_NAME" />-დან</translation>
<translation id="271033894570825754">ახალი</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">გახსენით საქაღალდე</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 წლის წინ}other{# წლის წინ}}</translation>
+<translation id="2878511608894704031">ყველაფრის წაშლა</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 დღეში}other{# დღეში}}</translation>
<translation id="2931838996092594335">დაწკაპუნება</translation>
<translation id="2981684127883932071">ნაჩვენებია შემოთავაზებები</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">დაიწყეთ ლაპარაკი</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> გადატანილია საქაღალდეში <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ყველა აპი</translation>
+<translation id="4491109536499578614">გამოსახულება</translation>
<translation id="4588090240171750605">გადაადგილება მარჯვნივ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, შეფასებულია <ph name="RATING_SCORE" /> ვარსკვლავით</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, აპის რეკომენდაცია</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 დღე და }other{# დღე და }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 წ.-ში}other{# წ.-ში}}</translation>
+<translation id="588258955323874662">სრული ეკრანი</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{დარჩენილია 1 წამი}other{დარჩენილია # წამი}}</translation>
<translation id="5941711191222866238">ჩაკეცვა</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 საათი}other{# საათი}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google ასისტენტი</translation>
<translation id="6430678249303439055">ამ აპის ყველა შეტყობინების დაბლოკვა</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 წამის წინ}other{# წამის წინ}}</translation>
+<translation id="6503257047630241175">RTF კონტენტი</translation>
<translation id="6539092367496845964">წარმოიქმნა შეფერხება. ცადეთ მოგვიანებით.</translation>
<translation id="654149438358937226">ყველა შეტყობინების დაბლოკვა</translation>
<translation id="6567071839949112727">დაწკაპუნება წინამორბედზე</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_kk.xtb b/chromium/ui/strings/translations/ui_strings_kk.xtb
index a9629a05813..6df6e142f6d 100644
--- a/chromium/ui/strings/translations/ui_strings_kk.xtb
+++ b/chromium/ui/strings/translations/ui_strings_kk.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">таңдау</translation>
<translation id="1859234291848436338">Жазу бағыты</translation>
<translation id="1860796786778352021">Хабарландыруды жабу</translation>
+<translation id="186476001994626254">Web Smart Paste мазмұны</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Барлығын &amp;таңдау</translation>
+<translation id="19085484004813472">Бұл – жаңа функция.</translation>
<translation id="2006524834898217237">Бұл құрылғы интернетке қосулы екеніне көз жеткізіңіз.</translation>
<translation id="208586643495776849">Қайталап көріңіз</translation>
<translation id="2090963878406559571">Нөмірді <ph name="ORIGIN" /> сайтынан Android телефонына жібергіңіз келсе, екі құрылғы үшін де параметрлерден <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Параметрлерді басқару</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 сағатта}other{# сағатта}}</translation>
<translation id="2583543531130364912">Сенсорлық экранды калибрлеңіз</translation>
+<translation id="2586657967955657006">Буфер</translation>
<translation id="2666092431469916601">Жоғары</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> құрылғысынан бөлісілді.</translation>
<translation id="271033894570825754">Жаңа</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Қалтаны ашу</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 жыл бұрын}other{# жыл бұрын}}</translation>
+<translation id="2878511608894704031">Барлығын жою</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 күнде}other{# күнде}}</translation>
<translation id="2931838996092594335">басады</translation>
<translation id="2981684127883932071">Ұсыныстарды көрсету</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Сөйлеуді бастау</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> қолданбасы <ph name="FOLDER_NAME" /> қалтасына ауысты.</translation>
<translation id="4316910396681052118">БАРЛЫҚ ҚОЛДАНБАЛАР</translation>
+<translation id="4491109536499578614">Кескін</translation>
<translation id="4588090240171750605">Оңға айналдыру</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, рейтингісі: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, қолданба ұсынысы</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 күн және }other{# күн және }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 жылда}other{# жылда}}</translation>
+<translation id="588258955323874662">Толық экран</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 секунд қалды}other{# секунд қалды}}</translation>
<translation id="5941711191222866238">Жию</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 сағат}other{# сағат}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">Бұл қолданбадан келетін барлық хабарландыруларды бөгеу</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 секунд бұрын}other{# секунд бұрын}}</translation>
+<translation id="6503257047630241175">RTF мазмұны</translation>
<translation id="6539092367496845964">Бірдеңе дұрыс болмады. Кейінірек қайталап көріңіз.</translation>
<translation id="654149438358937226">Барлық хабарландыруларды бөгеу</translation>
<translation id="6567071839949112727">жоғары деңгейлі элементті басу</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_km.xtb b/chromium/ui/strings/translations/ui_strings_km.xtb
index 75f5e85eaf2..003133b448b 100644
--- a/chromium/ui/strings/translations/ui_strings_km.xtb
+++ b/chromium/ui/strings/translations/ui_strings_km.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ជ្រើសរើស</translation>
<translation id="1859234291848436338">ទិសដៅការសរសេរ</translation>
<translation id="1860796786778352021">បិទការជូនដំណឹង</translation>
+<translation id="186476001994626254">ខ្លឹមសារពី Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">ជ្រើសរើសទាំងអស់</translation>
+<translation id="19085484004813472">នេះគឺជាមុខងារថ្មី</translation>
<translation id="2006524834898217237">ត្រូវប្រាកដថា ឧបករណ៍នេះមានការតភ្ជាប់អ៊ីនធឺណិត។</translation>
<translation id="208586643495776849">សូម​ព្យាយាម​ម្ដងទៀត</translation>
<translation id="2090963878406559571">ដើម្បី​ផ្ញើ​លេខទូរសព្ទ​ពី <ph name="ORIGIN" /> ទៅ​ទូរសព្ទ Android របស់អ្នក សូម <ph name="TROUBLESHOOT_LINK" /> សម្រាប់​ឧបករណ៍​ទាំងពីរ​នៅក្នុង​ការកំណត់​។</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">គ្រប់គ្រង​ការកំណត់</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 ម៉ោងទៀត}other{# ម៉ោងទៀត}}</translation>
<translation id="2583543531130364912">ក្រិត​អេក្រង់​ចុច​របស់​អ្នក</translation>
+<translation id="2586657967955657006">អង្គ​ចងចាំ</translation>
<translation id="2666092431469916601">ខាងលើ</translation>
<translation id="2701330563083355633">បាន​ចែករំលែក​ពី <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">ថ្មី</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">បើកថតឯកសារ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ឆ្នាំមុន}other{# ឆ្នាំមុន}}</translation>
+<translation id="2878511608894704031">លុបទាំងអស់</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 ថ្ងៃទៀត}other{# ថ្ងៃទៀត}}</translation>
<translation id="2931838996092594335">ចុច</translation>
<translation id="2981684127883932071">កំពុងបង្ហាញ​ការណែនាំ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">ចាប់ផ្តើមនិយាយ</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ត្រូវបាន​ផ្លាស់ទី​ទៅថត <ph name="FOLDER_NAME" />។</translation>
<translation id="4316910396681052118">កម្មវិធី​ទាំងអស់</translation>
+<translation id="4491109536499578614">រូបភាព</translation>
<translation id="4588090240171750605">ទាញចុះទៅខាងស្តាំ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ការវាយតម្លៃ​ដោយដាក់​ផ្កាយ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ការណែនាំ​កម្មវិធី</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ថ្ងៃ និង }other{# ថ្ងៃ និង }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 ឆ្នាំទៀត}other{# ឆ្នាំទៀត}}</translation>
+<translation id="588258955323874662">ពេញអេក្រង់</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{នៅសល់ 1 វិនាទីទៀត}other{នៅសល់ # វិនាទីទៀត}}</translation>
<translation id="5941711191222866238">បង្រួម</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ម៉ោង}other{# ម៉ោង}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google ជំនួយការ</translation>
<translation id="6430678249303439055">ទប់ស្កាត់​ការជូនដំណឹង​ទាំងអស់​ពីកម្មវិធី​នេះ</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 វិនាទី​មុន}other{# វិនាទី​មុន}}</translation>
+<translation id="6503257047630241175">ខ្លឹមសារ RTF</translation>
<translation id="6539092367496845964">មានអ្វីមួយខុសប្រក្រតី។ សូមព្យាយាមម្តង​ទៀតនៅ​ពេលក្រោយ។</translation>
<translation id="654149438358937226">ទប់ស្កាត់ការជូនដំណឹងទាំងអស់</translation>
<translation id="6567071839949112727">ចុច​ធាតុ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_kn.xtb b/chromium/ui/strings/translations/ui_strings_kn.xtb
index 23e9dbc4eea..b5ade6047d4 100644
--- a/chromium/ui/strings/translations/ui_strings_kn.xtb
+++ b/chromium/ui/strings/translations/ui_strings_kn.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ಆಯ್ಕೆ ಮಾಡಿ</translation>
<translation id="1859234291848436338">ಬರವಣಿಗೆ ನಿರ್ದೇಶನ</translation>
<translation id="1860796786778352021">ಅಧಿಸೂಚನೆ ಮುಚ್ಚು</translation>
+<translation id="186476001994626254">ವೆಬ್ ಸ್ಮಾರ್ಟ್ ಅಂಟಿಸುವ ವಿಷಯ</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;ಎಲ್ಲ ಆಯ್ಕೆ ಮಾಡಿ</translation>
+<translation id="19085484004813472">ಇದೊಂದು ಹೊಸ ವೈಶಿಷ್ಟ್ಯ</translation>
<translation id="2006524834898217237">ಈ ಸಾಧನವು ಇಂಟರ್ನೆಟ್‌ಗೆ ಸಂಪರ್ಕಗೊಂಡಿರುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ.</translation>
<translation id="208586643495776849">ಪುನಃ ಪ್ರಯತ್ನಿಸಿ</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> ನಿಂದ ನಿಮ್ಮ Android ಫೋನ್‌ಗೆ ಒಂದು ಸಂಖ್ಯೆಯನ್ನು ಕಳುಹಿಸಲು, ಎರಡೂ ಸಾಧನಗಳಿಗಾಗಿ, ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1ಗಂ. ಯಲ್ಲಿ}one{#ಗಂ. ಗಳಲ್ಲಿ}other{#ಗಂ. ಗಳಲ್ಲಿ}}</translation>
<translation id="2583543531130364912">ನಿಮ್ಮ ಟಚ್‌ಸ್ಕ್ರೀನ್ ಕ್ಯಾಲಿಬ್ರೇಟ್ ಮಾಡಿ</translation>
+<translation id="2586657967955657006">ಕ್ಲಿಪ್‌ಬೋರ್ಡ್</translation>
<translation id="2666092431469916601">ಮೇಲೆ</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> ನಿಂದ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ</translation>
<translation id="271033894570825754">ಹೊಸತು</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ಫೋಲ್ಡರ್ ತೆರೆಯಿರಿ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ವರ್ಷದ ಹಿಂದೆ}one{# ವರ್ಷಗಳ ಹಿಂದೆ}other{# ವರ್ಷಗಳ ಹಿಂದೆ}}</translation>
+<translation id="2878511608894704031">ಎಲ್ಲವನ್ನು ಅಳಿಸಿ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1ದಿ. ದಲ್ಲಿ}one{#ದಿ. ಗಳಲ್ಲಿ}other{#ದಿ. ಗಳಲ್ಲಿ}}</translation>
<translation id="2931838996092594335">ಕ್ಲಿಕ್‌ ಮಾಡಿ</translation>
<translation id="2981684127883932071">ಸಲಹೆಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತಿದೆ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">ಮಾತನಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸಿ</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ಆ್ಯಪ್ ಅನ್ನು <ph name="FOLDER_NAME" /> ಫೋಲ್ಡರ್‌ಗೆ ಸರಿಸಲಾಗಿದೆ.</translation>
<translation id="4316910396681052118">ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು</translation>
+<translation id="4491109536499578614">ಚಿತ್ರ</translation>
<translation id="4588090240171750605">ಬಲಕ್ಕೆ ಸ್ಕ್ರೋಲ್ ಮಾಡಿ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ಸ್ಟಾರ್ ರೇಟಿಂಗ್ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ಆ್ಯಪ್ ಶಿಫಾರಸು</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ದಿನ ಮತ್ತು }one{# ದಿನಗಳು ಮತ್ತು }other{# ದಿನಗಳು ಮತ್ತು }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1ವ. ದಲ್ಲಿ}one{#ವ. ಗಳಲ್ಲಿ}other{#ವ. ಗಳಲ್ಲಿ}}</translation>
+<translation id="588258955323874662">ಪೂರ್ಣಪರದೆ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 ಸೆಕೆಂಡ್ ಬಾಕಿ ಉಳಿದಿದೆ}one{# ಸೆಕೆಂಡುಗಳು ಬಾಕಿ ಉಳಿದಿವೆ}other{# ಸೆಕೆಂಡುಗಳು ಬಾಕಿ ಉಳಿದಿವೆ}}</translation>
-<translation id="5941711191222866238">ಕುಗ್ಗಿಸು</translation>
+<translation id="5941711191222866238">ಕುಗ್ಗಿಸಿ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}</translation>
<translation id="6012623610530968780">ಪುಟ <ph name="SELECTED_PAGE" /> ರಲ್ಲಿ <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">ಹುಡುಕಾಟ ಬಾಕ್ಸ್ ಪಠ್ಯವನ್ನು ತೆರವುಗೊಳಿಸಿ</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ಈ ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 ಸೆಕೆಂಡ್ ಹಿಂದೆ}one{# ಸೆಕೆಂಡುಗಳ ಹಿಂದೆ}other{# ಸೆಕೆಂಡುಗಳ ಹಿಂದೆ}}</translation>
+<translation id="6503257047630241175">RTF ವಿಷಯ</translation>
<translation id="6539092367496845964">ಏನೋ ತಪ್ಪಾಗಿದೆ. ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="654149438358937226">ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ</translation>
<translation id="6567071839949112727">ಪೂರ್ವಜರನ್ನು ಕ್ಲಿಕ್ ಮಾಡಿ</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">ಫೈಲ್ ಉಳಿಸು</translation>
<translation id="6656912866303152668">Chrome ನಲ್ಲಿ <ph name="TARGET_DEVICE_NAME" /> ಸಾಧನದ ಸಿಂಕ್ ಆನ್ ಆಗಿದೆಯೇ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ, ತದನಂತರ ಮತ್ತೆ ಕಳುಹಿಸಲು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="6699343763173986273">ಮೀಡಿಯಾದ ಮುಂದಿನ ಟ್ರ್ಯಾಕ್</translation>
-<translation id="6710213216561001401">ಹಿಂದೆ</translation>
+<translation id="6710213216561001401">ಹಿಂದಿನದು</translation>
<translation id="6779314412797872738">ಇಲ್ಲಿಂದ ನಿಮ್ಮ Android ಫೋನ್‌ಗೆ ಒಂದು ಸಂಖ್ಯೆಯನ್ನು ಕಳುಹಿಸಲು, ಎರಡೂ ಸಾಧನಗಳಿಗಾಗಿ, ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ <ph name="TROUBLESHOOT_LINK" />.</translation>
<translation id="6786750046913594791">ಫೋಲ್ಡರ್ ಮುಚ್ಚಿರಿ</translation>
<translation id="6808150112686056157">ಮೀಡಿಯಾ ನಿಲುಗಡೆ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ko.xtb b/chromium/ui/strings/translations/ui_strings_ko.xtb
index 4b9ecdfb2a7..3d25716f48e 100644
--- a/chromium/ui/strings/translations/ui_strings_ko.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ko.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">선택</translation>
<translation id="1859234291848436338">쓰기 방향</translation>
<translation id="1860796786778352021">알림 닫기</translation>
+<translation id="186476001994626254">웹 스마트 붙여넣기 콘텐츠</translation>
<translation id="1871244248791675517">Insert</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">전체 선택(&amp;A)</translation>
+<translation id="19085484004813472">새로운 기능입니다.</translation>
<translation id="2006524834898217237">기기가 인터넷에 연결되어 있는지 확인하세요.</translation>
<translation id="208586643495776849">다시 시도해 주세요.</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" />에서 Android 휴대전화로 번호를 전송하려면 설정에서 두 기기 모두의 <ph name="TROUBLESHOOT_LINK" />하세요.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">설정 관리하기</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1시간 후}other{#시간 후}}</translation>
<translation id="2583543531130364912">터치스크린 보정</translation>
+<translation id="2586657967955657006">클립보드</translation>
<translation id="2666092431469916601">맨 위</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" />에서 공유함</translation>
<translation id="271033894570825754">새 파일</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">폴더 열기</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1년 전}other{#년 전}}</translation>
+<translation id="2878511608894704031">모두 삭제</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1일 후}other{#일 후}}</translation>
<translation id="2931838996092594335">클릭</translation>
<translation id="2981684127883932071">추천 표시 중</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">말하기 시작</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> 앱이 <ph name="FOLDER_NAME" /> 폴더로 이동했습니다.</translation>
<translation id="4316910396681052118">모든 앱</translation>
+<translation id="4491109536499578614">이미지</translation>
<translation id="4588090240171750605">오른쪽 스크롤</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, 별표 평점 <ph name="RATING_SCORE" />점</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, 앱 추천</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1일 }other{#일 }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1년 후}other{#년 후}}</translation>
+<translation id="588258955323874662">전체화면</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1초 남음}other{#초 남음}}</translation>
<translation id="5941711191222866238">최소화</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1시간}other{#시간}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google 어시스턴트</translation>
<translation id="6430678249303439055">이 앱의 모든 알림 차단</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1초 전}other{#초 전}}</translation>
+<translation id="6503257047630241175">RTF 콘텐츠</translation>
<translation id="6539092367496845964">문제가 발생했습니다. 나중에 다시 시도해 보세요.</translation>
<translation id="654149438358937226">모든 알림 차단</translation>
<translation id="6567071839949112727">상위 개체 클릭</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ky.xtb b/chromium/ui/strings/translations/ui_strings_ky.xtb
index a76448cb702..7b7b74cbfb0 100644
--- a/chromium/ui/strings/translations/ui_strings_ky.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ky.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">тандоо</translation>
<translation id="1859234291848436338">Жазуу багыты</translation>
<translation id="1860796786778352021">Эскертмени жабуу</translation>
+<translation id="186476001994626254">Вебдеги мазмунду акылдуу чаптоо</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;Баарын тандоо</translation>
+<translation id="19085484004813472">Бул жаңы функция</translation>
<translation id="2006524834898217237">Бул түзмөк интернетке туташканын текшериңиз.</translation>
<translation id="208586643495776849">Кайталап көрүңүз</translation>
<translation id="2090963878406559571">Номерди <ph name="ORIGIN" /> сайтынан Android телефонуңузга жөнөтүү үчүн, жөндөөлөрдөн эки түзмөк үчүн тең <ph name="TROUBLESHOOT_LINK" /> керек.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Жөндөөлөрдү башкаруу</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 с. кийин}other{# с. кийин}}</translation>
<translation id="2583543531130364912">Сенсордук экраныңызды калибрлеп алыңыз</translation>
+<translation id="2586657967955657006">Алмашуу буфери</translation>
<translation id="2666092431469916601">Өйдө</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> аркылуу бөлүшүлдү</translation>
<translation id="271033894570825754">Жаңы</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Куржунду ачуу</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 жыл мурун}other{# жыл мурун}}</translation>
+<translation id="2878511608894704031">Баарын жок кылуу</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 к. кийин}other{# к. кийин}}</translation>
<translation id="2931838996092594335">чыкылдатуу</translation>
<translation id="2981684127883932071">Сунуштар көрсөтүлүүдө</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Сүйлөп баштаңыз</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> <ph name="FOLDER_NAME" /> папкасына жылдырылды.</translation>
<translation id="4316910396681052118">БАРДЫК КОЛДОНМОЛОР</translation>
+<translation id="4491109536499578614">Сүрөт</translation>
<translation id="4588090240171750605">Оңго сыдырып кароо</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Рейтинги: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Сунушталган колдонмо</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 күн жана }other{# күн жана }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 ж. кийин}other{# ж. кийин}}</translation>
+<translation id="588258955323874662">Толук экран</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 секунд калды}other{# секунд калды}}</translation>
<translation id="5941711191222866238">Кичирейтүү</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 саат}other{# саат}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Жардамчы</translation>
<translation id="6430678249303439055">Бул колдонмодон бардык билдирмелер бөгөттөлсүн</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 секунд мурун}other{# секунд мурун}}</translation>
+<translation id="6503257047630241175">RTF мазмуну</translation>
<translation id="6539092367496845964">Бир жерден ката кетти. Бир аздан кийин кайталап көрүңүз.</translation>
<translation id="654149438358937226">Бардык билдирмелерди бөгөттөө</translation>
<translation id="6567071839949112727">чыкылдатуу</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_lo.xtb b/chromium/ui/strings/translations/ui_strings_lo.xtb
index f124f948571..c2fd0f0cb4c 100644
--- a/chromium/ui/strings/translations/ui_strings_lo.xtb
+++ b/chromium/ui/strings/translations/ui_strings_lo.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ເລືອກ</translation>
<translation id="1859234291848436338">ທິດທາງການຂຽນ</translation>
<translation id="1860796786778352021">ປິດການແຈ້ງເຕືອນ</translation>
+<translation id="186476001994626254">ການວາງເນື້ອຫາອັດສະລິຍະໃນເວັບ</translation>
<translation id="1871244248791675517">ແຊກເຂົ້າ</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">ເລືອກ​ທັງ​ຫມົດ</translation>
+<translation id="19085484004813472">ນີ້ແມ່ນຄຸນສົມບັດໃໝ່</translation>
<translation id="2006524834898217237">ກວດໃຫ້ແນ່ໃຈວ່າອຸປະກອນນີ້ເຊື່ອມຕໍ່ກັບອິນເຕີເນັດ.</translation>
<translation id="208586643495776849">ກະລຸນາລອງໃໝ່</translation>
<translation id="2090963878406559571">ບໍ່ສາມາດສົ່ງເບີຈາກ <ph name="ORIGIN" /> ຫາໂທລະສັບ Android ຂອງທ່ານໄດ້, <ph name="TROUBLESHOOT_LINK" /> ສຳລັບທັງສອງອຸປະກອນໃນການຕັ້ງຄ່າ.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ຈັດການການຕັ້ງຄ່າ</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{ໃນ 1 ຊມ}other{ໃນ # ຊມ}}</translation>
<translation id="2583543531130364912">ປັບຕັ້ງຄ່າໜ້າຈໍສຳຜັດຂອງທ່ານ</translation>
+<translation id="2586657967955657006">ຄລິບບອດ</translation>
<translation id="2666092431469916601">ດ້ານເທິງ</translation>
<translation id="2701330563083355633">ແບ່ງປັນຈາກ <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">ໃຫມ່</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ເປີດ​ໂຟລເດີ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ປີກ່ອນນີ້}other{# ປີກ່ອນນີ້}}</translation>
+<translation id="2878511608894704031">ລຶບທັງໝົດ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{ໃນ 1 ມື້}other{ໃນ # ມື້}}</translation>
<translation id="2931838996092594335">ຄລິກ</translation>
<translation id="2981684127883932071">ກຳລັງສະແດງການແນະນຳ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">ເລີ່ມ​ຕົ້ນ​ເວົ້າ</translation>
<translation id="430191667033048642">ຍ້າຍ <ph name="MOVED_APP_NAME" /> ໄປຫາໂຟນເດີ <ph name="FOLDER_NAME" /> ແລ້ວ.</translation>
<translation id="4316910396681052118">ທຸກແອັບ</translation>
+<translation id="4491109536499578614">ຮູບ​</translation>
<translation id="4588090240171750605">ເລື່ອນໄປທາງຂວາ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ການໃຫ້ຄະແນນເປັນດາວ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ການແນະນຳແອັບ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ວັນ​ ແລະ }other{# ວັນ ແລະ }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{ໃນ 1 ປີ}other{ໃນ # ປີ}}</translation>
+<translation id="588258955323874662">ເຕັມໜ້າຈໍ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{ຍັງ​ເຫຼືອ 1 ວິ​ນາ​ທີ}other{ຍັງ​ເຫຼືອ # ວິ​ນາ​ທີ}}</translation>
<translation id="5941711191222866238">ຫຍໍ້ລົງ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ຊົ່ວ​ໂມງ}other{# ຊົ່ວ​ໂມງ}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">ຜູ້ຊ່ວຍ Google</translation>
<translation id="6430678249303439055">ບລັອກທຸກການແຈ້ງເຕືອນຈາກແອັບນີ້</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 ວິນາທີກ່ອນນີ້}other{# ວິນາທີກ່ອນນີ້}}</translation>
+<translation id="6503257047630241175">ເນື້ອຫາ RTF</translation>
<translation id="6539092367496845964">ມີບາງຢ່າງຜິດພາດ. ລອງໃໝ່ໃນພາຍຫລັງ.</translation>
<translation id="654149438358937226">ບລັອກທຸກການແຈ້ງເຕືອນ</translation>
<translation id="6567071839949112727">ຄລິກອັນກ່ອນນີ້</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_lt.xtb b/chromium/ui/strings/translations/ui_strings_lt.xtb
index 490295e3316..049539bbed5 100644
--- a/chromium/ui/strings/translations/ui_strings_lt.xtb
+++ b/chromium/ui/strings/translations/ui_strings_lt.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">pasirinkti</translation>
<translation id="1859234291848436338">Rašymo nurodymas</translation>
<translation id="1860796786778352021">Uždaryti pranešimus</translation>
+<translation id="186476001994626254">„Web Smart Paste“ turinys</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Pasirinkti &amp;viską</translation>
+<translation id="19085484004813472">Tai nauja funkcija</translation>
<translation id="2006524834898217237">Įsitikinkite, kad šis įrenginys prijungtas prie interneto.</translation>
<translation id="208586643495776849">Bandykite dar kartą</translation>
<translation id="2090963878406559571">Jei norite iš <ph name="ORIGIN" /> į „Android“ telefoną išsiųsti numerį, <ph name="TROUBLESHOOT_LINK" /> nustatymuose abiejuose įrenginiuose.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Tvarkyti nustatymus</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{po 1 val.}one{po # val.}few{po # val.}many{po # val.}other{po # val.}}</translation>
<translation id="2583543531130364912">Jutiklinio ekrano kalibravimas</translation>
+<translation id="2586657967955657006">Iškarpinė</translation>
<translation id="2666092431469916601">Į viršų</translation>
<translation id="2701330563083355633">Bendrinta iš „<ph name="DEVICE_NAME" />“</translation>
<translation id="271033894570825754">Naujiena</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Atidaryti aplanką</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Prieš 1 metus}one{Prieš # metus}few{Prieš # metus}many{Prieš # metų}other{Prieš # metų}}</translation>
+<translation id="2878511608894704031">Ištrinti viską</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{po 1 d.}one{po # d.}few{po # d.}many{po # d.}other{po # d.}}</translation>
<translation id="2931838996092594335">spustelėti</translation>
<translation id="2981684127883932071">Pateikiami pasiūlymai</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Pradėti kalbėti</translation>
<translation id="430191667033048642">„<ph name="MOVED_APP_NAME" />“ perkelta į aplanką „<ph name="FOLDER_NAME" />“.</translation>
<translation id="4316910396681052118">VISOS PROGRAMOS</translation>
+<translation id="4491109536499578614">Vaizdas</translation>
<translation id="4588090240171750605">Slinkti į dešinę</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, įvertinimas žvaigždutėmis <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485">„<ph name="APP_NAME" />“, programos rekomendacija</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 diena ir }one{# diena ir }few{# dienos ir }many{# dienos ir }other{# dienų ir }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{po 1 m.}one{po # m.}few{po # m.}many{po # m.}other{po # m.}}</translation>
+<translation id="588258955323874662">Visas ekranas</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Liko 1 sekundė}one{Liko # sekundė}few{Liko # sekundės}many{Liko # sekundės}other{Liko # sekundžių}}</translation>
<translation id="5941711191222866238">Sumažinti</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">„Google“ padėjėjas</translation>
<translation id="6430678249303439055">Blokuoti visus šios programos pranešimus</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Prieš 1 sekundę}one{Prieš # sekundę}few{Prieš # sekundes}many{Prieš # sekundės}other{Prieš # sekundžių}}</translation>
+<translation id="6503257047630241175">RTF turinys</translation>
<translation id="6539092367496845964">Įvyko klaida. Vėliau bandykite dar kartą.</translation>
<translation id="654149438358937226">Blokuoti visus pranešimus</translation>
<translation id="6567071839949112727">spustelėti aukštesnio lygmens elementą</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Išsaugoti failą</translation>
<translation id="6656912866303152668">Įsitikinkite, kad įrenginio „<ph name="TARGET_DEVICE_NAME" />“ naršyklėje „Chrome“ įjungtas sinchronizavimas, tada bandykite siųsti dar kartą.</translation>
<translation id="6699343763173986273">Kitas medijos takelis</translation>
-<translation id="6710213216561001401">Ankstesnis</translation>
+<translation id="6710213216561001401">Ankstesnė</translation>
<translation id="6779314412797872738">Jei norite iš čia į „Android“ telefoną išsiųsti numerį, nustatymuose<ph name="TROUBLESHOOT_LINK" /> abiejuose įrenginiuose.</translation>
<translation id="6786750046913594791">Uždaryti aplanką</translation>
<translation id="6808150112686056157">Sustabdyti mediją</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_lv.xtb b/chromium/ui/strings/translations/ui_strings_lv.xtb
index 9e0ab437246..2786cc61b1a 100644
--- a/chromium/ui/strings/translations/ui_strings_lv.xtb
+++ b/chromium/ui/strings/translations/ui_strings_lv.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">Atlasiet</translation>
<translation id="1859234291848436338">Rakstīšanas virziens</translation>
<translation id="1860796786778352021">Paziņojuma aizvēršana</translation>
+<translation id="186476001994626254">Web Smart Paste saturs</translation>
<translation id="1871244248791675517">Iespraušana</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Izvēlēties visus</translation>
+<translation id="19085484004813472">Šī ir jauna funkcija</translation>
<translation id="2006524834898217237">Pārbaudiet, vai šajā ierīcē ir izveidots savienojums ar internetu.</translation>
<translation id="208586643495776849">Lūdzu, mēģiniet vēlreiz</translation>
<translation id="2090963878406559571">Lai no vietnes <ph name="ORIGIN" /> uz Android tālruni nosūtītu numuru, <ph name="TROUBLESHOOT_LINK" /> abām iestatījumos esošajām ierīcēm.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Pārvaldīt iestatījumus</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{pēc 1 h}zero{pēc # h}one{pēc # h}other{pēc # h}}</translation>
<translation id="2583543531130364912">Skārienekrāna kalibrēšana</translation>
+<translation id="2586657967955657006">Starpliktuve</translation>
<translation id="2666092431469916601">Augša</translation>
<translation id="2701330563083355633">Kopīgota no: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Jauns</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Atvērt mapi</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Pirms 1 gada}zero{Pirms # gadiem}one{Pirms # gada}other{Pirms # gadiem}}</translation>
+<translation id="2878511608894704031">Dzēst visu</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{pēc 1 d.}zero{pēc # d.}one{pēc # d.}other{pēc # d.}}</translation>
<translation id="2931838996092594335">noklikšķināt</translation>
<translation id="2981684127883932071">Tiek rādīti ieteikumi</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Sākt runāt</translation>
<translation id="430191667033048642">Lietotne <ph name="MOVED_APP_NAME" /> pārvietota uz mapi <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">VISAS LIETOTNES</translation>
+<translation id="4491109536499578614">Attēls</translation>
<translation id="4588090240171750605">Ritināt pa labi</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, vērtējums zvaigznītēs: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, lietotņu ieteikumi</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 diena un }zero{# dienas un }one{# diena un }other{# dienas un }}</translation>
<translation id="5789643057113097023">:</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{pēc 1 g.}zero{pēc # g.}one{pēc # g.}other{pēc # g.}}</translation>
+<translation id="588258955323874662">Pilnekrāna</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Atlikusi 1 sekunde}zero{Atlikušas # sekundes}one{Atlikusi # sekunde}other{Atlikušas # sekundes}}</translation>
<translation id="5941711191222866238">Minimizēt</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 stunda}zero{# stundas}one{# stunda}other{# stundas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google asistents</translation>
<translation id="6430678249303439055">Bloķēt visus paziņojumus no šīs lietotnes</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Pirms 1 sekundes}zero{Pirms # sekundēm}one{Pirms # sekundes}other{Pirms # sekundēm}}</translation>
+<translation id="6503257047630241175">RTF saturs</translation>
<translation id="6539092367496845964">Radās kļūda. Vēlāk mēģiniet vēlreiz.</translation>
<translation id="654149438358937226">Bloķēt visus paziņojumus</translation>
<translation id="6567071839949112727">noklikšķināt uz priekšteča</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_mk.xtb b/chromium/ui/strings/translations/ui_strings_mk.xtb
index d3f6c8ba29b..4ab688beda8 100644
--- a/chromium/ui/strings/translations/ui_strings_mk.xtb
+++ b/chromium/ui/strings/translations/ui_strings_mk.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">избери</translation>
<translation id="1859234291848436338">Насока на пишување</translation>
<translation id="1860796786778352021">Затворање на известување</translation>
+<translation id="186476001994626254">Содржини од интернет за паметно лепење</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Избери ги &amp;сите</translation>
+<translation id="19085484004813472">Ова е нова функција</translation>
<translation id="2006524834898217237">Проверете дали уредов е поврзан на интернет.</translation>
<translation id="208586643495776849">Обидете се повторно</translation>
<translation id="2090963878406559571">За да испратите број од <ph name="ORIGIN" /> во телефонот со Android, <ph name="TROUBLESHOOT_LINK" /> за двата уреди во „Поставки“.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Управувајте со поставките</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{за 1 ч.}one{за # ч.}other{за # ч.}}</translation>
<translation id="2583543531130364912">Калибрирање на екранот на допир</translation>
+<translation id="2586657967955657006">Табла со исечоци</translation>
<translation id="2666092431469916601">Горе</translation>
<translation id="2701330563083355633">Споделено од <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Ново</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Отвори папка</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Пред 1 година}one{Пред # година}other{Пред # години}}</translation>
+<translation id="2878511608894704031">Избриши ги сите</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{за 1 д.}one{за # д.}other{за # д.}}</translation>
<translation id="2931838996092594335">кликни</translation>
<translation id="2981684127883932071">Се прикажуваат предлози</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Започни со говорење</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> е преместена во папката <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">СИТЕ АПЛИКАЦИИ</translation>
+<translation id="4491109536499578614">Слика</translation>
<translation id="4588090240171750605">Лизгај надесно</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, оцена со ѕвезди: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, препорачана апликација</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ден и }one{# ден и }other{# дена и }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{за 1 г.}one{за # г.}other{за # г.}}</translation>
+<translation id="588258955323874662">Цел екран</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Преостана 1 секунда}one{Преостана # секунда}other{Преостанаа # секунди}}</translation>
<translation id="5941711191222866238">Минимизирај</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 час}one{# час}other{# часа}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Помошник на Google</translation>
<translation id="6430678249303439055">Блокирај ги сите известувања од апликацијава</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Пред 1 секунда}one{Пред # секунда}other{Пред # секунди}}</translation>
+<translation id="6503257047630241175">Содржини во RTF-формат</translation>
<translation id="6539092367496845964">Нешто тргна наопаку. Обидете се повторно подоцна.</translation>
<translation id="654149438358937226">Блокирај ги сите известувања</translation>
<translation id="6567071839949112727">кликнете на претходникот</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ml.xtb b/chromium/ui/strings/translations/ui_strings_ml.xtb
index 2799f9051a3..ec82866f909 100644
--- a/chromium/ui/strings/translations/ui_strings_ml.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ml.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">തിരഞ്ഞെടുക്കൂ</translation>
<translation id="1859234291848436338">എഴുതേണ്ട ദിശ</translation>
<translation id="1860796786778352021">അറിയിപ്പ് അടയ്‌ക്കൽ</translation>
+<translation id="186476001994626254">വെബ് സ്‌മാർട്ട് ഒട്ടിക്കൽ ഉള്ളടക്കം</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">എല്ലാം &amp;തിരഞ്ഞെടുക്കൂ</translation>
+<translation id="19085484004813472">ഇതൊരു പുതിയ ഫീച്ചറാണ്</translation>
<translation id="2006524834898217237">ഈ ഉപകരണം ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്‌തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക.</translation>
<translation id="208586643495776849">വീണ്ടും ശ്രമിക്കുക</translation>
<translation id="2090963878406559571">നിങ്ങളുടെ Android ഫോണിലേക്ക് <ph name="ORIGIN" /> എന്നതിൽ നിന്ന് ഒരു നമ്പർ അയയ്ക്കാൻ, ക്രമീകരണത്തിൽ രണ്ട് ഉപകരണങ്ങളുടെയും <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ക്രമീകരണം മാനേജ് ചെയ്യുക</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{ഒരു മണിക്കൂറിനുള്ളിൽ}other{# മണിക്കൂറിനുള്ളിൽ}}</translation>
<translation id="2583543531130364912">നിങ്ങളുടെ ടച്ച്സ്‌ക്രീൻ കാലിബറേറ്റ് ചെയ്യുക</translation>
+<translation id="2586657967955657006">ക്ലിപ്പ്ബോർഡ്</translation>
<translation id="2666092431469916601">മുകളിലേക്ക്</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> ഉപകരണത്തിൽ നിന്ന് പങ്കിട്ടു</translation>
<translation id="271033894570825754">പുതിയത്</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ഫോൾഡർ തുറക്കുക</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{ഒരു വർഷം മുമ്പ്}other{# വർഷം മുമ്പ്}}</translation>
+<translation id="2878511608894704031">എല്ലാം ഇല്ലാതാക്കൂ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{ഒരു ദിവസത്തിൽ}other{# ദിവസത്തിൽ}}</translation>
<translation id="2931838996092594335">ക്ലിക്ക് ചെയ്യുക</translation>
<translation id="2981684127883932071">നിർദ്ദേശങ്ങൾ കാണിക്കുന്നു</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">സംഭാഷണം ആരംഭിക്കുക</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> എന്ന ആപ്പിനെ <ph name="FOLDER_NAME" /> എന്ന ഫോൾഡറിലേക്ക് നീക്കി.</translation>
<translation id="4316910396681052118">എല്ലാ ആപ്പുകളും</translation>
+<translation id="4491109536499578614">ചിത്രം</translation>
<translation id="4588090240171750605">വലത്തോട്ട് സ്ക്രോള്‍ ചെയ്യുക</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> നക്ഷത്ര റേറ്റിംഗ്</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ആപ്പ് നിർദ്ദേശം</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{ഒരു ദിവസവും }other{# ദിവസവും }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{ഒരു വർഷത്തിൽ}other{# വർഷത്തിൽ}}</translation>
+<translation id="588258955323874662">പൂര്‍‌ണ്ണസ്‌ക്രീന്‍</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{ഒരു സെക്കൻഡ് ശേഷിക്കുന്നു}other{# സെക്കൻഡ് ശേഷിക്കുന്നു}}</translation>
-<translation id="5941711191222866238">ചെറുതാക്കുക‍</translation>
+<translation id="5941711191222866238">ചെറുതാക്കുക</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{ഒരു മണിക്കൂർ}other{# മണിക്കൂർ}}</translation>
<translation id="6012623610530968780">പേജ് <ph name="SELECTED_PAGE" /> / <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">തിരയൽ ബോക്‌സ് ടെക്‌സ്‌റ്റ് മായ്‌ക്കുക</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ഈ ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്ക് ചെയ്യുക</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 സെക്കൻഡ് മുമ്പ്}other{# സെക്കൻഡ് മുമ്പ്}}</translation>
+<translation id="6503257047630241175">RTF ഉള്ളടക്കം</translation>
<translation id="6539092367496845964">എന്തോ കുഴപ്പം സംഭവിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക.</translation>
<translation id="654149438358937226">എല്ലാ അറിയിപ്പുകളും ബ്ലോക്ക് ചെയ്യുക</translation>
<translation id="6567071839949112727">ആൻസിസ്റ്ററിൽ ക്ലിക്ക് ചെയ്യുക</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_mn.xtb b/chromium/ui/strings/translations/ui_strings_mn.xtb
index 1a9c75f8b80..7db0351ec9a 100644
--- a/chromium/ui/strings/translations/ui_strings_mn.xtb
+++ b/chromium/ui/strings/translations/ui_strings_mn.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">сонгох</translation>
<translation id="1859234291848436338">Чиглэл бичих</translation>
<translation id="1860796786778352021">Мэдэгдлийг хаах</translation>
+<translation id="186476001994626254">Вэбийн ухаалгаар буулгах контент</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Бүгдийг сонго &amp;</translation>
+<translation id="19085484004813472">Энэ бол шинэ онцлог</translation>
<translation id="2006524834898217237">Энэ төхөөрөмж интернэтэд холбогдсон эсэхийг шалгана уу.</translation>
<translation id="208586643495776849">Дахин оролдоно уу</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" />-с Android утас руугаа дугаар илгээхийн тулд хоёр төхөөрөмжийнхөө аль алины тохиргоонд <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Тохиргоог удирдах</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 цагийн дараа}other{# цагийн дараа}}</translation>
<translation id="2583543531130364912">Мэдрэмтгий дэлгэцээ шалгах</translation>
+<translation id="2586657967955657006">Түр санах ой</translation>
<translation id="2666092431469916601">Дээд</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" />-с хуваалцсан</translation>
<translation id="271033894570825754">Шинэ</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Хавтсыг нээ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 жилийн өмнө}other{# жилийн өмнө}}</translation>
+<translation id="2878511608894704031">Бүгдийг нь устгах</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 хоногийн дараа}other{# хоногийн дараа}}</translation>
<translation id="2931838996092594335">дарах</translation>
<translation id="2981684127883932071">Зөвлөмжийг үзүүлж байна</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Ярьж эхлэх</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" />-г <ph name="FOLDER_NAME" /> фолдерт зөөсөн.</translation>
<translation id="4316910396681052118">БҮХ АПП</translation>
+<translation id="4491109536499578614">Зураг</translation>
<translation id="4588090240171750605">Баруун тийш гүйлгэх</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> одтой үнэлгээ</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, аппын зөвлөмж</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 өдөр ба }other{# өдөр ба }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 жилийн дараа}other{# жилийн дараа}}</translation>
+<translation id="588258955323874662">Дэлгэц дүүрэн</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 секунд үлдсэн}other{# секунд үлдсэн}}</translation>
<translation id="5941711191222866238">Багасгах</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 цаг}other{# цаг}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google туслах</translation>
<translation id="6430678249303439055">Энэ аппын бүх мэдэгдлийг блоклох</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 секундын өмнө}other{# секундын өмнө}}</translation>
+<translation id="6503257047630241175">RTF контент</translation>
<translation id="6539092367496845964">Алдаа гарлаа. Дараа дахин оролдоно уу.</translation>
<translation id="654149438358937226">Бүх мэдэгдлийг блоклох</translation>
<translation id="6567071839949112727">Анкесторыг товших</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_mr.xtb b/chromium/ui/strings/translations/ui_strings_mr.xtb
index ac1dbf48788..3f9a0536233 100644
--- a/chromium/ui/strings/translations/ui_strings_mr.xtb
+++ b/chromium/ui/strings/translations/ui_strings_mr.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">निवडा</translation>
<translation id="1859234291848436338">लिहिण्याची दिशा</translation>
<translation id="1860796786778352021">सूचना बंद</translation>
+<translation id="186476001994626254">वेब स्‍मार्ट पेस्ट आशय</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;सर्व निवडा</translation>
+<translation id="19085484004813472">हे नवीन वैशिष्‍ट्य आहे</translation>
<translation id="2006524834898217237">हे डिव्हाइस इंटरनेटशी कनेक्ट केलेले असल्याची खात्री करा.</translation>
<translation id="208586643495776849">कृपया पुन्हा प्रयत्न करा</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> वरून तुमच्या Android फोनवर नंबर पाठवण्याकरिता सेटिंग्जमध्ये दोन्ही डिव्हाइससाठी <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">सेटिंग्ज व्यवस्थापित करा</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{१ता मध्ये}other{#ता मध्ये}}</translation>
<translation id="2583543531130364912">तुमची टचस्क्रीन कॅलिब्रेट करा</translation>
+<translation id="2586657967955657006">क्लिपबोर्ड</translation>
<translation id="2666092431469916601">शीर्ष</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> वरून शेअर केले</translation>
<translation id="271033894570825754">नवीन</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">फोल्डर उघडा</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 वर्षापूर्वी}other{# वर्षांपूर्वी}}</translation>
+<translation id="2878511608894704031">सर्व हटवा</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{१दि मध्ये}other{#दि मध्ये}}</translation>
<translation id="2931838996092594335">क्लिक करा</translation>
<translation id="2981684127883932071">सूचना दाखवत आहे</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">बोलणे प्रारंभ करा</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> फोल्डर <ph name="FOLDER_NAME" /> वर हलवले.</translation>
<translation id="4316910396681052118">सर्व अ‍ॅप्स</translation>
+<translation id="4491109536499578614">इमेज</translation>
<translation id="4588090240171750605">उजवे स्क्रोल करा</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, तारा रेटिंग <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ॲप शिफारस</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 दिवस आणि }other{# दिवस आणि }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{१व मध्ये}other{#व मध्ये}}</translation>
+<translation id="588258955323874662">क्षेत्रे</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 सेकंद शिल्लक}other{# सेकंद शिल्लक}}</translation>
-<translation id="5941711191222866238">लहान करा</translation>
+<translation id="5941711191222866238">कमी करा</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 तास}other{# तास}}</translation>
<translation id="6012623610530968780"><ph name="TOTAL_PAGE_NUM" /> पैकी <ph name="SELECTED_PAGE" /> पेज</translation>
<translation id="6022924867608035986">शोध बॉक्स मजकूर साफ करा</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">या अ‍ॅपच्या सर्व सूचना ब्‍लॉक करा</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 सेकंदापूर्वी}other{# सेकंदांपूर्वी}}</translation>
+<translation id="6503257047630241175">RTF आशय</translation>
<translation id="6539092367496845964">काहीतरी चूक झाली. पुन्हा प्रयत्न करा.</translation>
<translation id="654149438358937226">सर्व सूचना ब्लॉक करा</translation>
<translation id="6567071839949112727">पूर्वजवर क्लिक करा</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ms.xtb b/chromium/ui/strings/translations/ui_strings_ms.xtb
index d39fda7c385..2238c01ce46 100644
--- a/chromium/ui/strings/translations/ui_strings_ms.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ms.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">pilih</translation>
<translation id="1859234291848436338">Arah Penulisan</translation>
<translation id="1860796786778352021">Tutup Pemberitahuan</translation>
+<translation id="186476001994626254">Kandungan Tampal Pintar Web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Pilih &amp;semua</translation>
+<translation id="19085484004813472">Ini ciri baharu</translation>
<translation id="2006524834898217237">Pastikan peranti ini disambungkan ke Internet.</translation>
<translation id="208586643495776849">Sila cuba lagi</translation>
<translation id="2090963878406559571">Untuk menghantar nombor dari <ph name="ORIGIN" /> ke telefon Android anda, <ph name="TROUBLESHOOT_LINK" /> untuk kedua-dua peranti dalam tetapan.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Urus tetapan</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{dalam 1j}other{dalam #j}}</translation>
<translation id="2583543531130364912">Tentukur skrin sentuh anda</translation>
+<translation id="2586657967955657006">Papan Keratan</translation>
<translation id="2666092431469916601">Atas</translation>
<translation id="2701330563083355633">Dikongsi daripada <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Baharu</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Buka folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 tahun yang lalu}other{# tahun yang lalu}}</translation>
+<translation id="2878511608894704031">Padamkan Semua</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{dalam 1h}other{dalam #h}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Memaparkan cadangan</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Mula Bercakap</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> dialihkan kepada folder <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">SEMUA APL</translation>
+<translation id="4491109536499578614">Imej</translation>
<translation id="4588090240171750605">Tatal ke Kanan</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Rating bintang <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Pengesyoran apl</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 hari dan }other{# hari dan }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{dalam 1t}other{dalam #t}}</translation>
+<translation id="588258955323874662">Skrin penuh</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 saat lagi}other{# saat lagi}}</translation>
<translation id="5941711191222866238">Minimumkan</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 jam}other{# jam}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">Sekat semua pemberitahuan daripada apl ini</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Sesaat yang lalu}other{# saat yang lalu}}</translation>
+<translation id="6503257047630241175">Kandungan RTF</translation>
<translation id="6539092367496845964">Kesilapan telah berlaku. Cuba sebentar lagi.</translation>
<translation id="654149438358937226">Sekat semua pemberitahuan</translation>
<translation id="6567071839949112727">klik pewaris</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_my.xtb b/chromium/ui/strings/translations/ui_strings_my.xtb
index ee32abe3891..ead401a252b 100644
--- a/chromium/ui/strings/translations/ui_strings_my.xtb
+++ b/chromium/ui/strings/translations/ui_strings_my.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ရွေးချယ်ရန်</translation>
<translation id="1859234291848436338">လမ်းညွှန်ချက် ရေးနေ</translation>
<translation id="1860796786778352021">အကြောင်းကြားစာ ပိတ်ရန်</translation>
+<translation id="186476001994626254">ဝဘ် စမတ် ကူးထည့်ရန် အကြောင်းအရာ</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">အားလုံးကို &amp;ရွေးရန်</translation>
+<translation id="19085484004813472">၎င်းသည် ဝန်ဆောင်မှုအသစ်ဖြစ်သည်</translation>
<translation id="2006524834898217237">ဤစက်သည် အင်တာနက်ချိတ်ထားမှု ရှိမရှိ စစ်ဆေးပါ။</translation>
<translation id="208586643495776849">ထပ်လုပ်ကြည့်ပါ</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> မှ နံပါတ်ကို သင့် Android ဖုန်းသို့ ပို့ရန် ဆက်တင်များတွင် စက်နှစ်ခုလုံးအတွက် <ph name="TROUBLESHOOT_LINK" />။</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ဆက်တင်များ စီမံခန့်ခွဲရန်</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{၁နာရီ အတွင်း}other{#နာရီ အတွင်း}}</translation>
<translation id="2583543531130364912">သင်၏ တို့ထိမျက်နှာပြင်ကို တိုင်းတာချိန်ညှိပါ</translation>
+<translation id="2586657967955657006">ကလစ်ဘုတ်</translation>
<translation id="2666092431469916601">ထိပ်</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> ထံမှ မျှဝေထားပါသည်</translation>
<translation id="271033894570825754">အသစသ်</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />၊ <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ဖိုင်တွဲဖွင့်</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{ပြီးခဲ့သည့် ၁ နှစ်}other{ပြီးခဲ့သည့် # နှစ်}}</translation>
+<translation id="2878511608894704031">အားလုံး ဖျက်ရန်</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{၁ရက် အတွင်း}other{#ရက် အတွင်း}}</translation>
<translation id="2931838996092594335">နှိပ်ပါ</translation>
<translation id="2981684127883932071">အကြံပြုချက်များကို ဖော်ပြနေသည်</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">စတင် စကားပြော</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ကို <ph name="FOLDER_NAME" /> ဖိုင်တွဲသို့ ရွှေ့လိုက်ပါပြီ။</translation>
<translation id="4316910396681052118">အက်ပ်များ အားလုံး</translation>
+<translation id="4491109536499578614">ပုံ</translation>
<translation id="4588090240171750605">ညာဖက်သို့ ရွှေ့ရန်</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />၊ ကြယ်ပွင့် အဆင့်သတ်မှတ်ချက် <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />၊ အက်ပ်အကြံပြုချက်</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{၁ ရက်နှင့် }other{# ရက်နှင့် }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{၁နှစ် အတွင်း}other{#နှစ် အတွင်း}}</translation>
+<translation id="588258955323874662">မျက်နှာပြင် အပြည့်</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{၁ စက္ကန့်ကျန်၏}other{# စက္ကန့်ကျန်၏}}</translation>
<translation id="5941711191222866238">အနည်းဆုံး လုပ်ရန်</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{၁ နာရီ}other{# နာရီ}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ဤအက်ပ်မှ အကြောင်းအကြားချက်များ အားလုံးကို ပိတ်ရန်</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{ပြီးခဲ့သည့် ၁ စက္ကန့်က}other{ပြီးခဲ့သည့် # စက္ကန့်က}}</translation>
+<translation id="6503257047630241175">RTF အကြောင်းအရာ</translation>
<translation id="6539092367496845964">တစ်ခုခု မှားသွားသည်။ နောက်မှ ထပ်စမ်းကြည့်ပါ။</translation>
<translation id="654149438358937226">သတိပေးချက်များအားလုံးကို ပိတ်ရန်</translation>
<translation id="6567071839949112727">ပင်မကို ကလစ်နှိပ်ပါ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ne.xtb b/chromium/ui/strings/translations/ui_strings_ne.xtb
index 536ed978abc..4e7a4603b5b 100644
--- a/chromium/ui/strings/translations/ui_strings_ne.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ne.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">चयन गर्नुहोस्</translation>
<translation id="1859234291848436338">लेखन निर्देशन</translation>
<translation id="1860796786778352021">सूचना बन्द भयो</translation>
+<translation id="186476001994626254">वेब स्मार्ट पेस्टको सामग्री</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;सबै चयन गर्नुहोस्</translation>
+<translation id="19085484004813472">यो नयाँ सुविधा हो</translation>
<translation id="2006524834898217237">यो यन्त्र इन्टरनेटमा जोडिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्।</translation>
<translation id="208586643495776849">कृपया फेरि प्रयास गर्नुहोस्</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> बाट आफ्नो Android फोनमा कुनै नम्बर पठाउन सेटिङमा गई दुवै यन्त्रहरूमा <ph name="TROUBLESHOOT_LINK" />।</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">सेटिङ व्यवस्थापन गर्नुहोस्</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{१ घन्टामा}other{#घन्टामा}}</translation>
<translation id="2583543531130364912">आफ्नो टचस्क्रिनलाई क्यालिब्रेट गर्नुहोस्</translation>
+<translation id="2586657967955657006">क्लिपबोर्ड</translation>
<translation id="2666092431469916601">शीर्ष</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> ले आदान प्रदान गरेको</translation>
<translation id="271033894570825754">नयाँ</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">फोल्डर खोल्नुहोस्</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{१ वर्ष अघि}other{# वर्ष अघि}}</translation>
+<translation id="2878511608894704031">सबै वस्तु मेट्नुहोस्</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{१ दिनमा}other{# दिनमा}}</translation>
<translation id="2931838996092594335">क्लिक गर्नुहोस्</translation>
<translation id="2981684127883932071">सुझावहरू देखाउँदै</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">बोल्न सुरु गर्नुहोस्</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> फोल्डर <ph name="FOLDER_NAME" /> मा सारियो।</translation>
<translation id="4316910396681052118">सबै एपहरू</translation>
+<translation id="4491109536499578614">छवि</translation>
<translation id="4588090240171750605">दायाँ तिर स्क्रोल गर्नुहोस्</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, तारा चिन्ह प्रयोग गरी गरिने मूल्याङ्कन <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, एपको सिफारिस</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{ १ दिन र }other{ # दिन र }}</translation>
<translation id="5789643057113097023">।</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{१ वर्षमा}other{# वर्षमा}}</translation>
+<translation id="588258955323874662">पूर्णस्क्रिन</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{ १ सेकेन्ड बाँकी}other{ # सेकेन्ड बाँकी}}</translation>
<translation id="5941711191222866238">सानो बनाउनुहोस</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{ १ घन्टा}other{ # घन्टा}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google सहायक</translation>
<translation id="6430678249303439055">यो अनुप्रयोगका सबै सूचनाहरूमाथि रोक लगाउनुहोस्</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{१ सेकेन्ड अघि}other{# सेकेन्ड अघि}}</translation>
+<translation id="6503257047630241175">RTF फाइलको सामग्री</translation>
<translation id="6539092367496845964">केही चिज गडबड भयो। पछि फेरि प्रयास गर्नुहोस्।</translation>
<translation id="654149438358937226">सबै सूचनाहरूमाथि रोक लगाउनुहोस्</translation>
<translation id="6567071839949112727">यसभन्दा अघिल्लो वस्तुमा क्लिक गर्नुहोस्</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_nl.xtb b/chromium/ui/strings/translations/ui_strings_nl.xtb
index a69ec81b4eb..eb63400c591 100644
--- a/chromium/ui/strings/translations/ui_strings_nl.xtb
+++ b/chromium/ui/strings/translations/ui_strings_nl.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">Selecteren</translation>
<translation id="1859234291848436338">Schrijfrichting</translation>
<translation id="1860796786778352021">Melding sluiten</translation>
+<translation id="186476001994626254">Content voor slim plakken op het web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">&amp;Alles selecteren</translation>
+<translation id="19085484004813472">Dit is een nieuwe functie</translation>
<translation id="2006524834898217237">Zorg dat dit apparaat is verbonden met internet.</translation>
<translation id="208586643495776849">Probeer het opnieuw</translation>
<translation id="2090963878406559571">Als je vanaf <ph name="ORIGIN" /> een nummer naar je Android-telefoon wilt sturen, moet je in de instellingen <ph name="TROUBLESHOOT_LINK" /> voor beide apparaten.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Instellingen beheren</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{over 1 u}other{over # u}}</translation>
<translation id="2583543531130364912">Je touchscreen kalibreren</translation>
+<translation id="2586657967955657006">Klembord</translation>
<translation id="2666092431469916601">Boven</translation>
<translation id="2701330563083355633">Gedeeld vanaf <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nieuw</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Map openen</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 jaar geleden}other{# jaar geleden}}</translation>
+<translation id="2878511608894704031">Alles verwijderen</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{over 1 d}other{over # d}}</translation>
<translation id="2931838996092594335">klikken</translation>
<translation id="2981684127883932071">Suggesties weergeven</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Inspreken starten</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> is verplaatst naar de map <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLE APPS</translation>
+<translation id="4491109536499578614">Afbeelding</translation>
<translation id="4588090240171750605">Naar rechts bladeren</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, sterbeoordeling <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, app-aanbeveling</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dag en }other{# dagen en }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{over 1 j}other{over # j}}</translation>
+<translation id="588258955323874662">Volledig scherm</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 seconde resterend}other{# seconden reseterend}}</translation>
<translation id="5941711191222866238">Minimaliseren</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 uur}other{# uur}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Alle meldingen van deze app blokkeren</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 seconde geleden}other{# seconden geleden}}</translation>
+<translation id="6503257047630241175">RTF-content</translation>
<translation id="6539092367496845964">Er is iets misgegaan. Probeer het later opnieuw.</translation>
<translation id="654149438358937226">Alle meldingen blokkeren</translation>
<translation id="6567071839949112727">klik via bovenliggende entiteit</translation>
@@ -238,7 +245,7 @@
<translation id="8677655579646609597"><ph name="QUANTITY" /> KB/s</translation>
<translation id="8685326675965865247">Zoek op je apparaat, in apps en instellingen en op internet. Gebruik de pijltoetsen om te navigeren in je apps.</translation>
<translation id="8725488761726303204">+ nog <ph name="NUMBER" /></translation>
-<translation id="8730621377337864115">Gereed</translation>
+<translation id="8730621377337864115">Klaar</translation>
<translation id="8772073294905169192">{HOURS,plural, =1{1 u}other{# u}}</translation>
<translation id="8798099450830957504">Standaard</translation>
<translation id="8806053966018712535">Map <ph name="FOLDER_NAME" /></translation>
diff --git a/chromium/ui/strings/translations/ui_strings_no.xtb b/chromium/ui/strings/translations/ui_strings_no.xtb
index 56956acfdf7..7304943abcd 100644
--- a/chromium/ui/strings/translations/ui_strings_no.xtb
+++ b/chromium/ui/strings/translations/ui_strings_no.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">velg</translation>
<translation id="1859234291848436338">Skriveretning</translation>
<translation id="1860796786778352021">Lukk varsel</translation>
+<translation id="186476001994626254">Web Smart Paste-innhold</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Marker &amp;alt</translation>
+<translation id="19085484004813472">Dette er en ny funksjon</translation>
<translation id="2006524834898217237">Kontrollér at enheten er koblet til internett.</translation>
<translation id="208586643495776849">Prøv igjen</translation>
<translation id="2090963878406559571">For å sende numre fra <ph name="ORIGIN" /> til Android-telefonen din, <ph name="TROUBLESHOOT_LINK" /> for begge enhetene i innstillingene.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Administrer innstillinger</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{om 1 t}other{om # t}}</translation>
<translation id="2583543531130364912">Kalibrer berøringsskjermen din</translation>
+<translation id="2586657967955657006">Utklippstavle</translation>
<translation id="2666092431469916601">Topp</translation>
<translation id="2701330563083355633">Delt fra <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Ny</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" /> – <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Åpne mappen</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{for 1 år siden}other{for # år siden}}</translation>
+<translation id="2878511608894704031">Slett alle</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{om 1 d}other{om # d}}</translation>
<translation id="2931838996092594335">klikk</translation>
<translation id="2981684127883932071">Viser forslag</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Begynn å snakke</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> er flyttet til mappen <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLE APPER</translation>
+<translation id="4491109536499578614">Bilde</translation>
<translation id="4588090240171750605">Rull mot høyre</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" /> – stjernerangering <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" /> – appanbefaling</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dag og }other{# dager og }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{om 1 år}other{om # år}}</translation>
+<translation id="588258955323874662">Fullskjerm</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekund igjen}other{# sekunder igjen}}</translation>
<translation id="5941711191222866238">Minimer</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 time}other{# timer}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Blokkér alle varsler fra denne appen</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{for 1 sekund siden}other{for # sekunder siden}}</translation>
+<translation id="6503257047630241175">RTF-innhold</translation>
<translation id="6539092367496845964">Noe gikk galt. Prøv på nytt senere.</translation>
<translation id="654149438358937226">Blokkér alle varsler</translation>
<translation id="6567071839949112727">klikk på et overordnet element</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_or.xtb b/chromium/ui/strings/translations/ui_strings_or.xtb
index 430647ac8a1..beb3e3ab7e1 100644
--- a/chromium/ui/strings/translations/ui_strings_or.xtb
+++ b/chromium/ui/strings/translations/ui_strings_or.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ଚୟନ</translation>
<translation id="1859234291848436338">ଦିଗନିର୍ଦ୍ଦେଶ ଲେଖାଯାଉଛି</translation>
<translation id="1860796786778352021">ବିଜ୍ଞପ୍ତି ବନ୍ଦ କରନ୍ତୁ</translation>
+<translation id="186476001994626254">ୱେବ୍ ସ୍ମାର୍ଟ ପେଷ୍ଟ ବିଷୟବସ୍ତୁ</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;ସମସ୍ତ ଚୟନ କରନ୍ତୁ</translation>
+<translation id="19085484004813472">ଏହା ଏକ ନୂଆ ଫିଚର୍ ଅଟେ</translation>
<translation id="2006524834898217237">ଏହି ଡିଭାଇସ୍‌ଟି ଇଣ୍ଟର୍ନେଟ୍ ସହ ସଂଯୁକ୍ତ ହୋଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।</translation>
<translation id="208586643495776849">ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" />ରୁ ଆପଣଙ୍କ Android ଫୋନ୍‌କୁ ଏକ ନମ୍ବର ପଠାଇବା ପାଇଁ ସେଟିଂସ୍‌ରେ ଉଭୟ ଡିଭାଇସ୍ ନିମନ୍ତେ <ph name="TROUBLESHOOT_LINK" />।</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ସେଟିଂସ୍ ପରିଚାଳନା କରନ୍ତୁ</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{ଘଣ୍ଟା ମଧ୍ୟରେ}other{# ଘଣ୍ଟା ମଧ୍ୟରେ}}</translation>
<translation id="2583543531130364912">ଆପଣଙ୍କର ଟଚ୍‌ସ୍କ୍ରିନ୍‌କୁ କାଲିବ୍ରେଟ୍ କରନ୍ତୁ</translation>
+<translation id="2586657967955657006">କ୍ଲିପ୍‌ବୋର୍ଡ</translation>
<translation id="2666092431469916601">ଶୀର୍ଷ</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" />ରୁ ସେୟାର୍ କରାଯାଇଛି</translation>
<translation id="271033894570825754">ନୂଆ</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ଫୋଲ୍ଡର ଖୋଲନ୍ତୁ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ବର୍ଷ ପୂର୍ବେ}other{# ବର୍ଷ ପୂର୍ବେ}}</translation>
+<translation id="2878511608894704031">ସବୁ ଡିଲିଟ୍ କରନ୍ତୁ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 ଦିନରେ}other{# ଦିନରେ}}</translation>
<translation id="2931838996092594335">କ୍ଲିକ୍ କରନ୍ତୁ</translation>
<translation id="2981684127883932071">ପ୍ରସ୍ତାବଗୁଡ଼ିକ ଡିସପ୍ଲେ ହେଉଛି</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">କହିବା ଆରମ୍ଭ କରନ୍ତୁ</translation>
<translation id="430191667033048642"><ph name="FOLDER_NAME" /> ଫୋଲ୍ଡରକୁ <ph name="MOVED_APP_NAME" /> ନିଆଗଲା।</translation>
<translation id="4316910396681052118">ସମସ୍ତ ଆପ୍‌</translation>
+<translation id="4491109536499578614">ଛବି</translation>
<translation id="4588090240171750605">ଦକ୍ଷିଣକୁ ସ୍କ୍ରୋଲ୍ କରନ୍ତୁ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ତାରା ମୂଲ୍ୟାଙ୍କନ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ଆପ୍‍ର ସୁପାରିଶ</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ଦିନ ଏବଂ }other{# ଦିନ ଏବଂ }}</translation>
<translation id="5789643057113097023">।</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{ବର୍ଷରେ}other{# ବର୍ଷରେ}}</translation>
+<translation id="588258955323874662">ପୂର୍ଣ୍ଣସ୍କ୍ରିନ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 ସେକେଣ୍ଡ ବାକିଅଛି}other{# ସେକେଣ୍ଡ ବାକିଅଛି}}</translation>
<translation id="5941711191222866238">ସର୍ବନିମ୍ନ କରନ୍ତୁ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ଏହି ଆପ୍‌ରୁ ସମସ୍ତ ବିଜ୍ଞପ୍ତି ବ୍ଲକ୍ କରନ୍ତୁ</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 ସେକେଣ୍ଡ ପୂର୍ବେ}other{# ସେକେଣ୍ଡ ପୂର୍ବେ}}</translation>
+<translation id="6503257047630241175">RTF ବିଷୟବସ୍ତୁ</translation>
<translation id="6539092367496845964">କିଛି ଭୁଲ ହୋଇଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।</translation>
<translation id="654149438358937226">ସମସ୍ତ ବିଜ୍ଞପ୍ତିକୁ ବ୍ଲକ୍ କରନ୍ତୁ</translation>
<translation id="6567071839949112727">ଆନେସେଷ୍ଟର୍‌ରେ କ୍ଲିକ୍‌ କରନ୍ତୁ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_pa.xtb b/chromium/ui/strings/translations/ui_strings_pa.xtb
index 8683ca6048c..71089ba0ff1 100644
--- a/chromium/ui/strings/translations/ui_strings_pa.xtb
+++ b/chromium/ui/strings/translations/ui_strings_pa.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ਚੁਣੋ</translation>
<translation id="1859234291848436338">ਦਿਸ਼ਾ ਲਿਖ ਰਿਹਾ ਹੈ</translation>
<translation id="1860796786778352021">ਸੂਚਨਾ ਬੰਦ</translation>
+<translation id="186476001994626254">ਵੈੱਬ ਸਮਾਰਟ ਪੇਸਟ ਸਮੱਗਰੀ</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;ਸਾਰੇ ਚੁਣੋ</translation>
+<translation id="19085484004813472">ਇਹ ਇੱਕ ਨਵੀਂ ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ</translation>
<translation id="2006524834898217237">ਪੱਕਾ ਕਰੋ ਕਿ ਇਹ ਡੀਵਾਈਸ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੋਵੇ।</translation>
<translation id="208586643495776849">ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> ਤੋਂ ਨੰਬਰ ਆਪਣੇ Android ਫ਼ੋਨ ਵਿੱਚ ਭੇਜਣ ਲਈ, ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਦੋਵੇਂ ਡੀਵਾਈਸਾਂ ਲਈ <ph name="TROUBLESHOOT_LINK" />।</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ਸੈਟਿੰਗਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 ਘੰ. ਵਿੱਚ}one{# ਘੰ. ਵਿੱਚ}other{# ਘੰ. ਵਿੱਚ}}</translation>
<translation id="2583543531130364912">ਆਪਣੀ ਟੱਚਸਕ੍ਰੀਨ ਨੂੰ ਕੈਲੀਬਰੇਟ ਕਰੋ</translation>
+<translation id="2586657967955657006">ਕਲਿੱਪਬੋਰਡ</translation>
<translation id="2666092431469916601">ਟੌਪ</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> ਤੋਂ ਸਾਂਝਾ ਕੀਤਾ ਗਿਆ</translation>
<translation id="271033894570825754">ਨਵਾਂ</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ਫੋਲਡਰ ਖੋਲ੍ਹੋ</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ਸਾਲ ਪਹਿਲਾਂ}one{# ਸਾਲ ਪਹਿਲਾਂ}other{# ਸਾਲ ਪਹਿਲਾਂ}}</translation>
+<translation id="2878511608894704031">ਸਭ ਮਿਟਾਓ</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 ਦਿਨ ਵਿੱਚ}one{# ਦਿਨ ਵਿੱਚ}other{# ਦਿਨ ਵਿੱਚ}}</translation>
<translation id="2931838996092594335">ਕਲਿੱਕ ਕਰੋ</translation>
<translation id="2981684127883932071">ਸੁਝਾਵਾਂ ਨੂੰ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">ਬੋਲਣਾ ਸ਼ੁਰੂ ਕਰੋ</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ਨੂੰ <ph name="FOLDER_NAME" /> ਫੋਲਡਰ ਵਿੱਚ ਲਿਜਾਇਆ ਗਿਆ।</translation>
<translation id="4316910396681052118">ਸਭ ਐਪਾਂ</translation>
+<translation id="4491109536499578614">ਚਿੱਤਰ</translation>
<translation id="4588090240171750605">ਸੱਜੇ ਪਾਸੇ ਸਕ੍ਰੌਲ ਕਰੋ</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ਤਾਰਾ ਰੇਟਿੰਗ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ਐਪ ਸਿਫ਼ਾਰਸ਼</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ਦਿਨ ਅਤੇ }one{# ਦਿਨ ਅਤੇ }other{# ਦਿਨ ਅਤੇ }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 ਸਾਲ ਵਿੱਚ}one{# ਸਾਲ ਵਿੱਚ}other{# ਸਾਲ ਵਿੱਚ}}</translation>
+<translation id="588258955323874662">ਫੁਲਸਕ੍ਰੀਨ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 ਸਕਿੰਟ ਬਾਕੀ}one{# ਸਕਿੰਟ ਬਾਕੀ}other{# ਸਕਿੰਟ ਬਾਕੀ}}</translation>
<translation id="5941711191222866238">ਨਿਊਨਤਮ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ਘੰਟਾ}one{# ਘੰਟੇ}other{# ਘੰਟੇ}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ਇਸ ਐਪ ਦੀਆਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਬਲਾਕ ਕਰੋ</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 ਸਕਿੰਟ ਪਹਿਲਾਂ}one{# ਸਕਿੰਟ ਪਹਿਲਾਂ}other{# ਸਕਿੰਟ ਪਹਿਲਾਂ}}</translation>
+<translation id="6503257047630241175">RTF ਸਮੱਗਰੀ</translation>
<translation id="6539092367496845964">ਕੋਈ ਗੜਬੜ ਹੋਈ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।</translation>
<translation id="654149438358937226">ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕਰੋ</translation>
<translation id="6567071839949112727">ਇਸ ਨਾਲ ਸੰਬੰਧਿਤ ਕਿਸੇ ਪੁਰਾਣੀ ਵਸਤੂ 'ਤੇ ਕਲਿੱਕ ਕਰੋ</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_pl.xtb b/chromium/ui/strings/translations/ui_strings_pl.xtb
index 8e28061e538..91cb8075084 100644
--- a/chromium/ui/strings/translations/ui_strings_pl.xtb
+++ b/chromium/ui/strings/translations/ui_strings_pl.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">wybierz</translation>
<translation id="1859234291848436338">Kierunek pisania</translation>
<translation id="1860796786778352021">Zamknięcie powiadomienia</translation>
+<translation id="186476001994626254">Treść typu Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Zaznacz &amp;wszystko</translation>
+<translation id="19085484004813472">To jest nowa funkcja</translation>
<translation id="2006524834898217237">Upewnij się, że to urządzenie jest połączone z internetem.</translation>
<translation id="208586643495776849">Spróbuj ponownie</translation>
<translation id="2090963878406559571">Aby przesłać numer z <ph name="ORIGIN" /> na swój telefon z Androidem, <ph name="TROUBLESHOOT_LINK" /> obu urządzeń w ustawieniach.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Zarządzaj ustawieniami</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{za 1 godz.}few{za # godz.}many{za # godz.}other{za # godz.}}</translation>
<translation id="2583543531130364912">Skalibruj ekran dotykowy</translation>
+<translation id="2586657967955657006">Schowek</translation>
<translation id="2666092431469916601">Do góry</translation>
<translation id="2701330563083355633">Tę kartę udostępnia <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nowy</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otwórz folder</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{rok temu}few{# lata temu}many{# lat temu}other{# roku temu}}</translation>
+<translation id="2878511608894704031">Usuń wszystko</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{za 1 dzień}few{za # dni}many{za # dni}other{za # dnia}}</translation>
<translation id="2931838996092594335">kliknij</translation>
<translation id="2981684127883932071">Wyświetlam sugestie</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Zacznij mówić</translation>
<translation id="430191667033048642">Aplikacja <ph name="MOVED_APP_NAME" /> została przeniesiona do folderu <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">WSZYSTKIE APLIKACJE</translation>
+<translation id="4491109536499578614">Obraz</translation>
<translation id="4588090240171750605">Przewiń w prawo</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, liczba gwiazdek <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, rekomendowana aplikacja</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dzień i }few{# dni i }many{# dni i }other{# dnia i }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{za 1 rok}few{za # lata}many{za # lat}other{za # roku}}</translation>
+<translation id="588258955323874662">Pełny ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Pozostała 1 sekunda}few{Pozostały # sekundy}many{Pozostało # sekund}other{Pozostało # sekundy}}</translation>
<translation id="5941711191222866238">Minimalizuj</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 godzina}few{# godziny}many{# godzin}other{# godziny}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asystent Google</translation>
<translation id="6430678249303439055">Blokuj wszystkie powiadomienia z tej aplikacji</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{sekundę temu}few{# sekundy temu}many{# sekund temu}other{# sekundy temu}}</translation>
+<translation id="6503257047630241175">Treść RTF</translation>
<translation id="6539092367496845964">Coś poszło nie tak. Spróbuj później.</translation>
<translation id="654149438358937226">Blokuj wszystkie powiadomienia</translation>
<translation id="6567071839949112727">kliknij element nadrzędny</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Zapisz plik</translation>
<translation id="6656912866303152668">Upewnij się, że <ph name="TARGET_DEVICE_NAME" /> ma w Chrome włączoną synchronizację, a potem spróbuj wysłać ponownie.</translation>
<translation id="6699343763173986273">Następny utwór multimedialny</translation>
-<translation id="6710213216561001401">Wstecz</translation>
+<translation id="6710213216561001401">Poprzedni</translation>
<translation id="6779314412797872738">Aby przesłać stąd numer na swój telefon z Androidem, <ph name="TROUBLESHOOT_LINK" /> obu urządzeń w ustawieniach.</translation>
<translation id="6786750046913594791">Zamknij folder</translation>
<translation id="6808150112686056157">Zatrzymaj multimedia</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb
index f4e0d62f219..c4e139708b9 100644
--- a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb
+++ b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">selecione</translation>
<translation id="1859234291848436338">Direção de Gravação</translation>
<translation id="1860796786778352021">Fechar notificação</translation>
+<translation id="186476001994626254">Conteúdo Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Selecionar &amp;tudo</translation>
+<translation id="19085484004813472">Este é um recurso novo</translation>
<translation id="2006524834898217237">Verifique se o dispositivo está conectado à Internet.</translation>
<translation id="208586643495776849">Tente novamente</translation>
<translation id="2090963878406559571">Para enviar um número de <ph name="ORIGIN" /> para seu smartphone Android, <ph name="TROUBLESHOOT_LINK" /> para os dois dispositivos nas configurações.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gerenciar configurações</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{em 1 h}one{em # h}other{em # h}}</translation>
<translation id="2583543531130364912">Calibrar a touchscreen</translation>
+<translation id="2586657967955657006">Área de transferência</translation>
<translation id="2666092431469916601">Parte superior</translation>
<translation id="2701330563083355633">Compartilhada por <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Abrir pasta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ano atrás}one{# ano atrás}other{# anos atrás}}</translation>
+<translation id="2878511608894704031">Excluir tudo</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{em 1 d}one{em # d}other{em # d}}</translation>
<translation id="2931838996092594335">clicar</translation>
<translation id="2981684127883932071">Exibindo sugestões</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Comece a falar</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> movido para a pasta <ph name="FOLDER_NAME" /></translation>
<translation id="4316910396681052118">TODOS OS APPS</translation>
+<translation id="4491109536499578614">Imagem</translation>
<translation id="4588090240171750605">Percorrer à direita</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, avaliado com <ph name="RATING_SCORE" /> estrelas</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recomendação de app</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{Um dia e }one{# dias e }other{# dias e }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{em 1 a}one{em # a}other{em # a}}</translation>
+<translation id="588258955323874662">Tela inteira</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Um segundo restante}one{# segundos restantes}other{# segundos restantes}}</translation>
<translation id="5941711191222866238">Minimizar</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{Uma hora}one{# horas}other{# horas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistente</translation>
<translation id="6430678249303439055">Bloquear todas as notificações emitidas por este app</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 segundo atrás}one{# segundo atrás}other{# segundos atrás}}</translation>
+<translation id="6503257047630241175">Conteúdo RTF</translation>
<translation id="6539092367496845964">Algo deu errado. Tente novamente mais tarde.</translation>
<translation id="654149438358937226">Bloquear todas as notificações</translation>
<translation id="6567071839949112727">clicar no predecessor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb
index 7a524e4b382..24ee0d927be 100644
--- a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb
+++ b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seleccionar</translation>
<translation id="1859234291848436338">Direcção da escrita</translation>
<translation id="1860796786778352021">Fechar notificação</translation>
+<translation id="186476001994626254">Conteúdo Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Seleccion&amp;ar tudo</translation>
+<translation id="19085484004813472">Esta é uma nova funcionalidade</translation>
<translation id="2006524834898217237">Certifique-se de que este dispositivo está ligado à Internet.</translation>
<translation id="208586643495776849">Tente novamente</translation>
<translation id="2090963878406559571">Para enviar um número de <ph name="ORIGIN" /> para o seu telemóvel Android, <ph name="TROUBLESHOOT_LINK" /> para ambos os dispositivos nas definições.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Faça a gestão das definições</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{em 1 h}other{em # h}}</translation>
<translation id="2583543531130364912">Calibrar o ecrã tátil</translation>
+<translation id="2586657967955657006">Área de transferência</translation>
<translation id="2666092431469916601">Parte superior</translation>
<translation id="2701330563083355633">Partilhado a partir do <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Abrir pasta</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Há 1 ano}other{Há # anos}}</translation>
+<translation id="2878511608894704031">Eliminar tudo</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{em 1 d.}other{em # d.}}</translation>
<translation id="2931838996092594335">clicar</translation>
<translation id="2981684127883932071">A apresentar sugestões</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Comece a Falar</translation>
<translation id="430191667033048642">A aplicação <ph name="MOVED_APP_NAME" /> foi movida para a pasta <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TODAS AS APLICAÇÕES</translation>
+<translation id="4491109536499578614">Imagem</translation>
<translation id="4588090240171750605">Deslocar-se para a direita</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, número de estrelas: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, recomendação de aplicação</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dia e }other{# dias e }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{em 1 a.}other{em # a.}}</translation>
+<translation id="588258955323874662">Ecrã inteiro</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Falta 1 segundo}other{Faltam # segundos}}</translation>
<translation id="5941711191222866238">Minimizar</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hora}other{# horas}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Assistente Google</translation>
<translation id="6430678249303439055">Bloquear todas as notificações desta aplicação</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Há 1 segundo}other{Há # segundos}}</translation>
+<translation id="6503257047630241175">Conteúdo RTF</translation>
<translation id="6539092367496845964">Ocorreu um erro. Tente novamente mais tarde.</translation>
<translation id="654149438358937226">Bloquear todas as notificações</translation>
<translation id="6567071839949112727">clicar no predecessor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ro.xtb b/chromium/ui/strings/translations/ui_strings_ro.xtb
index 199eca561aa..abe4d03df61 100644
--- a/chromium/ui/strings/translations/ui_strings_ro.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ro.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">selectează</translation>
<translation id="1859234291848436338">Direcție de scriere</translation>
<translation id="1860796786778352021">Buton de închidere a notificării</translation>
+<translation id="186476001994626254">Conținut cu inserare inteligentă de pe web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Select&amp;ează tot</translation>
+<translation id="19085484004813472">Aceasta este o funcție nouă</translation>
<translation id="2006524834898217237">Verifică dacă dispozitivul este conectat la internet.</translation>
<translation id="208586643495776849">Încearcă din nou</translation>
<translation id="2090963878406559571">Pentru a trimite un număr de la <ph name="ORIGIN" /> pe telefonul Android, <ph name="TROUBLESHOOT_LINK" /> pentru ambele dispozitive din setări.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Gestionează setările</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{în 1 h}few{în # h}other{în # h}}</translation>
<translation id="2583543531130364912">Calibrează ecranul tactil</translation>
+<translation id="2586657967955657006">Clipboard</translation>
<translation id="2666092431469916601">Sus</translation>
<translation id="2701330563083355633">Trimis de <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nou</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Deschideți dosarul</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Acum 1 an}few{Acum # ani}other{Acum # de ani}}</translation>
+<translation id="2878511608894704031">Șterge tot</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{în o zi}few{în # zile}other{în # zile}}</translation>
<translation id="2931838996092594335">dă clic</translation>
<translation id="2981684127883932071">Se afișează sugestiile</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Începeți să vorbiți</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> s-a mutat în dosarul <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TOATE APLICAȚIILE</translation>
+<translation id="4491109536499578614">Imagine</translation>
<translation id="4588090240171750605">Derulează spre dreapta</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, evaluare cu stele: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, aplicație recomandată</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{O zi și }few{# zile și }other{# de zile și }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{în 1 an}few{în # ani}other{în # ani}}</translation>
+<translation id="588258955323874662">Ecran complet</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{O secundă rămasă}few{# secunde rămase}other{# de secunde rămase}}</translation>
<translation id="5941711191222866238">Minimizează</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{o oră}few{# ore}other{# de ore}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistent Google</translation>
<translation id="6430678249303439055">Blochează toate notificările de la această aplicație</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Acum 1 secundă}few{Acum # secunde}other{Acum # de secunde}}</translation>
+<translation id="6503257047630241175">Conținut RTF</translation>
<translation id="6539092367496845964">A apărut o eroare. Încearcă din nou mai târziu.</translation>
<translation id="654149438358937226">Blochează toate notificările</translation>
<translation id="6567071839949112727">dă clic pe predecesor</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ru.xtb b/chromium/ui/strings/translations/ui_strings_ru.xtb
index 0b431f33e69..de470b55dc6 100644
--- a/chromium/ui/strings/translations/ui_strings_ru.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ru.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">выбрать</translation>
<translation id="1859234291848436338">Направление письма</translation>
<translation id="1860796786778352021">Закрыть уведомление</translation>
+<translation id="186476001994626254">Контент в формате Web Smart Paste</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Выделить &amp;все</translation>
+<translation id="19085484004813472">Это новая функция</translation>
<translation id="2006524834898217237">Убедитесь, что устройство подключено к Интернету.</translation>
<translation id="208586643495776849">Повторите попытку.</translation>
<translation id="2090963878406559571">Чтобы отправить номер с сайта <ph name="ORIGIN" /> на телефон Android, <ph name="TROUBLESHOOT_LINK" /> в настройках обоих устройств.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Настройки</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{через 1 ч.}one{через # ч.}few{через # ч.}many{через # ч.}other{через # ч.}}</translation>
<translation id="2583543531130364912">Откалибруйте сенсорный экран</translation>
+<translation id="2586657967955657006">Буфер обмена</translation>
<translation id="2666092431469916601">Наверх</translation>
<translation id="2701330563083355633">С устройства "<ph name="DEVICE_NAME" />"</translation>
<translation id="271033894570825754">Новый</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Открыть папку.</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{год назад}one{# год назад}few{# года назад}many{# лет назад}other{# года назад}}</translation>
+<translation id="2878511608894704031">Удалить все</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{через 1 д.}one{через # д.}few{через # д.}many{через # д.}other{через # д.}}</translation>
<translation id="2931838996092594335">нажать</translation>
<translation id="2981684127883932071">Показаны подсказки</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Начать голосовой ввод</translation>
<translation id="430191667033048642">Приложение "<ph name="MOVED_APP_NAME" />" перемещено в папку "<ph name="FOLDER_NAME" />".</translation>
<translation id="4316910396681052118">ВСЕ ПРИЛОЖЕНИЯ</translation>
+<translation id="4491109536499578614">Изображение</translation>
<translation id="4588090240171750605">Прокрутка вправо</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, рейтинг – <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, рекомендуемое приложение</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 день }one{# день }few{# дня }many{# дней }other{# дня }}</translation>
<translation id="5789643057113097023">:</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{через 1 г.}one{через # г.}few{через # г.}many{через # л.}other{через # г.}}</translation>
+<translation id="588258955323874662">Полноэкранный режим</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Осталась 1 секунда}one{Осталась # секунда}few{Осталось # секунды}many{Осталось # секунд}other{Осталось # секунды}}</translation>
<translation id="5941711191222866238">Свернуть</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 час}one{# час}few{# часа}many{# часов}other{# часа}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Ассистент</translation>
<translation id="6430678249303439055">Блокировать все уведомления этого приложения</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{только что}one{# секунду назад}few{# секунды назад}many{# секунд назад}other{# секунды назад}}</translation>
+<translation id="6503257047630241175">Контент в формате RTF</translation>
<translation id="6539092367496845964">Произошла ошибка. Повторите попытку позже.</translation>
<translation id="654149438358937226">Блокировать все уведомления</translation>
<translation id="6567071839949112727">нажмите на родительский элемент</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_si.xtb b/chromium/ui/strings/translations/ui_strings_si.xtb
index 8b54fab2a2b..fb78a3bcc36 100644
--- a/chromium/ui/strings/translations/ui_strings_si.xtb
+++ b/chromium/ui/strings/translations/ui_strings_si.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">තෝරන්න</translation>
<translation id="1859234291848436338">ලිවීමේ දිශාව</translation>
<translation id="1860796786778352021">දැන්වීම වසන්න</translation>
+<translation id="186476001994626254">වෙබ් ස්මාර්ට් ඇලවීම් අන්තර්ගතය</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">සියල්ල තෝරන්න</translation>
+<translation id="19085484004813472">මෙය නව විශේෂාංගයකි</translation>
<translation id="2006524834898217237">මෙම උපාංගය අන්තර්ජාලයට සම්බන්ධ වී තිබෙන බව තහවුරු කර ගන්න.</translation>
<translation id="208586643495776849">නැවත උත්සාහ කරන්න</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> සිට ඔබේ Android දුරකථනයට අංකයක් යැවීමට, සැකසීම් තුළ උපාංග දෙකම සඳහා <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">සැකසීම් කළමනාකරණය කරන්න</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 පැයකින්}one{පැය #කින්}other{පැය #කින්}}</translation>
<translation id="2583543531130364912">ඔබේ ස්පර්ශ තිරය ක්‍රමාංකනය කරන්න</translation>
+<translation id="2586657967955657006">පසුරු පුවරුව</translation>
<translation id="2666092431469916601">ඉහල</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> වෙතින් බෙදා ගන්නා ලදි</translation>
<translation id="271033894570825754">පුවත්</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ෆෝල්ඩරය විවෘත කරන්න</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{වසර 1කට පෙර}one{වසර #කට පෙර}other{වසර #කට පෙර}}</translation>
+<translation id="2878511608894704031">සියල්ල මකන්න</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 දිනකින්}one{දින #කින්}other{දින #කින්}}</translation>
<translation id="2931838996092594335">ක්ලික් කරන්න</translation>
<translation id="2981684127883932071">යෝජනා දැක්වීම</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">කතා කිරීම අරඹන්න</translation>
<translation id="430191667033048642"><ph name="FOLDER_NAME" /> ෆෝල්ඩරයට <ph name="MOVED_APP_NAME" /> ගෙන යැවිණි.</translation>
<translation id="4316910396681052118">සියලු යෙදුම්</translation>
+<translation id="4491109536499578614">රූපය</translation>
<translation id="4588090240171750605">දකුණට අනුචලනය කරන්න</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, තරු ඇගයීම <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, යෙදුම් නිර්දේශය</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{දින 1 ක් සහ }one{දින # ක් සහ }other{දින # ක් සහ }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 වසරකින්}one{වසර #කින්}other{වසර #කින්}}</translation>
+<translation id="588258955323874662">සම්පුර්ණ තිරය</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{තත්පර 1 ක් ඉතිරියි}one{තත්පර # ක් ඉතිරියි}other{තත්පර # ක් ඉතිරියි}}</translation>
<translation id="5941711191222866238">කුඩා කරන්න</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{පැය 1}one{පැය #}other{පැය #}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google සහායක</translation>
<translation id="6430678249303439055">මෙම යෙදුමෙන් එන දැනුම් දීම් සියල්ල අවහිර කරන්න</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{තත්පර 1කට පෙර}one{තත්පර #කට පෙර}other{තත්පර #කට පෙර}}</translation>
+<translation id="6503257047630241175">RTF අන්තර්ගතය</translation>
<translation id="6539092367496845964">යම් දෙයක් වැරදිණි. පසුව නැවත උත්සාහ කරන්න.</translation>
<translation id="654149438358937226">සියලු දැනුම්දීම් අවහිර කරන්න</translation>
<translation id="6567071839949112727">මුතුන්මිත්තා ක්ලික් කරන්න</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sk.xtb b/chromium/ui/strings/translations/ui_strings_sk.xtb
index e8118007695..2c661a5fad4 100644
--- a/chromium/ui/strings/translations/ui_strings_sk.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sk.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">vybrať</translation>
<translation id="1859234291848436338">Smer písania</translation>
<translation id="1860796786778352021">Zavrieť upozornenie</translation>
+<translation id="186476001994626254">Obsah pridaný inteligentným prilepením z webov</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Vybrať &amp;všetko</translation>
+<translation id="19085484004813472">Toto je nová funkcia</translation>
<translation id="2006524834898217237">Skontrolujte, či je toto zariadenie pripojené k internetu.</translation>
<translation id="208586643495776849">Skúste to znova</translation>
<translation id="2090963878406559571">Ak chcete zo zariadenia <ph name="ORIGIN" /> odoslať číslo do telefónu s Androidom, <ph name="TROUBLESHOOT_LINK" /> pre obidve zariadenia v nastaveniach.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Spravovať nastavenia</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{o 1 h}few{o # h}many{o # h}other{o # h}}</translation>
<translation id="2583543531130364912">Skalibrujte dotykovú obrazovku</translation>
+<translation id="2586657967955657006">Schránka</translation>
<translation id="2666092431469916601">Vrch</translation>
<translation id="2701330563083355633">Zdieľané zo zariadenia <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Nové</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otvoriť priečinok</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{pred rokom}few{pred # rokmi}many{pred # rokom}other{pred # rokmi}}</translation>
+<translation id="2878511608894704031">Odstrániť všetko</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{o 1 d.}few{o # d.}many{o # d.}other{o # d.}}</translation>
<translation id="2931838996092594335">kliknutie</translation>
<translation id="2981684127883932071">Zobrazujú sa návrhy</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Začať hovoriť</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> bola presunutá do priečinka <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">VŠETKY APLIKÁCIE</translation>
+<translation id="4491109536499578614">Obrázok</translation>
<translation id="4588090240171750605">Rolovať doprava</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, hodnotenie hviezdičkami <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, odporúčanie aplikácie</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 deň a }few{# dni a }many{# dňa a }other{# dní a }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{o 1 r.}few{o # r.}many{o # r.}other{o # r.}}</translation>
+<translation id="588258955323874662">Celá obrazovka</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Zostáva 1 sekunda}few{Zostávajú # sekundy}many{Zostáva # sekundy}other{Zostáva # sekúnd}}</translation>
<translation id="5941711191222866238">Minimalizovať</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 hodina}few{# hodiny}many{# hodiny}other{# hodín}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistent Google</translation>
<translation id="6430678249303439055">Blokovať všetky upozornenia z tejto aplikácie</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{pred sekundou}few{pred # sekundami}many{pred # sekundami}other{pred # sekundami}}</translation>
+<translation id="6503257047630241175">Obsah vo formáte RTF</translation>
<translation id="6539092367496845964">Vyskytol sa problém. Skúste to znova neskôr.</translation>
<translation id="654149438358937226">Blokovať všetky upozornenia</translation>
<translation id="6567071839949112727">kliknúť na nadradený objekt</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Uložiť súbor</translation>
<translation id="6656912866303152668">Skontrolujte, či má <ph name="TARGET_DEVICE_NAME" /> v Chrome zapnutú synchronizáciu, a skúste to odoslať znova.</translation>
<translation id="6699343763173986273">Média – ďalšia stopa</translation>
-<translation id="6710213216561001401">Dozadu</translation>
+<translation id="6710213216561001401">Späť</translation>
<translation id="6779314412797872738">Ak chcete odtiaľto odoslať číslo do telefónu s Androidom, <ph name="TROUBLESHOOT_LINK" /> pre obidve zariadenia v nastaveniach.</translation>
<translation id="6786750046913594791">Zatvoriť priečinok</translation>
<translation id="6808150112686056157">Médiá – zastaviť</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sl.xtb b/chromium/ui/strings/translations/ui_strings_sl.xtb
index dab1d0cec39..ca86f3d2459 100644
--- a/chromium/ui/strings/translations/ui_strings_sl.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sl.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">izberi</translation>
<translation id="1859234291848436338">Smer pisanja</translation>
<translation id="1860796786778352021">Zapri obvestilo</translation>
+<translation id="186476001994626254">Spletna vsebina za pametno lepljenje</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Izberi &amp;vse</translation>
+<translation id="19085484004813472">To je nova funkcija</translation>
<translation id="2006524834898217237">Naprava mora biti povezana z internetom.</translation>
<translation id="208586643495776849">Poskusite znova</translation>
<translation id="2090963878406559571">Če želite poslati številko z naslova <ph name="ORIGIN" /> v telefon Android, v nastavitvah obiščite to povezavo za odpravljanje težav za obe napravi: <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Upravljanje nastavitev</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{čez 1 h}one{čez # h}two{čez # h}few{čez # h}other{čez # h}}</translation>
<translation id="2583543531130364912">Umerjanje zaslona na dotik</translation>
+<translation id="2586657967955657006">Odložišče</translation>
<translation id="2666092431469916601">Na vrh</translation>
<translation id="2701330563083355633">V skupno rabo dala naprava: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Odpri mapo</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Pred 1 letom}one{Pred # letom}two{Pred # letoma}few{Pred # leti}other{Pred # leti}}</translation>
+<translation id="2878511608894704031">Izbriši vse</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{čez 1 d}one{čez # d}two{čez # d}few{čez # d}other{čez # d}}</translation>
<translation id="2931838996092594335">klikniti</translation>
<translation id="2981684127883932071">Prikazovanje predlogov</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642">Aplikacija <ph name="MOVED_APP_NAME" /> premaknjena v mapo <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">VSE APLIKACIJE</translation>
+<translation id="4491109536499578614">Slika</translation>
<translation id="4588090240171750605">Pomik desno</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ocena z zvezdicami (<ph name="RATING_SCORE" />)</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, priporočilo aplikacije</translation>
@@ -146,8 +151,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dan in }one{# dan in }two{# dneva in }few{# dni in }other{# dni in }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{čez 1 l}one{čez # l}two{čez # l}few{čez # l}other{čez # l}}</translation>
+<translation id="588258955323874662">Celozaslonsko</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Še 1 s}one{Še # s}two{Še # s}few{Še # s}other{Še # s}}</translation>
-<translation id="5941711191222866238">Pomanjšaj</translation>
+<translation id="5941711191222866238">Minimiziraj</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 h}one{# h}two{# h}few{# h}other{# h}}</translation>
<translation id="6012623610530968780"><ph name="SELECTED_PAGE" />. stran od <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">Izbris besedila v iskalnem polju</translation>
@@ -169,6 +175,7 @@
<translation id="6417265370957905582">Pomočnik Google</translation>
<translation id="6430678249303439055">Blokiraj vsa obvestila iz te aplikacije</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Pred 1 sekundo}one{Pred # sekundo}two{Pred # sekundama}few{Pred # sekundami}other{Pred # sekundami}}</translation>
+<translation id="6503257047630241175">Vsebina v obliki RTF</translation>
<translation id="6539092367496845964">Prišlo je do napake. Poskusite znova pozneje.</translation>
<translation id="654149438358937226">Blokiraj vsa obvestila</translation>
<translation id="6567071839949112727">kliknite prednika</translation>
@@ -177,7 +184,7 @@
<translation id="6620110761915583480">Shrani datoteko</translation>
<translation id="6656912866303152668">Poskrbite, da ima naprava <ph name="TARGET_DEVICE_NAME" /> vklopljeno sinhronizacijo v Chromu, nato poskusite poslati znova.</translation>
<translation id="6699343763173986273">Naslednja skladba</translation>
-<translation id="6710213216561001401">Nazaj</translation>
+<translation id="6710213216561001401">Prejšnji</translation>
<translation id="6779314412797872738">Če želite poslati številko iz te naprave v telefon Android, v nastavitvah obiščite to povezavo za odpravljanje težav za obe napravi: <ph name="TROUBLESHOOT_LINK" />.</translation>
<translation id="6786750046913594791">Zapri mapo</translation>
<translation id="6808150112686056157">Ustavitev</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sq.xtb b/chromium/ui/strings/translations/ui_strings_sq.xtb
index 958d3801534..96ca5f9bd3e 100644
--- a/chromium/ui/strings/translations/ui_strings_sq.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sq.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">përzgjidh</translation>
<translation id="1859234291848436338">Drejtimi i shkrimit</translation>
<translation id="1860796786778352021">Mbyllja e njoftimit</translation>
+<translation id="186476001994626254">Përmbajtja e ngjitjes inteligjente për ueb</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Zgjidhi &amp;të gjitha</translation>
+<translation id="19085484004813472">Kjo është një veçori e re</translation>
<translation id="2006524834898217237">Sigurohu që kjo pajisje të jetë e lidhur me internetin.</translation>
<translation id="208586643495776849">Provo sërish</translation>
<translation id="2090963878406559571">Për të dërguar një numër nga <ph name="ORIGIN" /> te telefoni yt Android, <ph name="TROUBLESHOOT_LINK" /> për të dyja pajisjet te cilësimet.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Menaxho cilësimet</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{në 1 orë}other{në # orë}}</translation>
<translation id="2583543531130364912">Kalibro ekranin me prekje</translation>
+<translation id="2586657967955657006">Kujtesa e fragmenteve</translation>
<translation id="2666092431469916601">I sipërm</translation>
<translation id="2701330563083355633">Ndarë nga <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">E re</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Hap dosjen</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 vit më parë}other{# vite më parë}}</translation>
+<translation id="2878511608894704031">Fshi të gjitha</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{në 1 ditë}other{në # ditë}}</translation>
<translation id="2931838996092594335">kliko</translation>
<translation id="2981684127883932071">Po shfaq sugjerimet</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Fillo të flasësh</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> u zhvendos te skedari <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TË GJITHA APLIKACIONET</translation>
+<translation id="4491109536499578614">Imazh</translation>
<translation id="4588090240171750605">Lëvize djathtas</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, vlerësimi me yje <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, rekomandim për aplikacione</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ditë e }other{# ditë e }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{në 1 vit}other{në # vite}}</translation>
+<translation id="588258955323874662">Ekrani i plotë</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekondë e mbetur}other{# sekonda të mbetura}}</translation>
<translation id="5941711191222866238">Minimizo</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 orë}other{# orë}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Asistenti i Google</translation>
<translation id="6430678249303439055">Blloko të gjitha njoftimet nga ky aplikacion</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 sekondë më parë}other{# sekonda më parë}}</translation>
+<translation id="6503257047630241175">Përmbajtja në formatin RTF</translation>
<translation id="6539092367496845964">Ndodhi një gabim. Provo përsëri më vonë.</translation>
<translation id="654149438358937226">Blloko të gjitha njoftimet</translation>
<translation id="6567071839949112727">kliko te paraardhësi</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sr-Latn.xtb b/chromium/ui/strings/translations/ui_strings_sr-Latn.xtb
index 21d7321b49f..8f0ee636edf 100644
--- a/chromium/ui/strings/translations/ui_strings_sr-Latn.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sr-Latn.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">izaberi</translation>
<translation id="1859234291848436338">Smer pisanja</translation>
<translation id="1860796786778352021">Zatvori obaveštenje</translation>
+<translation id="186476001994626254">Veb-sadržaj za pametno nalepljivanje</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Izaberi &amp;sve</translation>
+<translation id="19085484004813472">Ovo je nova funkcija</translation>
<translation id="2006524834898217237">Uverite se da je ovaj uređaj povezan na internet.</translation>
<translation id="208586643495776849">Probajte ponovo</translation>
<translation id="2090963878406559571">Da biste poslali broj sa <ph name="ORIGIN" /> na Android telefon, <ph name="TROUBLESHOOT_LINK" /> za oba uređaja u podešavanjima.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Upravljajte podešavanjima</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{za 1 č}one{za # č}few{za # č}other{za # č}}</translation>
<translation id="2583543531130364912">Kalibracija dodirnog ekrana</translation>
+<translation id="2586657967955657006">Memorija</translation>
<translation id="2666092431469916601">Vrh</translation>
<translation id="2701330563083355633">Deljeno sa: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Novo</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Otvorite direktorijum</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Pre godinu dana}one{Pre # godinu}few{Pre # godine}other{Pre # godina}}</translation>
+<translation id="2878511608894704031">Izbriši sve</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{za 1 d}one{za # d}few{za # d}other{za # d}}</translation>
<translation id="2931838996092594335">klik</translation>
<translation id="2981684127883932071">Prikazuju se predlozi</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Počnite da govorite</translation>
<translation id="430191667033048642">Aplikacija <ph name="MOVED_APP_NAME" /> je premeštena u direktorijum <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">SVE APLIKACIJE</translation>
+<translation id="4491109536499578614">Slika</translation>
<translation id="4588090240171750605">Pomeri nadesno</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, ocena u zvezdicama <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, preporučena aplikacija</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dan i }one{# dan i }few{# dana i }other{# dana i }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{za 1 g}one{za # g}few{za # g}other{za # g}}</translation>
+<translation id="588258955323874662">Ceo ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Još 1 sekunda}one{Još # sekunda}few{Još # sekunde}other{Još # sekundi}}</translation>
-<translation id="5941711191222866238">Smanji</translation>
+<translation id="5941711191222866238">Umanji</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 sat}one{# sat}few{# sata}other{# sati}}</translation>
<translation id="6012623610530968780"><ph name="SELECTED_PAGE" />. stranica od <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">Obrišite tekst iz okvira za pretragu</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google pomoćnik</translation>
<translation id="6430678249303439055">Blokiraj sva obaveštenja iz ove aplikacije</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Pre 1 sekundu}one{Pre # sekundu}few{Pre # sekunde}other{Pre # sekundi}}</translation>
+<translation id="6503257047630241175">RTF sadržaj</translation>
<translation id="6539092367496845964">Nešto nije u redu. Probajte ponovo kasnije.</translation>
<translation id="654149438358937226">Blokiraj sva obaveštenja</translation>
<translation id="6567071839949112727">kliknite na prethodni element</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sr.xtb b/chromium/ui/strings/translations/ui_strings_sr.xtb
index ad630f74399..2e62f688c8f 100644
--- a/chromium/ui/strings/translations/ui_strings_sr.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sr.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">изабери</translation>
<translation id="1859234291848436338">Смер писања</translation>
<translation id="1860796786778352021">Затвори обавештење</translation>
+<translation id="186476001994626254">Веб-садржај за паметно налепљивање</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Изабери &amp;све</translation>
+<translation id="19085484004813472">Ово је нова функција</translation>
<translation id="2006524834898217237">Уверите се да је овај уређај повезан на интернет.</translation>
<translation id="208586643495776849">Пробајте поново</translation>
<translation id="2090963878406559571">Да бисте послали број са <ph name="ORIGIN" /> на Android телефон, <ph name="TROUBLESHOOT_LINK" /> за оба уређаја у подешавањима.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Управљајте подешавањима</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{за 1 ч}one{за # ч}few{за # ч}other{за # ч}}</translation>
<translation id="2583543531130364912">Калибрација додирног екрана</translation>
+<translation id="2586657967955657006">Меморија</translation>
<translation id="2666092431469916601">Врх</translation>
<translation id="2701330563083355633">Дељено са: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Ново</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Отворите директоријум</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Пре годину дана}one{Пре # годину}few{Пре # године}other{Пре # година}}</translation>
+<translation id="2878511608894704031">Избриши све</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{за 1 д}one{за # д}few{за # д}other{за # д}}</translation>
<translation id="2931838996092594335">клик</translation>
<translation id="2981684127883932071">Приказују се предлози</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Почните да говорите</translation>
<translation id="430191667033048642">Апликација <ph name="MOVED_APP_NAME" /> је премештена у директоријум <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">СВЕ АПЛИКАЦИЈЕ</translation>
+<translation id="4491109536499578614">Слика</translation>
<translation id="4588090240171750605">Помери надесно</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, оцена у звездицама <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, препоручена апликација</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 дан и }one{# дан и }few{# дана и }other{# дана и }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{за 1 г}one{за # г}few{за # г}other{за # г}}</translation>
+<translation id="588258955323874662">Цеo екран</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Још 1 секунда}one{Још # секунда}few{Још # секунде}other{Још # секунди}}</translation>
-<translation id="5941711191222866238">Смањи</translation>
+<translation id="5941711191222866238">Умањи</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 сат}one{# сат}few{# сата}other{# сати}}</translation>
<translation id="6012623610530968780"><ph name="SELECTED_PAGE" />. страница од <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">Обришите текст из оквира за претрагу</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google помоћник</translation>
<translation id="6430678249303439055">Блокирај сва обавештења из ове апликације</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Пре 1 секунду}one{Пре # секунду}few{Пре # секунде}other{Пре # секунди}}</translation>
+<translation id="6503257047630241175">RTF садржај</translation>
<translation id="6539092367496845964">Нешто није у реду. Пробајте поново касније.</translation>
<translation id="654149438358937226">Блокирај сва обавештења</translation>
<translation id="6567071839949112727">кликните на претходни елемент</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sv.xtb b/chromium/ui/strings/translations/ui_strings_sv.xtb
index 9bdbe567ac4..940e5ea98d2 100644
--- a/chromium/ui/strings/translations/ui_strings_sv.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sv.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">välj</translation>
<translation id="1859234291848436338">Skrivriktning</translation>
<translation id="1860796786778352021">Meddelande om stängning</translation>
+<translation id="186476001994626254">Web Smart Paste-innehåll</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Välj &amp;alla</translation>
+<translation id="19085484004813472">Det här är en ny funktion</translation>
<translation id="2006524834898217237">Kontrollera att enheten är ansluten till internet.</translation>
<translation id="208586643495776849">Försök igen</translation>
<translation id="2090963878406559571"><ph name="TROUBLESHOOT_LINK" /> i inställningarna för båda enheterna om du vill skicka ett nummer från <ph name="ORIGIN" /> till Android-telefonen härifrån.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Hantera inställningar</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{om 1 tim}other{om # tim}}</translation>
<translation id="2583543531130364912">Kalibrera pekskärmen</translation>
+<translation id="2586657967955657006">Urklipp</translation>
<translation id="2666092431469916601">Överst</translation>
<translation id="2701330563083355633">Delades från <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Ny</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Öppna mappen</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 år sedan}other{# år sedan}}</translation>
+<translation id="2878511608894704031">Radera allt</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{om 1 dag}other{om # dgr}}</translation>
<translation id="2931838996092594335">klicka</translation>
<translation id="2981684127883932071">Visar förslag</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Börja tala</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> har flyttats till mappen <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ALLA APPAR</translation>
+<translation id="4491109536499578614">Bild</translation>
<translation id="4588090240171750605">Rulla åt höger</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, betyg <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, apprekommendation</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 dag och }other{# dagar och }}</translation>
<translation id="5789643057113097023">,</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{om 1 år}other{om # år}}</translation>
+<translation id="588258955323874662">Helskärm</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 sekund kvar}other{# sekunder kvar}}</translation>
<translation id="5941711191222866238">Minimera</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 timme}other{# timmar}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Blockera alla aviseringar från den här appen</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 sekund sedan}other{# sekunder sedan}}</translation>
+<translation id="6503257047630241175">RTF-innehåll</translation>
<translation id="6539092367496845964">Något gick fel. Försök igen senare.</translation>
<translation id="654149438358937226">Blockera alla aviseringar</translation>
<translation id="6567071839949112727">klicka på det överordnade objektet</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_sw.xtb b/chromium/ui/strings/translations/ui_strings_sw.xtb
index 1bab6353510..fbb9b3424fd 100644
--- a/chromium/ui/strings/translations/ui_strings_sw.xtb
+++ b/chromium/ui/strings/translations/ui_strings_sw.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">chagua</translation>
<translation id="1859234291848436338">Mwelekeo wa Maandishi</translation>
<translation id="1860796786778352021">Funga arifa</translation>
+<translation id="186476001994626254">Maudhui ya Ubandikaji Mahiri wa Wavuti</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Chagua &amp;yote</translation>
+<translation id="19085484004813472">Hiki ni kipengele kipya</translation>
<translation id="2006524834898217237">Hakikisha kwamba kifaa hiki kimeunganishwa kwenye intaneti.</translation>
<translation id="208586643495776849">Tafadhali jaribu tena</translation>
<translation id="2090963878406559571">Ili utume nambari kutoka <ph name="ORIGIN" /> kwenda kwenye simu yako ya Android, <ph name="TROUBLESHOOT_LINK" /> katika vifaa vyote viwili kwenye mipangilio.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Dhibiti mipangilio</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{saa 1}other{saa #}}</translation>
<translation id="2583543531130364912">Rekebisha usahihi wa skrini yako ya kugusa</translation>
+<translation id="2586657967955657006">Ubao wa kunakili</translation>
<translation id="2666092431469916601">Ya Juu</translation>
<translation id="2701330563083355633">Umepokea kutoka <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Mpya</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Fungua folda</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{Mwaka 1 uliopita}other{Miaka # iliyopita}}</translation>
+<translation id="2878511608894704031">Futa Vyote</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{siku 1}other{siku #}}</translation>
<translation id="2931838996092594335">bofya</translation>
<translation id="2981684127883932071">Inaonyesha mapendekezo</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Anza Kuzungumza</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> imehamishiwa kwenye folda ya <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">PROGRAMU ZOTE</translation>
+<translation id="4491109536499578614">Picha</translation>
<translation id="4588090240171750605">Sogeza Kulia</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Ukadiriaji wa nyota <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Pendekezo la programu</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{Siku 1 na }other{Siku # na }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{mwaka 1}other{miaka #}}</translation>
+<translation id="588258955323874662">Skrini nzima</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Imesalia sekunde 1}other{Zimesalia sekunde #}}</translation>
<translation id="5941711191222866238">Punguza</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{Saa 1}other{Saa #}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Mratibu wa Google</translation>
<translation id="6430678249303439055">Zuia arifa zote kutoka programu hii</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Sekunde 1 iliyopita}other{Sekunde # zilizopita}}</translation>
+<translation id="6503257047630241175">Maudhui ya Muundo wa Maandishi Anuai (RTF)</translation>
<translation id="6539092367496845964">Hitilafu fulani imetokea. Jaribu tena baadaye.</translation>
<translation id="654149438358937226">Zuia arifa zote</translation>
<translation id="6567071839949112727">bofya kipengee</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Hifadhi Faili</translation>
<translation id="6656912866303152668">Hakikisha kwamba umewasha kipengele cha usawazishaji katika <ph name="TARGET_DEVICE_NAME" /> kwenye Chrome kisha ujaribu kutuma tena.</translation>
<translation id="6699343763173986273">Wimbo Unaofuata kwenye Media</translation>
-<translation id="6710213216561001401">Iliyotangulia</translation>
+<translation id="6710213216561001401">Iliyopita</translation>
<translation id="6779314412797872738">Ili utume nambari kutoka hapa hadi kwenye simu yako ya Android, <ph name="TROUBLESHOOT_LINK" /> kwenye vifaa vyote viwili katika mipangilio.</translation>
<translation id="6786750046913594791">Funga Folda</translation>
<translation id="6808150112686056157">Simamisha Media</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ta.xtb b/chromium/ui/strings/translations/ui_strings_ta.xtb
index 23b5f0dc726..99ac657ba30 100644
--- a/chromium/ui/strings/translations/ui_strings_ta.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ta.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">தேர்ந்தெடு</translation>
<translation id="1859234291848436338">எழுதும் திசை</translation>
<translation id="1860796786778352021">அறிவிப்பை மூடு</translation>
+<translation id="186476001994626254">இணைய ஸ்மார்ட் ஒட்டுதல் உள்ளடக்கம்</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">அ&amp;னைத்தையும் தேர்ந்தெடு</translation>
+<translation id="19085484004813472">இது ஒரு புதிய அம்சம்</translation>
<translation id="2006524834898217237">இந்த சாதனம் இணையத்துடன் இணைக்கப்பட்டுள்ளதை உறுதி செய்யவும்.</translation>
<translation id="208586643495776849">மீண்டும் முயலவும்</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> இலிருந்து உங்கள் Android மொபைலுக்கு எண்ணை அனுப்ப இரு சாதனங்களிலும் உள்ள அமைப்புகளுக்குச் சென்று <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">அமைப்புகளை நிர்வகியுங்கள்</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{இன்னும் 1ம}other{இன்னும் #ம}}</translation>
<translation id="2583543531130364912">டச்ஸ்கிரீனை அளவுத்திருத்தம் செய்யவும்</translation>
+<translation id="2586657967955657006">கிளிப்போர்டு</translation>
<translation id="2666092431469916601">மேலே</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> சாதனத்தில் இருந்து பகிரப்பட்டுள்ளது</translation>
<translation id="271033894570825754">புதிது</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">கோப்புறையைத் திற</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ஆண்டுக்கு முன்}other{# ஆண்டுகளுக்கு முன்}}</translation>
+<translation id="2878511608894704031">அனைத்தையும் நீக்கு</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{இன்னும் 1நா}other{இன்னும் #நா}}</translation>
<translation id="2931838996092594335">கிளிக் செய்க</translation>
<translation id="2981684127883932071">பரிந்துரைகளைக் காட்டுகிறது</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">பேச்சைத் தொடங்கு</translation>
<translation id="430191667033048642"><ph name="FOLDER_NAME" /> கோப்புறைக்கு <ph name="MOVED_APP_NAME" /> ஆப்ஸ் நகர்த்தப்பட்டது.</translation>
<translation id="4316910396681052118">எல்லாப் பயன்பாடுகளும்</translation>
+<translation id="4491109536499578614">படம்</translation>
<translation id="4588090240171750605">வலப்புறம் நகர்த்து</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, நட்சத்திர மதிப்பீடு: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, ஆப்ஸ் பரிந்துரை</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 நாள், }other{# நாட்கள், }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{இன்னும் 1ஆ}other{இன்னும் #ஆ}}</translation>
+<translation id="588258955323874662">முழுத்திரை</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 நிமிடம் உள்ளது}other{# நிமிடங்கள் உள்ளன}}</translation>
<translation id="5941711191222866238">சிறிதாக்கு</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 மணிநேரம்}other{# மணிநேரம்}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">இந்தப் பயன்பாட்டிலிருந்து எல்லா அறிவிப்புகளையும் தடு</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 வினாடிக்கு முன்பு}other{# வினாடிகளுக்கு முன்பு}}</translation>
+<translation id="6503257047630241175">RTF உள்ளடக்கம்</translation>
<translation id="6539092367496845964">ஏதோ தவறாகிவிட்டது. பிறகு முயலவும்.</translation>
<translation id="654149438358937226">எல்லா அறிவிப்புகளையும் தடு</translation>
<translation id="6567071839949112727">ஆன்செஸ்டரைக் கிளிக் செய்</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_te.xtb b/chromium/ui/strings/translations/ui_strings_te.xtb
index c0812b6e2a7..4ec800e2764 100644
--- a/chromium/ui/strings/translations/ui_strings_te.xtb
+++ b/chromium/ui/strings/translations/ui_strings_te.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">ఎంచుకోండి</translation>
<translation id="1859234291848436338">వ్రాసే దిశ</translation>
<translation id="1860796786778352021">నోటిఫికేషన్‌ను మూసివేయి</translation>
+<translation id="186476001994626254">వెబ్ స్మార్ట్ పేస్ట్ కంటెంట్</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;అన్నీ ఎంచుకోండి</translation>
+<translation id="19085484004813472">ఇది కొత్త ఫీచర్</translation>
<translation id="2006524834898217237">ఈ పరికరం ఇంటర్నెట్‌కు కనెక్ట్ చేయబడి ఉందని నిర్ధారించుకోండి.</translation>
<translation id="208586643495776849">దయచేసి మళ్లీ ప్రయత్నించండి</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> నుండి మీ Android ఫోన్‌కు నంబర్‌ను పంపడానికి, రెండు పరికరాలలోని సెట్టింగ్‌లలో <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -58,7 +60,7 @@
<translation id="2388990488038464401">మీ ఫోన్ నుండి ఈ నంబర్‌‌కు కాల్ చేయాలా?</translation>
<translation id="24452542372838207">నోటిఫికేషన్‌ను విస్తరించు</translation>
<translation id="2445449901874883781">ఎక్కువ సాంద్రతను ఉపయోగించు</translation>
-<translation id="2482878487686419369">ప్రకటనలు</translation>
+<translation id="2482878487686419369">నోటిఫికేషన్‌లు</translation>
<translation id="2497284189126895209">మొత్తం ఫైళ్లు</translation>
<translation id="2515586267016047495">Alt</translation>
<translation id="2522350507219695259">క్రమాంకనం పూర్తయింది</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">సెట్టింగ్‌లను మేనేజ్ చేయండి</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 గంటలో}other{#గంటలలో}}</translation>
<translation id="2583543531130364912">మీ టచ్‌స్క్రీన్‌ను క్రమాంకనం చేయండి</translation>
+<translation id="2586657967955657006">క్లిప్‌బోర్డ్</translation>
<translation id="2666092431469916601">పైన</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> నుండి షేర్ చేయబడింది</translation>
<translation id="271033894570825754">కొత్తది</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">ఫోల్డర్‌ను తెరవండి</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 సంవత్సరం క్రితం}other{# సంవత్సరాల క్రితం}}</translation>
+<translation id="2878511608894704031">అన్నీ తొలగించు</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 రోజులో}other{# రోజులో}}</translation>
<translation id="2931838996092594335">క్లిక్ చేయి</translation>
<translation id="2981684127883932071">సూచనలను ప్రదర్శిస్తోంది</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">మాట్లాడటాన్ని ప్రారంభించు</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> అనేది <ph name="FOLDER_NAME" /> ఫోల్డర్‌కు తరలించబడింది.</translation>
<translation id="4316910396681052118">అన్ని అనువర్తనాలు</translation>
+<translation id="4491109536499578614">చిత్రం</translation>
<translation id="4588090240171750605">కుడికి స్క్రోల్ చేయి</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, నక్షత్ర రేటింగ్ <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, యాప్ సిఫార్సు</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 రోజు మరియు }other{# రోజులు మరియు }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 సంవత్సరంలో}other{# సంవత్సరంలో}}</translation>
+<translation id="588258955323874662">పూర్తితెర</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 సెకను మిగిలి ఉంది}other{# సెకన్లు మిగిలి ఉన్నాయి}}</translation>
<translation id="5941711191222866238">కనిష్టీకరించు</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 గంట}other{# గంటలు}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">ఈ యాప్ నుండి నోటిఫికేషన్‌లు అన్ని బ్లాక్ చేయండి</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 సెకను క్రితం}other{# సెకన్ల క్రితం}}</translation>
+<translation id="6503257047630241175">RTF కంటెంట్</translation>
<translation id="6539092367496845964">ఏదో తప్పు జరిగింది. తర్వాత మళ్లీ ప్రయత్నించండి.</translation>
<translation id="654149438358937226">అన్ని నోటిఫికేషన్‌లను బ్లాక్ చేయి</translation>
<translation id="6567071839949112727">క్లిక్ మూలకం</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_th.xtb b/chromium/ui/strings/translations/ui_strings_th.xtb
index e43a1cd912b..34a0fa35b8c 100644
--- a/chromium/ui/strings/translations/ui_strings_th.xtb
+++ b/chromium/ui/strings/translations/ui_strings_th.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">เลือก</translation>
<translation id="1859234291848436338">การเขียนเส้นทาง</translation>
<translation id="1860796786778352021">ปิดการแจ้งเตือน</translation>
+<translation id="186476001994626254">Web Smart Paste Content</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">เลือก&amp;ทั้งหมด</translation>
+<translation id="19085484004813472">นี่เป็นฟีเจอร์ใหม่</translation>
<translation id="2006524834898217237">ตรวจสอบว่าอุปกรณ์นี้เชื่อมต่ออินเทอร์เน็ตแล้ว</translation>
<translation id="208586643495776849">โปรดลองอีกครั้ง</translation>
<translation id="2090963878406559571">หากต้องการส่งหมายเลขจาก <ph name="ORIGIN" /> ไปยังโทรศัพท์ Android ให้<ph name="TROUBLESHOOT_LINK" />อุปกรณ์ทั้งสองในการตั้งค่า</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">จัดการการตั้งค่า</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{ใน 1 ชม.}other{ใน # ชม.}}</translation>
<translation id="2583543531130364912">ปรับเทียบหน้าจอสัมผัส</translation>
+<translation id="2586657967955657006">คลิปบอร์ด</translation>
<translation id="2666092431469916601">ด้านบน</translation>
<translation id="2701330563083355633">แชร์จาก <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">ใหม่</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">เปิดโฟลเดอร์</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 ปีที่ผ่านมา}other{# ปีที่ผ่านมา}}</translation>
+<translation id="2878511608894704031">ลบทั้งหมด</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{ใน 1 วัน}other{ใน # วัน}}</translation>
<translation id="2931838996092594335">คลิก</translation>
<translation id="2981684127883932071">กำลังแสดงคำแนะนำ</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">เริ่มพูด</translation>
<translation id="430191667033048642">ย้าย <ph name="MOVED_APP_NAME" /> ไปยังโฟลเดอร์ <ph name="FOLDER_NAME" /> แล้ว</translation>
<translation id="4316910396681052118">แอปทั้งหมด</translation>
+<translation id="4491109536499578614">รูปภาพ</translation>
<translation id="4588090240171750605">เลื่อนทางขวา</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, การให้ดาว <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, แอปแนะนำ</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 วันกับอีก }other{# วันกับอีก }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{ใน 1 ปี}other{ใน # ปี}}</translation>
+<translation id="588258955323874662">เต็มหน้าจอ</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{เหลือ 1 วินาที}other{เหลือ # วินาที}}</translation>
-<translation id="5941711191222866238">ย่อ</translation>
+<translation id="5941711191222866238">ย่อเล็กสุด</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ชั่วโมง}other{# ชั่วโมง}}</translation>
<translation id="6012623610530968780">หน้า <ph name="SELECTED_PAGE" /> จาก <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">ล้างข้อความในช่องค้นหา</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistant</translation>
<translation id="6430678249303439055">บล็อกการแจ้งเตือนทั้งหมดจากแอปนี้</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{ 1 วินาทีที่ผ่านมา}other{# วินาทีที่ผ่านมา}}</translation>
+<translation id="6503257047630241175">เนื้อหา RTF</translation>
<translation id="6539092367496845964">เกิดข้อผิดพลาด ลองอีกครั้งภายหลัง</translation>
<translation id="654149438358937226">บล็อกการแจ้งเตือนทั้งหมด</translation>
<translation id="6567071839949112727">คลิกต้นตระกูล</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_tr.xtb b/chromium/ui/strings/translations/ui_strings_tr.xtb
index 26d5be2b2e7..69f249964fb 100644
--- a/chromium/ui/strings/translations/ui_strings_tr.xtb
+++ b/chromium/ui/strings/translations/ui_strings_tr.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">seç</translation>
<translation id="1859234291848436338">Yazma Yönü</translation>
<translation id="1860796786778352021">Bildirimi kapat</translation>
+<translation id="186476001994626254">Web Smart Paste İçeriği</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652">%<ph name="NUMBER" /></translation>
<translation id="1901303067676059328">Tümünü &amp;seç</translation>
+<translation id="19085484004813472">Bu yeni bir özelliktir</translation>
<translation id="2006524834898217237">Bu cihazın internete bağlı olduğundan emin olun.</translation>
<translation id="208586643495776849">Lütfen tekrar deneyin</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> sitesinden Android telefonunuza bir numara göndermek üzere ayarlarda her iki cihaz için <ph name="TROUBLESHOOT_LINK" />.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Ayarları yönet</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 saat içinde}other{# saat içinde}}</translation>
<translation id="2583543531130364912">Dokunmatik ekranınızı ayarlayın</translation>
+<translation id="2586657967955657006">Pano</translation>
<translation id="2666092431469916601">Üst</translation>
<translation id="2701330563083355633">Paylaşan: <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Yeni</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Klasörü aç</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 yıl önce}other{# yıl önce}}</translation>
+<translation id="2878511608894704031">Tümünü Sil</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 gün içinde}other{# gün içinde}}</translation>
<translation id="2931838996092594335">tıklama</translation>
<translation id="2981684127883932071">Öneriler gösteriliyor</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Konuşmaya Başla</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" />, <ph name="FOLDER_NAME" />klasörüne taşındı</translation>
<translation id="4316910396681052118">TÜM UYGULAMALAR</translation>
+<translation id="4491109536499578614">Resim</translation>
<translation id="4588090240171750605">Sağa Kaydır</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, <ph name="RATING_SCORE" /> Yıldız puanı</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Uygulama önerisi</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 gün ve }other{# gün ve }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 yıl içinde}other{# yıl içinde}}</translation>
+<translation id="588258955323874662">Tam ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 saniye kaldı}other{# saniye kaldı}}</translation>
-<translation id="5941711191222866238">Simge durumuna küçült</translation>
+<translation id="5941711191222866238">Küçült</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 saat}other{# saat}}</translation>
<translation id="6012623610530968780">Sayfa <ph name="SELECTED_PAGE" />/<ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">Arama kutusu metnini temizle</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Asistan</translation>
<translation id="6430678249303439055">Bu uygulamadan gelen tüm bildirimleri engelle</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 saniye önce}other{# saniye önce}}</translation>
+<translation id="6503257047630241175">RTF İçeriği</translation>
<translation id="6539092367496845964">Bir sorun oldu. Daha sonra tekrar deneyin.</translation>
<translation id="654149438358937226">Tüm bildirimleri engelle</translation>
<translation id="6567071839949112727">üst öğeyi tıkla</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_uk.xtb b/chromium/ui/strings/translations/ui_strings_uk.xtb
index 6d2e4a137dc..4df93d21f16 100644
--- a/chromium/ui/strings/translations/ui_strings_uk.xtb
+++ b/chromium/ui/strings/translations/ui_strings_uk.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">вибрати</translation>
<translation id="1859234291848436338">Напрямок письма</translation>
<translation id="1860796786778352021">Закрити сповіщення</translation>
+<translation id="186476001994626254">Веб-контент для розумного вставлення</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" />%</translation>
<translation id="1901303067676059328">Вибрати &amp;всі</translation>
+<translation id="19085484004813472">Це нова функція</translation>
<translation id="2006524834898217237">Перевірте, чи пристрій підключено до Інтернету.</translation>
<translation id="208586643495776849">Повторіть спробу</translation>
<translation id="2090963878406559571">Щоб надіслати номер із сайту <ph name="ORIGIN" /> на телефон Android, <ph name="TROUBLESHOOT_LINK" /> для обох пристроїв у налаштуваннях.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Керувати налаштуваннями</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{через 1 год}one{через # год}few{через # год}many{через # год}other{через # год}}</translation>
<translation id="2583543531130364912">Відкалібруйте сенсорний екран</translation>
+<translation id="2586657967955657006">Буфер обміну</translation>
<translation id="2666092431469916601">Верх</translation>
<translation id="2701330563083355633">Надіслано з пристрою "<ph name="DEVICE_NAME" />"</translation>
<translation id="271033894570825754">Новий</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Відкрити папку</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 рік тому}one{# рік тому}few{# роки тому}many{# років тому}other{# року тому}}</translation>
+<translation id="2878511608894704031">Видалити все</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{через 1 дн.}one{через # дн.}few{через # дн.}many{через # дн.}other{через # дн.}}</translation>
<translation id="2931838996092594335">натиснути</translation>
<translation id="2981684127883932071">Показано пропозиції</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642">Додаток <ph name="MOVED_APP_NAME" /> переміщено в папку "<ph name="FOLDER_NAME" />".</translation>
<translation id="4316910396681052118">УСІ ДОДАТКИ</translation>
+<translation id="4491109536499578614">Зображення</translation>
<translation id="4588090240171750605">Прокрутка вправо</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, оцінка – <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, рекомендований додаток</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 день і }one{# день і }few{# дні та }many{# днів і }other{# дня та }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{через 1 р.}one{через # р.}few{через # р.}many{через # р.}other{через # р.}}</translation>
+<translation id="588258955323874662">Повноекранний режим</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Залишилась 1 секунда}one{Залишилася # секунда}few{Залишилося # секунди}many{Залишилося # секунд}other{Залишилося # секунди}}</translation>
-<translation id="5941711191222866238">Зменшити</translation>
+<translation id="5941711191222866238">Згорнути</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 година}one{# година}few{# години}many{# годин}other{# години}}</translation>
<translation id="6012623610530968780">Сторінка <ph name="SELECTED_PAGE" /> з <ph name="TOTAL_PAGE_NUM" /></translation>
<translation id="6022924867608035986">Очистити вікно пошуку</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Асистент</translation>
<translation id="6430678249303439055">Блокувати всі сповіщення від цього додатка</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 секунду тому}one{# секунду тому}few{# секунди тому}many{# секунд тому}other{# секунди тому}}</translation>
+<translation id="6503257047630241175">Контент у форматі RTF</translation>
<translation id="6539092367496845964">Сталася помилка. Спробуйте пізніше.</translation>
<translation id="654149438358937226">Блокувати всі сповіщення</translation>
<translation id="6567071839949112727">натиснути предок</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Зберегти файл</translation>
<translation id="6656912866303152668">Переконайтеся, що на пристрої<ph name="TARGET_DEVICE_NAME" /> увімкнено синхронізацію в Chrome, а потім спробуйте надіслати знову.</translation>
<translation id="6699343763173986273">Наступна композиція</translation>
-<translation id="6710213216561001401">Попереднє</translation>
+<translation id="6710213216561001401">Назад</translation>
<translation id="6779314412797872738">Щоб надіслати номер на телефон Android, <ph name="TROUBLESHOOT_LINK" /> для обох пристроїв у налаштуваннях.</translation>
<translation id="6786750046913594791">Закрити папку</translation>
<translation id="6808150112686056157">Зупинити</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_ur.xtb b/chromium/ui/strings/translations/ui_strings_ur.xtb
index 5afe31594ba..2aee37e54f1 100644
--- a/chromium/ui/strings/translations/ui_strings_ur.xtb
+++ b/chromium/ui/strings/translations/ui_strings_ur.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">منتخب کریں</translation>
<translation id="1859234291848436338">تحریر کا ڈائریکشن</translation>
<translation id="1860796786778352021">اطلاع بند</translation>
+<translation id="186476001994626254">ویب اسمارٹ پیسٹ مواد</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">&amp;سبھی کو منتخب کریں</translation>
+<translation id="19085484004813472">یہ نئی خصوصیت ہے</translation>
<translation id="2006524834898217237">یہ یقینی بنائیں کہ آپ کا آلہ انٹرنیٹ سے منسلک ہے۔</translation>
<translation id="208586643495776849">براہ کرم دوبارہ کوشش کریں</translation>
<translation id="2090963878406559571">‏<ph name="ORIGIN" /> سے اپنے Android فون پر نمبر بھیجنے کے لیے، ترتیبات میں دونوں آلات کے لیے <ph name="TROUBLESHOOT_LINK" />۔</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">ترتیبات کا نظم کریں</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 گھنٹے میں}other{# گھنٹے میں}}</translation>
<translation id="2583543531130364912">اپنی ٹچ اسکرین کو کیلیبریٹ کریں</translation>
+<translation id="2586657967955657006">کلپ بورڈ</translation>
<translation id="2666092431469916601">سرفہرست</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> سے اشتراک کردہ</translation>
<translation id="271033894570825754">نیا</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />، <ph name="PRICE" /></translation>
<translation id="2803313416453193357">فولڈر کھولیں</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 سال پہلے}other{# سال پہلے}}</translation>
+<translation id="2878511608894704031">سبھی کو حذف کریں</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 دن میں}other{# دنوں میں}}</translation>
<translation id="2931838996092594335">کلک کریں</translation>
<translation id="2981684127883932071">تجاویز پیش کی جا رہی ہیں</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">بولنا شروع کریں</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> کو فولڈر<ph name="FOLDER_NAME" /> میں منتقل کر دیا گیا۔</translation>
<translation id="4316910396681052118">سبھی ایپس</translation>
+<translation id="4491109536499578614">تصویر</translation>
<translation id="4588090240171750605">دائیں سکرول کریں</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />، ستارہ کی درجہ بندی <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />، ایپ کی تجویز</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 دن اور }other{# دن اور }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 سال میں}other{# سال میں}}</translation>
+<translation id="588258955323874662">پوری اسکرین</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 سیکنڈ باقی}other{# سیکنڈ باقی}}</translation>
<translation id="5941711191222866238">چھوٹا کریں</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 گھنٹہ}other{# گھنٹے}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">‏Google اسسٹنٹ</translation>
<translation id="6430678249303439055">اس ایپ سے تمام اطلاعات کو مسدود کریں</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 سیکنڈ پہلے}other{# سیکنڈ پہلے}}</translation>
+<translation id="6503257047630241175">‏RTF مواد</translation>
<translation id="6539092367496845964">کچھ غلط ہو گیا۔ بعد میں دوبارہ کوشش کریں۔</translation>
<translation id="654149438358937226">تمام اطلاعات مسدود کریں</translation>
<translation id="6567071839949112727">اینسیسٹر پر کلک کریں</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_uz.xtb b/chromium/ui/strings/translations/ui_strings_uz.xtb
index 73eb3291e5a..cc28cba67e7 100644
--- a/chromium/ui/strings/translations/ui_strings_uz.xtb
+++ b/chromium/ui/strings/translations/ui_strings_uz.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">tanlash</translation>
<translation id="1859234291848436338">Yozuv yo‘nalishi</translation>
<translation id="1860796786778352021">Bildirishnomani yopish</translation>
+<translation id="186476001994626254">Internetdan smart joylangan kontent</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Hammasini belgilash</translation>
+<translation id="19085484004813472">Bu yangi funksiya</translation>
<translation id="2006524834898217237">Bu qurilma internetga ulanganini tekshiring.</translation>
<translation id="208586643495776849">Qayta urining</translation>
<translation id="2090963878406559571"><ph name="ORIGIN" /> orqali Android telefoningizga raqam yuborish uchun ikkala qurilma sozlamalarida <ph name="TROUBLESHOOT_LINK" /> sahifasini oching.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Sozlamalarni boshqarish</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 soatdan keyin}other{# soatdan keyin}}</translation>
<translation id="2583543531130364912">Sensorli ekranni kalibrlang</translation>
+<translation id="2586657967955657006">Klipbord</translation>
<translation id="2666092431469916601">Tepaga</translation>
<translation id="2701330563083355633"><ph name="DEVICE_NAME" /> orqali ulashilgan</translation>
<translation id="271033894570825754">Yangi</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Jildni ochish</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 yil oldin}other{# yil oldin}}</translation>
+<translation id="2878511608894704031">Hammasini tozalash</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 kundan keyin}other{# kundan keyin}}</translation>
<translation id="2931838996092594335">bosish</translation>
<translation id="2981684127883932071">Takliflar chiqarildi</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Gapirishni boshlang</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> ilovasi <ph name="FOLDER_NAME" /> jildiga olindi.</translation>
<translation id="4316910396681052118">BARCHA ILOVALAR</translation>
+<translation id="4491109536499578614">Tasvir</translation>
<translation id="4588090240171750605">O‘ngga aylantirish</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Bahosi: <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Ilova tavsiyasi</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 kun va }other{# kun va }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 yildan keyin}other{# yildan keyin}}</translation>
+<translation id="588258955323874662">Butun ekran</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 soniya qoldi}other{# soniya qoldi}}</translation>
<translation id="5941711191222866238">Yig‘ish</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 soat}other{# soat}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google Assistent</translation>
<translation id="6430678249303439055">Bu ilovadagi barcha bildirishnomalar bloklansin</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 soniya oldin}other{# soniya oldin}}</translation>
+<translation id="6503257047630241175">RTF kontent</translation>
<translation id="6539092367496845964">Xatolik yuz berdi. Keyinroq qaytadan urining.</translation>
<translation id="654149438358937226">Barcha bildirishnomalar bloklansin</translation>
<translation id="6567071839949112727">yuqori darajali obyekt ustiga bosish</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_vi.xtb b/chromium/ui/strings/translations/ui_strings_vi.xtb
index 7640c124cd8..29ca54739bc 100644
--- a/chromium/ui/strings/translations/ui_strings_vi.xtb
+++ b/chromium/ui/strings/translations/ui_strings_vi.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">chọn</translation>
<translation id="1859234291848436338">Hướng Ghi</translation>
<translation id="1860796786778352021">Đóng thông báo</translation>
+<translation id="186476001994626254">Nội dung dán thông minh trên web</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Chọn &amp;tất cả</translation>
+<translation id="19085484004813472">Đây là tính năng mới</translation>
<translation id="2006524834898217237">Hãy đảm bảo thiết bị này có kết nối Internet.</translation>
<translation id="208586643495776849">Vui lòng thử lại</translation>
<translation id="2090963878406559571">Để gửi một số điện thoại từ <ph name="ORIGIN" /> sang điện thoại Android, hãy <ph name="TROUBLESHOOT_LINK" /> cho cả hai thiết bị trong phần cài đặt.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Quản lý các tùy chọn cài đặt</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{trong 1 giờ}other{trong # giờ}}</translation>
<translation id="2583543531130364912">Hiệu chỉnh màn hình cảm ứng</translation>
+<translation id="2586657967955657006">Khay nhớ tạm</translation>
<translation id="2666092431469916601">Hàng đầu</translation>
<translation id="2701330563083355633">Chia sẻ từ <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Mới</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Mở thư mục</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 năm trước}other{# năm trước}}</translation>
+<translation id="2878511608894704031">Xóa tất cả</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{trong 1 ngày}other{trong # ngày}}</translation>
<translation id="2931838996092594335">nhấp vào</translation>
<translation id="2981684127883932071">Đang hiển thị mục đề xuất</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Bắt đầu nói</translation>
<translation id="430191667033048642">Đã chuyển <ph name="MOVED_APP_NAME" /> vào thư mục <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">TẤT CẢ ỨNG DỤNG</translation>
+<translation id="4491109536499578614">Hình ảnh</translation>
<translation id="4588090240171750605">Cuộn qua Phải</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Xếp hạng theo sao <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Ứng dụng đề xuất</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 ngày và }other{# ngày và }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{trong 1 năm}other{trong # năm}}</translation>
+<translation id="588258955323874662">Toàn màn hình</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{Còn 1 giây}other{Còn # giây}}</translation>
<translation id="5941711191222866238">Thu nhỏ</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 giờ}other{# giờ}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Trợ lý Google</translation>
<translation id="6430678249303439055">Chặn tất cả thông báo từ ứng dụng này</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 giây trước}other{# giây trước}}</translation>
+<translation id="6503257047630241175">Nội dung ở định dạng văn bản giàu tính chất (RTF)</translation>
<translation id="6539092367496845964">Đã xảy ra lỗi. Hãy thử lại sau.</translation>
<translation id="654149438358937226">Chặn tất cả thông báo</translation>
<translation id="6567071839949112727">nhấp vào đối tượng cấp trên</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">Lưu Tệp</translation>
<translation id="6656912866303152668">Đảm bảo bạn đã bật tính năng đồng bộ hóa của <ph name="TARGET_DEVICE_NAME" /> trong Chrome, rồi thử gửi lại.</translation>
<translation id="6699343763173986273">Bản nhạc tiếp theo của trình phát phương tiện</translation>
-<translation id="6710213216561001401">Trước đó</translation>
+<translation id="6710213216561001401">Trước</translation>
<translation id="6779314412797872738">Để chuyển một số điện thoại từ đây sang điện thoại Android, hãy <ph name="TROUBLESHOOT_LINK" /> cho cả hai thiết bị trong phần cài đặt.</translation>
<translation id="6786750046913594791">Đóng thư mục</translation>
<translation id="6808150112686056157">Dừng trình phát phương tiện</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb
index f364c410c37..4eea1f17465 100644
--- a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb
+++ b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">选中</translation>
<translation id="1859234291848436338">书写方向</translation>
<translation id="1860796786778352021">关闭通知</translation>
+<translation id="186476001994626254">Web Smart Paste 内容</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">全选(&amp;A)</translation>
+<translation id="19085484004813472">这是一项新功能</translation>
<translation id="2006524834898217237">请确保此设备已连接到互联网。</translation>
<translation id="208586643495776849">请重试</translation>
<translation id="2090963878406559571">要从 <ph name="ORIGIN" /> 向您的 Android 手机发送电话号码,请在这两部设备的设置中分别<ph name="TROUBLESHOOT_LINK" />。</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">管理设置</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 小时后}other{# 小时后}}</translation>
<translation id="2583543531130364912">校准您的触摸屏</translation>
+<translation id="2586657967955657006">剪贴板</translation>
<translation id="2666092431469916601">顶部</translation>
<translation id="2701330563083355633">共享自 <ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">新建</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />,<ph name="PRICE" /></translation>
<translation id="2803313416453193357">打开文件夹</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 年前}other{# 年前}}</translation>
+<translation id="2878511608894704031">全部删除</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 天后}other{# 天后}}</translation>
<translation id="2931838996092594335">点击</translation>
<translation id="2981684127883932071">显示了建议</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">开始讲话</translation>
<translation id="430191667033048642">“<ph name="MOVED_APP_NAME" />”已移至文件夹“<ph name="FOLDER_NAME" />”</translation>
<translation id="4316910396681052118">所有应用</translation>
+<translation id="4491109536499578614">图片</translation>
<translation id="4588090240171750605">向右滚动</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />,星级评分为 <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />,推荐的应用</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 天 }other{# 天 }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 年后}other{# 年后}}</translation>
+<translation id="588258955323874662">全屏</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{还剩 1 秒}other{还剩 # 秒}}</translation>
<translation id="5941711191222866238">最小化</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 小时}other{# 小时}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google 助理</translation>
<translation id="6430678249303439055">屏蔽来自此应用的所有通知</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 秒前}other{# 秒前}}</translation>
+<translation id="6503257047630241175">RTF 内容</translation>
<translation id="6539092367496845964">出了点问题。请稍后重试。</translation>
<translation id="654149438358937226">屏蔽所有通知</translation>
<translation id="6567071839949112727">点击祖先实体</translation>
@@ -178,7 +185,7 @@
<translation id="6620110761915583480">保存文件</translation>
<translation id="6656912866303152668">请确保 <ph name="TARGET_DEVICE_NAME" /> 已在 Chrome 中开启同步,然后再次尝试发送。</translation>
<translation id="6699343763173986273">媒体下一曲</translation>
-<translation id="6710213216561001401">上一个</translation>
+<translation id="6710213216561001401">上一项</translation>
<translation id="6779314412797872738">要在此处向您的 Android 手机发送电话号码,请在这两台设备的设置中<ph name="TROUBLESHOOT_LINK" />。</translation>
<translation id="6786750046913594791">关闭文件夹</translation>
<translation id="6808150112686056157">媒体停止</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_zh-HK.xtb b/chromium/ui/strings/translations/ui_strings_zh-HK.xtb
index d5089bdf051..537b6c66077 100644
--- a/chromium/ui/strings/translations/ui_strings_zh-HK.xtb
+++ b/chromium/ui/strings/translations/ui_strings_zh-HK.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">選取</translation>
<translation id="1859234291848436338">文字方向</translation>
<translation id="1860796786778352021">通知關閉</translation>
+<translation id="186476001994626254">網絡智能貼上內容</translation>
<translation id="1871244248791675517">插入鍵</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">全部選取(&amp;A)</translation>
+<translation id="19085484004813472">呢個係新功能</translation>
<translation id="2006524834898217237">請確保此裝置已連接互聯網。</translation>
<translation id="208586643495776849">請再試一次</translation>
<translation id="2090963878406559571">如要將 <ph name="ORIGIN" /> 的號碼從此裝置傳送至您的 Android 手機,請在這兩部裝置的設定中<ph name="TROUBLESHOOT_LINK" />。</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">管理設定</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 小時後}other{# 小時後}}</translation>
<translation id="2583543531130364912">校正您的觸控螢幕</translation>
+<translation id="2586657967955657006">剪貼簿</translation>
<translation id="2666092431469916601">返回頁首</translation>
<translation id="2701330563083355633">透過 <ph name="DEVICE_NAME" /> 分享</translation>
<translation id="271033894570825754">全新</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />,<ph name="PRICE" /></translation>
<translation id="2803313416453193357">開啟資料夾</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 年前}other{# 年前}}</translation>
+<translation id="2878511608894704031">全部刪除</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 天後}other{# 天後}}</translation>
<translation id="2931838996092594335">點擊</translation>
<translation id="2981684127883932071">顯示緊建議</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">開始說</translation>
<translation id="430191667033048642">「<ph name="MOVED_APP_NAME" />」已經移咗去「<ph name="FOLDER_NAME" />」資料夾。</translation>
<translation id="4316910396681052118">所有應用程式</translation>
+<translation id="4491109536499578614">圖片</translation>
<translation id="4588090240171750605">向右捲動</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />,<ph name="RATING_SCORE" /> 星</translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />,推薦應用程式</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 天和}other{# 天和}}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 年後}other{# 年後}}</translation>
+<translation id="588258955323874662">全螢幕</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{還有 1 秒}other{還有 # 秒}}</translation>
<translation id="5941711191222866238">最小化</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 小時}other{# 小時}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google 助理</translation>
<translation id="6430678249303439055">封鎖來自此應用程式的所有通知</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 秒前}other{# 秒前}}</translation>
+<translation id="6503257047630241175">RTF 內容</translation>
<translation id="6539092367496845964">發生錯誤,請稍後再試。</translation>
<translation id="654149438358937226">封鎖所有通知</translation>
<translation id="6567071839949112727">㩒一下上層元素</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb
index 70f4585fb1e..c2626022f47 100644
--- a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb
+++ b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">選取</translation>
<translation id="1859234291848436338">文字方向</translation>
<translation id="1860796786778352021">通知關閉</translation>
+<translation id="186476001994626254">Web Smart Paste 內容</translation>
<translation id="1871244248791675517">Ins</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">選取全部(&amp;A)</translation>
+<translation id="19085484004813472">這是新功能</translation>
<translation id="2006524834898217237">請確認此裝置已連上網際網路。</translation>
<translation id="208586643495776849">請再試一次</translation>
<translation id="2090963878406559571">如要將號碼從 <ph name="ORIGIN" /> 傳送到你的 Android 手機,請在這兩部裝置的設定中<ph name="TROUBLESHOOT_LINK" />。</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">管理設定</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{1 小時內}other{# 小時內}}</translation>
<translation id="2583543531130364912">校正觸控螢幕</translation>
+<translation id="2586657967955657006">剪貼簿</translation>
<translation id="2666092431469916601">置頂</translation>
<translation id="2701330563083355633">透過 <ph name="DEVICE_NAME" /> 分享</translation>
<translation id="271033894570825754">新</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />,<ph name="PRICE" /></translation>
<translation id="2803313416453193357">開啟資料夾</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 年前}other{# 年前}}</translation>
+<translation id="2878511608894704031">全部刪除</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{1 天內}other{# 天內}}</translation>
<translation id="2931838996092594335">點選</translation>
<translation id="2981684127883932071">目前正顯示建議項目</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Start Speaking</translation>
<translation id="430191667033048642">已將「<ph name="MOVED_APP_NAME" />」移至「<ph name="FOLDER_NAME" />」資料夾。</translation>
<translation id="4316910396681052118">所有應用程式</translation>
+<translation id="4491109536499578614">圖片</translation>
<translation id="4588090240171750605">向右捲動</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />,星級評等 <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />,推薦的應用程式</translation>
@@ -147,8 +152,9 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 天 }other{# 天 }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{1 年內}other{# 年內}}</translation>
+<translation id="588258955323874662">全螢幕</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{還剩 1 秒}other{還剩 # 秒}}</translation>
-<translation id="5941711191222866238">縮到最小</translation>
+<translation id="5941711191222866238">最小化</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 小時}other{# 小時}}</translation>
<translation id="6012623610530968780">第 <ph name="SELECTED_PAGE" /> 頁 (共 <ph name="TOTAL_PAGE_NUM" /> 頁)</translation>
<translation id="6022924867608035986">清除搜尋框文字</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Google 助理</translation>
<translation id="6430678249303439055">一律封鎖來自這個應用程式的通知</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{1 秒前}other{# 秒前}}</translation>
+<translation id="6503257047630241175">RTF 內容</translation>
<translation id="6539092367496845964">發生錯誤,請稍後再試。</translation>
<translation id="654149438358937226">封鎖所有通知</translation>
<translation id="6567071839949112727">點選上層項目</translation>
diff --git a/chromium/ui/strings/translations/ui_strings_zu.xtb b/chromium/ui/strings/translations/ui_strings_zu.xtb
index 06be84faade..9bb84c8ee72 100644
--- a/chromium/ui/strings/translations/ui_strings_zu.xtb
+++ b/chromium/ui/strings/translations/ui_strings_zu.xtb
@@ -37,9 +37,11 @@
<translation id="1842960171412779397">khetha</translation>
<translation id="1859234291848436338">Ibhala izikhobisi-ndlela</translation>
<translation id="1860796786778352021">Ukuvala isaziso</translation>
+<translation id="186476001994626254">Okuqukethwe kokunamathisela kwe-Web Smart</translation>
<translation id="1871244248791675517">Okumaphakathi</translation>
<translation id="1884435127456172652"><ph name="NUMBER" /> %</translation>
<translation id="1901303067676059328">Khetha &amp;konke</translation>
+<translation id="19085484004813472">Lesi yisici esisha</translation>
<translation id="2006524834898217237">Yenza isiqinisekiso sokuthi le divayisi ixhunywe ku-inthanethi.</translation>
<translation id="208586643495776849">Sicela uzame futhi</translation>
<translation id="2090963878406559571">Ukuze uthumele inombolo esuka ku-<ph name="ORIGIN" /> kuya kufoni yakho ye-Android, i-<ph name="TROUBLESHOOT_LINK" /> kuwo womabili amadivayisi kuzilungiselelo.</translation>
@@ -67,6 +69,7 @@
<translation id="2570734079541893434">Phatha amasethingi</translation>
<translation id="2573731672208488250">{HOURS,plural, =1{nge-1h}one{nge-#h}other{nge-#h}}</translation>
<translation id="2583543531130364912">Sika isikrini sakho esithintwayo</translation>
+<translation id="2586657967955657006">Ibhodi lokumanathisela</translation>
<translation id="2666092431469916601">Ngaphezulu</translation>
<translation id="2701330563083355633">Kwabelwe kusuka ku-<ph name="DEVICE_NAME" /></translation>
<translation id="271033894570825754">Okusha</translation>
@@ -74,6 +77,7 @@
<translation id="2749082172777216925"><ph name="APP_NAME_INFO" />, <ph name="PRICE" /></translation>
<translation id="2803313416453193357">Vula ifolda</translation>
<translation id="2824719307700604149">{YEARS,plural, =1{1 unyaka odlule}one{# iminyaka edlule}other{# iminyaka edlule}}</translation>
+<translation id="2878511608894704031">Susa Konke</translation>
<translation id="2907671656515444832">{DAYS,plural, =1{nge-1d}one{ku-#d}other{ku-#d}}</translation>
<translation id="2931838996092594335">chofoza</translation>
<translation id="2981684127883932071">Ukubonisa iziphakamiso</translation>
@@ -114,6 +118,7 @@
<translation id="4289300219472526559">Qala ukukhuluma</translation>
<translation id="430191667033048642"><ph name="MOVED_APP_NAME" /> iyiswe kufolda <ph name="FOLDER_NAME" />.</translation>
<translation id="4316910396681052118">ZONKE IZINHLELO ZOKUSEBENZA</translation>
+<translation id="4491109536499578614">Isithombe</translation>
<translation id="4588090240171750605">Skrolela ngakwesokudla</translation>
<translation id="4631891353005174729"><ph name="APP_NAME_TYPE" />, Isilinganiso senkanyezi <ph name="RATING_SCORE" /></translation>
<translation id="4648249871170053485"><ph name="APP_NAME" />, Izincomo zohlelo lokusebenza</translation>
@@ -147,6 +152,7 @@
<translation id="5768079895599174203">{DAYS,plural, =1{1 usuku ne- }one{# izinsuku ne- }other{# izinsuku ne- }}</translation>
<translation id="5789643057113097023">.</translation>
<translation id="5866104238061687188">{YEARS,plural, =1{nge-1y}one{nge-#y}other{nge-#y}}</translation>
+<translation id="588258955323874662">Isikrini esigcwele</translation>
<translation id="5906667377645263094">{SECONDS,plural, =1{1 isekhondi elisele}one{# amasekhondi asele}other{# amasekhondi asele}}</translation>
<translation id="5941711191222866238">Nciphisa</translation>
<translation id="5943826764092288734">{HOURS,plural, =1{1 ihora}one{# amahora}other{# amahora}}</translation>
@@ -170,6 +176,7 @@
<translation id="6417265370957905582">Umsizi we-Google</translation>
<translation id="6430678249303439055">Vimbela zonke izaziso kusuka kulolu hlelo lokusebenza</translation>
<translation id="6483402905448010557">{SECONDS,plural, =1{Isekhondi elingu-1 eledlule}one{# amasekhondi edlule}other{# amasekhondi edlule}}</translation>
+<translation id="6503257047630241175">Okuqukethwe kwe-RTF</translation>
<translation id="6539092367496845964">Okuthile akuhambanga kahle. Zama futhi emuva kwesikhathi.</translation>
<translation id="654149438358937226">Vimbela zonke izaziso</translation>
<translation id="6567071839949112727">chofoza idlozi</translation>
diff --git a/chromium/ui/strings/ui_strings.grd b/chromium/ui/strings/ui_strings.grd
index 40994a1de67..e4f2a50e09a 100644
--- a/chromium/ui/strings/ui_strings.grd
+++ b/chromium/ui/strings/ui_strings.grd
@@ -366,10 +366,28 @@ need to be translated for each locale.-->
<message name="IDS_APP_MENU_EMPTY_SUBMENU" desc="Used when a submenu has no entries">
(empty)
</message>
-
+ <message name="IDS_CLIPBOARD_MENU_IMAGE" desc="Used when an image is stored in the clipboard history">
+ Image
+ </message>
+ <message name="IDS_CLIPBOARD_MENU_RTF_CONTENT" desc="Used when Rich Text Format (RTF) content is stored in the clipboard history">
+ RTF Content
+ </message>
+ <message name="IDS_CLIPBOARD_MENU_WEB_SMART_PASTE" desc="Used when web smart paste content is stored in the clipboard history">
+ Web Smart Paste Content
+ </message>
+ <message name="IDS_CLIPBOARD_MENU_CLIPBOARD" desc="The name of the system clipboard">
+ Clipboard
+ </message>
+ <message name="IDS_CLIPBOARD_MENU_DELETE_ALL" desc="Used to indicate to a user that all clipboard items will be deleted if selected">
+ Delete All
+ </message>
<message name="IDS_MENU_ITEM_NEW_BADGE" desc="Appears as a badge on menu items denoting new features">
New
</message>
+ <message name="IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE"
+ desc="Message appended to screen reader description of menu items which have the 'New' badge">
+ This is a new feature
+ </message>
<!-- General application strings -->
<message name="IDS_SENTENCE_END" desc="The symbol that is used to end a sentence.">
@@ -694,6 +712,11 @@ need to be translated for each locale.-->
Win
</message>
</if>
+ <if expr="chromeos">
+ <message name="IDS_APP_FULLSCREEN_KEY" desc="This refers to the 'Fullscreen' media key on certain keyboards.">
+ Fullscreen
+ </message>
+ </if>
<!-- Accelerator format -->
<message name="IDS_APP_ACCELERATOR_WITH_MODIFIER" desc="Accelerator with a modifier key">
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_APP_FULLSCREEN_KEY.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_APP_FULLSCREEN_KEY.png.sha1
new file mode 100644
index 00000000000..36a4234e1d7
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_APP_FULLSCREEN_KEY.png.sha1
@@ -0,0 +1 @@
+36a5013c7a18ff69d3b4e8c759bdf83414358fe5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_CLIPBOARD.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_CLIPBOARD.png.sha1
new file mode 100644
index 00000000000..ab9c09a7d1f
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_CLIPBOARD.png.sha1
@@ -0,0 +1 @@
+8b0426cbb4003994a99b39f69e7b0335009756c5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_DELETE_ALL.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_DELETE_ALL.png.sha1
new file mode 100644
index 00000000000..ab9c09a7d1f
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_DELETE_ALL.png.sha1
@@ -0,0 +1 @@
+8b0426cbb4003994a99b39f69e7b0335009756c5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_IMAGE.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_IMAGE.png.sha1
new file mode 100644
index 00000000000..ab9c09a7d1f
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_IMAGE.png.sha1
@@ -0,0 +1 @@
+8b0426cbb4003994a99b39f69e7b0335009756c5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_RTF_CONTENT.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_RTF_CONTENT.png.sha1
new file mode 100644
index 00000000000..ab9c09a7d1f
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_RTF_CONTENT.png.sha1
@@ -0,0 +1 @@
+8b0426cbb4003994a99b39f69e7b0335009756c5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_WEB_SMART_PASTE.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_WEB_SMART_PASTE.png.sha1
new file mode 100644
index 00000000000..ab9c09a7d1f
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_CLIPBOARD_MENU_WEB_SMART_PASTE.png.sha1
@@ -0,0 +1 @@
+8b0426cbb4003994a99b39f69e7b0335009756c5 \ No newline at end of file
diff --git a/chromium/ui/strings/ui_strings_grd/IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE.png.sha1
new file mode 100644
index 00000000000..cfbd8f8950b
--- /dev/null
+++ b/chromium/ui/strings/ui_strings_grd/IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE.png.sha1
@@ -0,0 +1 @@
+c88f3adea4b633341c30b3220292ae3cfa4dd50f \ No newline at end of file
diff --git a/chromium/ui/surface/BUILD.gn b/chromium/ui/surface/BUILD.gn
index 3293e4d3310..e7e9468b21c 100644
--- a/chromium/ui/surface/BUILD.gn
+++ b/chromium/ui/surface/BUILD.gn
@@ -2,10 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
-jumbo_component("surface") {
+component("surface") {
sources = [
"surface_export.h",
"transport_dib.cc",
diff --git a/chromium/ui/touch_selection/BUILD.gn b/chromium/ui/touch_selection/BUILD.gn
index 11c2b71b3cb..a166409c386 100644
--- a/chromium/ui/touch_selection/BUILD.gn
+++ b/chromium/ui/touch_selection/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
@@ -10,7 +9,7 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
-jumbo_component("touch_selection") {
+component("touch_selection") {
output_name = "ui_touch_selection"
sources = [
diff --git a/chromium/ui/touch_selection/touch_handle.cc b/chromium/ui/touch_selection/touch_handle.cc
index afb1f8d0901..579f998ac97 100644
--- a/chromium/ui/touch_selection/touch_handle.cc
+++ b/chromium/ui/touch_selection/touch_handle.cc
@@ -17,21 +17,21 @@ namespace ui {
namespace {
// Maximum duration of a fade sequence.
-const double kFadeDurationMs = 200;
+constexpr auto kFadeDuration = base::TimeDelta::FromMilliseconds(200);
// Maximum amount of travel for a fade sequence. This avoids handle "ghosting"
// when the handle is moving rapidly while the fade is active.
-const double kFadeDistanceSquared = 20.f * 20.f;
+constexpr double kFadeDistanceSquared = 20.0f * 20.0f;
// Avoid using an empty touch rect, as it may fail the intersection test event
// if it lies within the other rect's bounds.
-const float kMinTouchMajorForHitTesting = 1.f;
+constexpr float kMinTouchMajorForHitTesting = 1.0f;
// The maximum touch size to use when computing whether a touch point is
// targetting a touch handle. This is necessary for devices that misreport
// touch radii, preventing inappropriately largely touch sizes from completely
// breaking handle dragging behavior.
-const float kMaxTouchMajorForHitTesting = 36.f;
+constexpr float kMaxTouchMajorForHitTesting = 36.0f;
// Note that the intersection region is boundary *exclusive*.
bool RectIntersectsCircle(const gfx::RectF& rect,
@@ -236,19 +236,17 @@ bool TouchHandle::Animate(base::TimeTicks frame_time) {
DCHECK(enabled_);
- float time_u =
- 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
- float position_u = (focus_bottom_ - fade_start_position_).LengthSquared() /
- kFadeDistanceSquared;
- float u = std::max(time_u, position_u);
- SetAlpha(is_visible_ ? u : 1.f - u);
+ const float time_u = 1.0f - (fade_end_time_ - frame_time) / kFadeDuration;
+ const float position_u =
+ (focus_bottom_ - fade_start_position_).LengthSquared() /
+ kFadeDistanceSquared;
+ const float u = std::max(time_u, position_u);
+ SetAlpha(is_visible_ ? u : 1.0f - u);
- if (u >= 1.f) {
+ if (u >= 1)
EndFade();
- return false;
- }
- return true;
+ return u < 1;
}
gfx::RectF TouchHandle::GetVisibleBounds() const {
@@ -419,9 +417,8 @@ void TouchHandle::BeginFade() {
return;
}
- fade_end_time_ = base::TimeTicks::Now() +
- base::TimeDelta::FromMillisecondsD(
- kFadeDurationMs * std::abs(target_alpha - alpha_));
+ fade_end_time_ =
+ base::TimeTicks::Now() + kFadeDuration * std::abs(target_alpha - alpha_);
fade_start_position_ = focus_bottom_;
client_->SetNeedsAnimate();
}
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn
index 3df474182de..7a19c1c40bb 100644
--- a/chromium/ui/views/BUILD.gn
+++ b/chromium/ui/views/BUILD.gn
@@ -4,7 +4,6 @@
import("//build/buildflag_header.gni")
import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//components/vector_icons/vector_icons.gni")
import("//testing/test.gni")
@@ -59,7 +58,7 @@ buildflag_header("buildflags") {
flags = [ "ENABLE_DESKTOP_AURA=$enable_desktop_aura" ]
}
-jumbo_component("views") {
+component("views") {
all_dependent_configs = [ ":flags" ]
public = [
@@ -101,6 +100,7 @@ jumbo_component("views") {
"border.h",
"bubble/bubble_border.h",
"bubble/bubble_dialog_delegate_view.h",
+ "bubble/bubble_dialog_model_host.h",
"bubble/bubble_frame_view.h",
"bubble/info_bubble.h",
"bubble/tooltip_icon.h",
@@ -110,7 +110,6 @@ jumbo_component("views") {
"controls/button/button.h",
"controls/button/button_controller.h",
"controls/button/button_controller_delegate.h",
- "controls/button/button_observer.h",
"controls/button/checkbox.h",
"controls/button/image_button.h",
"controls/button/image_button_factory.h",
@@ -125,7 +124,6 @@ jumbo_component("views") {
"controls/combobox/combobox_listener.h",
"controls/combobox/combobox_util.h",
"controls/editable_combobox/editable_combobox.h",
- "controls/editable_combobox/editable_combobox_listener.h",
"controls/focus_ring.h",
"controls/focusable_border.h",
"controls/highlight_path_generator.h",
@@ -315,6 +313,7 @@ jumbo_component("views") {
"border.cc",
"bubble/bubble_border.cc",
"bubble/bubble_dialog_delegate_view.cc",
+ "bubble/bubble_dialog_model_host.cc",
"bubble/bubble_frame_view.cc",
"bubble/footnote_container_view.cc",
"bubble/info_bubble.cc",
@@ -335,6 +334,8 @@ jumbo_component("views") {
"controls/button/toggle_button.cc",
"controls/combobox/combobox.cc",
"controls/combobox/combobox_util.cc",
+ "controls/combobox/empty_combobox_model.cc",
+ "controls/combobox/empty_combobox_model.h",
"controls/editable_combobox/editable_combobox.cc",
"controls/focus_ring.cc",
"controls/focusable_border.cc",
@@ -485,6 +486,7 @@ jumbo_component("views") {
"//third_party/icu",
"//ui/accessibility",
"//ui/base/clipboard",
+ "//ui/base/dragdrop/mojom:mojom_shared",
"//ui/display",
"//ui/latency",
"//ui/native_theme",
@@ -504,6 +506,7 @@ jumbo_component("views") {
"//ui/base",
"//ui/base/clipboard",
"//ui/base/cursor:cursor_base",
+ "//ui/base/dragdrop/mojom:mojom_headers",
"//ui/base/ime/init",
"//ui/compositor",
"//ui/display",
@@ -521,7 +524,7 @@ jumbo_component("views") {
deps += [ "//ui/display/util" ]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
public += [ "color_chooser/color_chooser_view.h" ]
sources += [ "color_chooser/color_chooser_view.cc" ]
}
@@ -598,7 +601,7 @@ jumbo_component("views") {
"//ui/accelerated_widget_mac",
"//ui/events:dom_keycode_converter",
]
- libs = [
+ frameworks = [
"AppKit.framework",
"CoreGraphics.framework",
"Foundation.framework",
@@ -725,7 +728,7 @@ jumbo_component("views") {
"//ui/aura",
"//ui/events",
"//ui/platform_window",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
"//ui/touch_selection",
"//ui/wm",
"//ui/wm/public",
@@ -761,16 +764,8 @@ jumbo_component("views") {
"//ui/base/x",
"//ui/platform_window/x11",
]
- public += [
- "widget/desktop_aura/desktop_drag_drop_client_aurax11.h",
- "widget/desktop_aura/desktop_screen_x11.h",
- "widget/desktop_aura/desktop_window_tree_host_x11.h",
- ]
- sources += [
- "widget/desktop_aura/desktop_drag_drop_client_aurax11.cc",
- "widget/desktop_aura/desktop_screen_x11.cc",
- "widget/desktop_aura/desktop_window_tree_host_x11.cc",
- ]
+ public += [ "widget/desktop_aura/desktop_screen_x11.h" ]
+ sources += [ "widget/desktop_aura/desktop_screen_x11.cc" ]
} else if (is_win) {
public += [ "widget/desktop_aura/desktop_window_tree_host_win.h" ]
sources += [
@@ -785,13 +780,9 @@ jumbo_component("views") {
}
if (use_ozone) {
public += [ "widget/desktop_aura/desktop_screen_ozone.h" ]
- sources += [
- "widget/desktop_aura/desktop_drag_drop_client_ozone.cc",
- "widget/desktop_aura/desktop_drag_drop_client_ozone.h",
- "widget/desktop_aura/desktop_screen_ozone.cc",
- ]
+ sources += [ "widget/desktop_aura/desktop_screen_ozone.cc" ]
}
- if (is_linux) {
+ if (is_linux || is_chromeos) {
public += [ "widget/desktop_aura/desktop_window_tree_host_linux.h" ]
sources += [
"style/platform_style_linux.cc",
@@ -800,21 +791,19 @@ jumbo_component("views") {
"widget/desktop_aura/window_event_filter_linux.cc",
"widget/desktop_aura/window_event_filter_linux.h",
]
- deps += [
- "//ui/base:hit_test",
- "//ui/platform_window/extensions",
- ]
+ deps += [ "//ui/base:hit_test" ]
}
- if (is_linux || is_fuchsia) {
+ if (is_linux || is_chromeos || is_fuchsia) {
public += [
"widget/desktop_aura/desktop_window_tree_host_platform.h",
"widget/desktop_aura/window_move_client_platform.h",
]
sources += [
+ "widget/desktop_aura/desktop_drag_drop_client_ozone.cc",
+ "widget/desktop_aura/desktop_drag_drop_client_ozone.h",
"widget/desktop_aura/desktop_window_tree_host_platform.cc",
"widget/desktop_aura/window_move_client_platform.cc",
]
- deps += [ "//ui/platform_window/extensions" ]
}
if (use_atk) {
sources += [
@@ -865,7 +854,7 @@ jumbo_component("views") {
}
}
-jumbo_source_set("test_support") {
+source_set("test_support") {
testonly = true
sources = [
"animation/test/flood_fill_ink_drop_ripple_test_api.cc",
@@ -914,6 +903,8 @@ jumbo_source_set("test_support") {
"test/scoped_views_test_helper.h",
"test/slider_test_api.cc",
"test/slider_test_api.h",
+ "test/test_ax_event_observer.cc",
+ "test/test_ax_event_observer.h",
"test/test_layout_manager.cc",
"test/test_layout_manager.h",
"test/test_layout_provider.cc",
@@ -926,6 +917,8 @@ jumbo_source_set("test_support") {
"test/test_widget_observer.h",
"test/view_metadata_test_utils.cc",
"test/view_metadata_test_utils.h",
+ "test/views_drawing_test_utils.cc",
+ "test/views_drawing_test_utils.h",
"test/views_test_base.cc",
"test/views_test_base.h",
"test/views_test_helper.cc",
@@ -1029,6 +1022,10 @@ jumbo_source_set("test_support") {
}
test("views_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [
"accessible_pane_view_unittest.cc",
"animation/bounds_animator_unittest.cc",
@@ -1037,6 +1034,7 @@ test("views_unittests") {
"animation/ink_drop_highlight_unittest.cc",
"animation/ink_drop_host_view_unittest.cc",
"animation/ink_drop_impl_unittest.cc",
+ "animation/ink_drop_mask_unittest.cc",
"animation/ink_drop_ripple_unittest.cc",
"animation/ink_drop_unittest.cc",
"animation/installable_ink_drop_animator_unittest.cc",
@@ -1048,6 +1046,9 @@ test("views_unittests") {
"bubble/bubble_border_unittest.cc",
"bubble/bubble_dialog_delegate_view_unittest.cc",
"bubble/bubble_frame_view_unittest.cc",
+ "bubble/info_bubble_unittest.cc",
+ "controls/base_control_test_widget.cc",
+ "controls/base_control_test_widget.h",
"controls/button/button_unittest.cc",
"controls/button/checkbox_unittest.cc",
"controls/button/image_button_factory_unittest.cc",
@@ -1062,10 +1063,12 @@ test("views_unittests") {
"controls/editable_combobox/editable_combobox_unittest.cc",
"controls/image_view_unittest.cc",
"controls/label_unittest.cc",
+ "controls/link_unittest.cc",
"controls/menu/menu_controller_unittest.cc",
"controls/menu/menu_item_view_unittest.cc",
"controls/menu/menu_model_adapter_unittest.cc",
"controls/menu/menu_runner_unittest.cc",
+ "controls/menu/menu_separator_unittest.cc",
"controls/menu/submenu_view_unittest.cc",
"controls/menu/test_menu_item_view.cc",
"controls/menu/test_menu_item_view.h",
@@ -1076,6 +1079,7 @@ test("views_unittests") {
"controls/progress_bar_unittest.cc",
"controls/resize_area_unittest.cc",
"controls/scroll_view_unittest.cc",
+ "controls/scrollbar/base_scroll_bar_button_unittest.cc",
"controls/scrollbar/scrollbar_unittest.cc",
"controls/separator_unittest.cc",
"controls/slider_unittest.cc",
@@ -1093,6 +1097,7 @@ test("views_unittests") {
"focus/focus_traversal_unittest.cc",
"layout/animating_layout_manager_unittest.cc",
"layout/box_layout_unittest.cc",
+ "layout/composite_layout_tests.cc",
"layout/fill_layout_unittest.cc",
"layout/flex_layout_unittest.cc",
"layout/grid_layout_unittest.cc",
@@ -1103,6 +1108,7 @@ test("views_unittests") {
"metadata/type_conversion_unittest.cc",
"paint_info_unittest.cc",
"rect_based_targeting_utils_unittest.cc",
+ "repeat_controller_unittest.cc",
"run_all_unittests_main.cc",
"selection_controller_unittest.cc",
"test/widget_test_unittest.cc",
@@ -1139,6 +1145,7 @@ test("views_unittests") {
"//components/viz/common",
"//mojo/core/embedder",
"//skia",
+ "//testing/gmock",
"//testing/gtest",
"//third_party/icu",
"//ui/accessibility",
@@ -1146,8 +1153,10 @@ test("views_unittests") {
"//ui/base:test_support",
"//ui/base/clipboard",
"//ui/base/clipboard:clipboard_test_support",
+ "//ui/base/dragdrop/mojom:mojom_shared",
"//ui/base/ime/init",
"//ui/compositor:test_support",
+ "//ui/display:test_support",
"//ui/events:dom_keycode_converter",
"//ui/events:events_base",
"//ui/events:test_support",
@@ -1157,7 +1166,7 @@ test("views_unittests") {
"//ui/gl:test_support",
"//ui/native_theme",
"//ui/native_theme:test_support",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
"//ui/resources",
"//ui/resources:ui_test_pak",
"//ui/strings",
@@ -1230,6 +1239,7 @@ test("views_unittests") {
if (use_aura) {
sources += [
+ "accessibility/accessibility_alert_window_unittest.cc",
"accessibility/ax_aura_obj_cache_unittest.cc",
"accessibility/ax_tree_source_views_unittest.cc",
"controls/native/native_view_host_aura_unittest.cc",
@@ -1276,17 +1286,16 @@ test("views_unittests") {
]
if (use_x11) {
sources += [
- "widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc",
"widget/desktop_aura/desktop_screen_x11_unittest.cc",
"widget/desktop_aura/x11_drag_drop_client_unittest.cc",
]
deps += [ "//ui/base/x:test_support" ]
}
- if (is_linux || is_fuchsia) {
+ if (is_linux || is_chromeos || is_fuchsia) {
sources += [
"widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc",
]
- if (is_linux) {
+ if (is_linux || is_chromeos) {
sources += [
"widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc",
]
@@ -1373,15 +1382,17 @@ source_set("views_interactive_ui_tests") {
deps += [
"//ui/events/platform",
"//ui/platform_window",
- "//ui/platform_window/platform_window_handler",
+ "//ui/platform_window/wm",
]
}
if (use_x11) {
sources += [
- "widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc",
"widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc",
]
+ }
+
+ if (use_x11 || ozone_platform_x11) {
deps += [
"//ui/base/x:test_support",
"//ui/events/platform/x11:x11",
diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS
index 69da070dbbb..6723a35cfe1 100644
--- a/chromium/ui/views/DEPS
+++ b/chromium/ui/views/DEPS
@@ -23,8 +23,6 @@ include_rules = [
"+ui/touch_selection",
"+ui/wm/core",
"+ui/wm/public",
-
- "-testing/gmock",
]
specific_include_rules = {
diff --git a/chromium/ui/views/OWNERS b/chromium/ui/views/OWNERS
index 0489f00961a..aedb3cdb56b 100644
--- a/chromium/ui/views/OWNERS
+++ b/chromium/ui/views/OWNERS
@@ -1,5 +1,10 @@
-# Prefer tapted@ for mac specific changes.
+# Note: Unless you want a specific reviewer's expertise, please send CLs to
+# chromium-ui-views-reviews@google.com rather than to specific individuals.
+# These CLs will be automatically reassigned to a reviewer within about
+# 5 minutes. This approach helps our team to load-balance incoming reviews.
+# Googlers can read more about this at go/gwsq-gerrit.
+ellyjones@chromium.org
kylixrd@chromium.org
msw@chromium.org
pbos@chromium.org
@@ -9,10 +14,14 @@ sky@chromium.org
tapted@chromium.org
weili@chromium.org
-# Prefer ellyjones@ for any changes to these files.
-per-file *_mac.*=ellyjones@chromium.org
-per-file *_cocoa.*=ellyjones@chromium.org
-per-file *.mm=ellyjones@chromium.org
+# Prefer avi@, ellyjones@, lgrey@, or tapted@ for mac specific changes.
+per-file *_mac.*=avi@chromium.org
+per-file *_cocoa.*=avi@chromium.org
+per-file *.mm=avi@chromium.org
+
+per-file *_mac.*=lgrey@chromium.org
+per-file *_cocoa.*=lgrey@chromium.org
+per-file *.mm=lgrey@chromium.org
# If you're doing structural changes get a review from one of the OWNERS.
per-file BUILD.gn=*
diff --git a/chromium/ui/views/PRESUBMIT.py b/chromium/ui/views/PRESUBMIT.py
index 93bfc642ec6..7273cb19322 100644
--- a/chromium/ui/views/PRESUBMIT.py
+++ b/chromium/ui/views/PRESUBMIT.py
@@ -16,8 +16,8 @@ INCLUDE_CPP_FILES_ONLY = (
def CheckChangeLintsClean(input_api, output_api):
"""Makes sure that the change is cpplint clean."""
sources = lambda x: input_api.FilterSourceFile(
- x, white_list=INCLUDE_CPP_FILES_ONLY,
- black_list=input_api.DEFAULT_BLACK_LIST)
+ x, files_to_check=INCLUDE_CPP_FILES_ONLY,
+ files_to_skip=input_api.DEFAULT_FILES_TO_SKIP)
return input_api.canned_checks.CheckChangeLintsClean(
input_api, output_api, sources, lint_filters=[], verbose_level=1)
diff --git a/chromium/ui/views/accessibility/accessibility_alert_window.cc b/chromium/ui/views/accessibility/accessibility_alert_window.cc
index 4c5b0e47f60..9afdb5263f9 100644
--- a/chromium/ui/views/accessibility/accessibility_alert_window.cc
+++ b/chromium/ui/views/accessibility/accessibility_alert_window.cc
@@ -29,7 +29,7 @@ AccessibilityAlertWindow::AccessibilityAlertWindow(aura::Window* parent,
AccessibilityAlertWindow::~AccessibilityAlertWindow() = default;
void AccessibilityAlertWindow::HandleAlert(const std::string& alert_string) {
- if (!alert_window_->parent())
+ if (!alert_window_ || !alert_window_->parent())
return;
alert_window_->SetTitle(base::UTF8ToUTF16(alert_string));
diff --git a/chromium/ui/views/accessibility/accessibility_alert_window.h b/chromium/ui/views/accessibility/accessibility_alert_window.h
index 999c55e9f78..5895a714ea8 100644
--- a/chromium/ui/views/accessibility/accessibility_alert_window.h
+++ b/chromium/ui/views/accessibility/accessibility_alert_window.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "base/gtest_prod_util.h"
#include "base/scoped_observer.h"
#include "ui/aura/env.h"
#include "ui/aura/env_observer.h"
@@ -35,6 +36,9 @@ class VIEWS_EXPORT AccessibilityAlertWindow : public aura::EnvObserver {
void HandleAlert(const std::string& alert_string);
private:
+ FRIEND_TEST_ALL_PREFIXES(AccessibilityAlertWindowTest, HandleAlert);
+ FRIEND_TEST_ALL_PREFIXES(AccessibilityAlertWindowTest, OnWillDestroyEnv);
+
// aura::EnvObserver:
void OnWillDestroyEnv() override;
diff --git a/chromium/ui/views/accessibility/accessibility_alert_window_unittest.cc b/chromium/ui/views/accessibility/accessibility_alert_window_unittest.cc
new file mode 100644
index 00000000000..60af94effee
--- /dev/null
+++ b/chromium/ui/views/accessibility/accessibility_alert_window_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/accessibility/accessibility_alert_window.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer_type.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace views {
+
+class FakeAXAuraObjCacheDelegate : public AXAuraObjCache::Delegate {
+ public:
+ FakeAXAuraObjCacheDelegate() = default;
+ FakeAXAuraObjCacheDelegate(const FakeAXAuraObjCacheDelegate&) = delete;
+ FakeAXAuraObjCacheDelegate& operator=(const FakeAXAuraObjCacheDelegate&) =
+ delete;
+ ~FakeAXAuraObjCacheDelegate() override = default;
+
+ void OnChildWindowRemoved(AXAuraObjWrapper* parent) override {}
+ void OnEvent(AXAuraObjWrapper* aura_obj,
+ ax::mojom::Event event_type) override {
+ if (event_type == ax::mojom::Event::kAlert)
+ count_++;
+ }
+
+ int count() { return count_; }
+ void set_count(int count) { count_ = count; }
+
+ private:
+ int count_ = 0;
+};
+
+class AccessibilityAlertWindowTest : public ViewsTestBase {
+ public:
+ AccessibilityAlertWindowTest() = default;
+ AccessibilityAlertWindowTest(const AccessibilityAlertWindowTest&) = delete;
+ AccessibilityAlertWindowTest& operator=(const AccessibilityAlertWindowTest&) =
+ delete;
+ ~AccessibilityAlertWindowTest() override = default;
+
+ protected:
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+
+ parent_ = std::make_unique<aura::Window>(nullptr);
+ parent_->Init(ui::LAYER_SOLID_COLOR);
+ }
+
+ std::unique_ptr<aura::Window> parent_;
+ AXAuraObjCache cache;
+};
+
+TEST_F(AccessibilityAlertWindowTest, HandleAlert) {
+ FakeAXAuraObjCacheDelegate delegate;
+ cache.SetDelegate(&delegate);
+
+ AccessibilityAlertWindow window(parent_.get(), &cache);
+
+ window.HandleAlert("test");
+ EXPECT_EQ(1, delegate.count());
+
+ delegate.set_count(0);
+ window.OnWillDestroyEnv();
+
+ window.HandleAlert("test");
+ EXPECT_EQ(0, delegate.count());
+}
+
+TEST_F(AccessibilityAlertWindowTest, OnWillDestroyEnv) {
+ AccessibilityAlertWindow window(parent_.get(), &cache);
+ window.OnWillDestroyEnv();
+
+ EXPECT_FALSE(window.observer_.IsObservingSources());
+ EXPECT_FALSE(window.alert_window_);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.h b/chromium/ui/views/accessibility/ax_tree_source_views.h
index 37317b530b5..a867cd971bc 100644
--- a/chromium/ui/views/accessibility/ax_tree_source_views.h
+++ b/chromium/ui/views/accessibility/ax_tree_source_views.h
@@ -61,6 +61,8 @@ class VIEWS_EXPORT AXTreeSourceViews
// Useful for debugging.
std::string ToString(views::AXAuraObjWrapper* root, std::string prefix);
+ const ui::AXTreeID tree_id_for_test() const { return tree_id_; }
+
private:
// The top-level object to use for the AX tree. See class comment.
AXAuraObjWrapper* const root_ = nullptr;
diff --git a/chromium/ui/views/accessibility/ax_virtual_view.cc b/chromium/ui/views/accessibility/ax_virtual_view.cc
index 060d4a77c65..cc9f8aae5fe 100644
--- a/chromium/ui/views/accessibility/ax_virtual_view.cc
+++ b/chromium/ui/views/accessibility/ax_virtual_view.cc
@@ -21,6 +21,7 @@
#include "ui/base/ui_base_types.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
@@ -284,7 +285,7 @@ gfx::NativeViewAccessible AXVirtualView::ChildAtIndex(int index) {
return nullptr;
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
gfx::NativeViewAccessible AXVirtualView::GetNSWindow() {
NOTREACHED();
return nullptr;
@@ -386,7 +387,10 @@ gfx::NativeViewAccessible AXVirtualView::GetFocus() {
}
ui::AXPlatformNode* AXVirtualView::GetFromNodeID(int32_t id) {
- // TODO(nektar): Implement.
+ AXVirtualView* virtual_view = GetFromId(id);
+ if (virtual_view) {
+ return virtual_view->ax_platform_node();
+ }
return nullptr;
}
@@ -423,6 +427,18 @@ gfx::AcceleratedWidget AXVirtualView::GetTargetForNativeAccessibilityEvent() {
return gfx::kNullAcceleratedWidget;
}
+base::Optional<bool> AXVirtualView::GetTableHasColumnOrRowHeaderNode() const {
+ return GetDelegate()->GetTableHasColumnOrRowHeaderNode();
+}
+
+std::vector<int32_t> AXVirtualView::GetColHeaderNodeIds() const {
+ return GetDelegate()->GetColHeaderNodeIds();
+}
+
+std::vector<int32_t> AXVirtualView::GetColHeaderNodeIds(int col_index) const {
+ return GetDelegate()->GetColHeaderNodeIds(col_index);
+}
+
bool AXVirtualView::IsIgnored() const {
return GetData().IsIgnored();
}
@@ -473,6 +489,12 @@ View* AXVirtualView::GetOwnerView() const {
return nullptr;
}
+ViewAXPlatformNodeDelegate* AXVirtualView::GetDelegate() const {
+ DCHECK(GetOwnerView());
+ return static_cast<ViewAXPlatformNodeDelegate*>(
+ &GetOwnerView()->GetViewAccessibility());
+}
+
AXVirtualViewWrapper* AXVirtualView::GetOrCreateWrapper(
views::AXAuraObjCache* cache) {
#if defined(USE_AURA)
diff --git a/chromium/ui/views/accessibility/ax_virtual_view.h b/chromium/ui/views/accessibility/ax_virtual_view.h
index 6b1f9eeda4a..299d106a01b 100644
--- a/chromium/ui/views/accessibility/ax_virtual_view.h
+++ b/chromium/ui/views/accessibility/ax_virtual_view.h
@@ -40,6 +40,7 @@ namespace views {
class AXAuraObjCache;
class View;
class ViewAccessibility;
+class ViewAXPlatformNodeDelegate;
// Implements a virtual view that is used only for accessibility.
//
@@ -99,6 +100,8 @@ class VIEWS_EXPORT AXVirtualView : public ui::AXPlatformNodeDelegateBase {
}
AXVirtualView* virtual_parent_view() { return virtual_parent_view_; }
+ ui::AXPlatformNode* ax_platform_node() { return ax_platform_node_; }
+
// Returns true if |view| is contained within the hierarchy of this
// AXVirtualView, even as an indirect descendant. Will return true if |view|
// is also this AXVirtualView.
@@ -153,10 +156,16 @@ class VIEWS_EXPORT AXVirtualView : public ui::AXPlatformNodeDelegateBase {
bool IsOffscreen() const override;
const ui::AXUniqueId& GetUniqueId() const override;
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
+ base::Optional<bool> GetTableHasColumnOrRowHeaderNode() const override;
+ std::vector<int32_t> GetColHeaderNodeIds() const override;
+ std::vector<int32_t> GetColHeaderNodeIds(int col_index) const override;
// Gets the real View that owns our shallowest virtual ancestor,, if any.
View* GetOwnerView() const;
+ // Gets the view platform delegate if exists, otherwise nullptr.
+ ViewAXPlatformNodeDelegate* GetDelegate() const;
+
// Gets or creates a wrapper suitable for use with tree sources.
AXVirtualViewWrapper* GetOrCreateWrapper(views::AXAuraObjCache* cache);
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc
index 35dce74a77b..00b72e939ed 100644
--- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -213,7 +213,7 @@ void ViewAXPlatformNodeDelegate::NotifyAccessibilityEvent(
ax_platform_node_->NotifyAccessibilityEvent(event_type);
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void ViewAXPlatformNodeDelegate::AnnounceText(const base::string16& text) {
ax_platform_node_->AnnounceText(text);
}
@@ -345,6 +345,10 @@ gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::ChildAtIndex(int index) {
return nullptr;
}
+bool ViewAXPlatformNodeDelegate::HasModalDialog() const {
+ return GetChildWidgets().is_tab_modal_showing;
+}
+
gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetNSWindow() {
NOTREACHED();
return nullptr;
@@ -377,6 +381,17 @@ bool ViewAXPlatformNodeDelegate::IsLeaf() const {
return ViewAccessibility::IsLeaf() || AXPlatformNodeDelegateBase::IsLeaf();
}
+bool ViewAXPlatformNodeDelegate::IsToplevelBrowserWindow() {
+ // Note: only used on Desktop Linux. Other platforms don't have an application
+ // node so this would never return true.
+ ui::AXNodeData data = GetData();
+ if (data.role != ax::mojom::Role::kWindow)
+ return false;
+
+ AXPlatformNodeDelegate* parent = GetParentDelegate();
+ return parent && parent->GetData().role == ax::mojom::Role::kApplication;
+}
+
gfx::Rect ViewAXPlatformNodeDelegate::GetBoundsRect(
const ui::AXCoordinateSystem coordinate_system,
const ui::AXClippingBehavior clipping_behavior,
@@ -525,6 +540,46 @@ const ui::AXUniqueId& ViewAXPlatformNodeDelegate::GetUniqueId() const {
return ViewAccessibility::GetUniqueId();
}
+base::Optional<bool>
+ViewAXPlatformNodeDelegate::GetTableHasColumnOrRowHeaderNode() const {
+ if (!GetAncestorTableView())
+ return false;
+ return !GetAncestorTableView()->visible_columns().empty();
+}
+
+std::vector<int32_t> ViewAXPlatformNodeDelegate::GetColHeaderNodeIds() const {
+ std::vector<int32_t> col_header_ids;
+ if (!virtual_children().empty()) {
+ for (const std::unique_ptr<AXVirtualView>& header_cell :
+ virtual_children().front()->children()) {
+ const ui::AXNodeData& header_data = header_cell->GetData();
+ if (header_data.role == ax::mojom::Role::kColumnHeader) {
+ col_header_ids.push_back(header_data.id);
+ }
+ }
+ }
+ return col_header_ids;
+}
+
+std::vector<int32_t> ViewAXPlatformNodeDelegate::GetColHeaderNodeIds(
+ int col_index) const {
+ std::vector<int32_t> columns = GetColHeaderNodeIds();
+ if (columns.size() <= size_t{col_index}) {
+ return {};
+ }
+ return {columns[col_index]};
+}
+
+TableView* ViewAXPlatformNodeDelegate::GetAncestorTableView() const {
+ ui::AXNodeData data;
+ view()->GetViewAccessibility().GetAccessibleNodeData(&data);
+
+ if (!ui::IsTableLike(data.role))
+ return nullptr;
+
+ return static_cast<TableView*>(view());
+}
+
bool ViewAXPlatformNodeDelegate::IsOrderedSetItem() const {
const ui::AXNodeData& data = GetData();
return (view()->GetGroup() >= 0) ||
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h
index 8d3ddca5571..923e54ac9bb 100644
--- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h
@@ -17,6 +17,7 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/controls/table/table_view.h"
#include "ui/views/widget/widget_observer.h"
namespace ui {
@@ -45,7 +46,7 @@ class ViewAXPlatformNodeDelegate : public ViewAccessibility,
// ViewAccessibility:
gfx::NativeViewAccessible GetNativeObject() const override;
void NotifyAccessibilityEvent(ax::mojom::Event event_type) override;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void AnnounceText(const base::string16& text) override;
#endif
void FireFocusAfterMenuClose() override;
@@ -58,11 +59,13 @@ class ViewAXPlatformNodeDelegate : public ViewAccessibility,
const ui::AXNodeData& GetData() const override;
int GetChildCount() const override;
gfx::NativeViewAccessible ChildAtIndex(int index) override;
+ bool HasModalDialog() const override;
gfx::NativeViewAccessible GetNSWindow() override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
gfx::NativeViewAccessible GetParent() override;
bool IsChildOfLeaf() const override;
bool IsLeaf() const override;
+ bool IsToplevelBrowserWindow() override;
gfx::Rect GetBoundsRect(
const ui::AXCoordinateSystem coordinate_system,
const ui::AXClippingBehavior clipping_behavior,
@@ -81,6 +84,9 @@ class ViewAXPlatformNodeDelegate : public ViewAccessibility,
bool IsMinimized() const override;
// Also in |ViewAccessibility|.
const ui::AXUniqueId& GetUniqueId() const override;
+ base::Optional<bool> GetTableHasColumnOrRowHeaderNode() const override;
+ std::vector<int32_t> GetColHeaderNodeIds() const override;
+ std::vector<int32_t> GetColHeaderNodeIds(int col_index) const override;
// Ordered-set-like and item-like nodes.
bool IsOrderedSetItem() const override;
@@ -106,6 +112,9 @@ class ViewAXPlatformNodeDelegate : public ViewAccessibility,
ChildWidgetsResult GetChildWidgets() const;
+ // Gets the real TableView, otherwise nullptr.
+ TableView* GetAncestorTableView() const;
+
// We own this, but it is reference-counted on some platforms so we can't use
// a unique_ptr. It is destroyed in the destructor.
ui::AXPlatformNode* ax_platform_node_;
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc
index 3f8100365a7..b7bf51dc1a8 100644
--- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux_unittest.cc
@@ -36,6 +36,10 @@ TEST_F(ViewAXPlatformNodeDelegateAuraLinuxTest, TextfieldAccessibility) {
textfield->SetAccessibleName(base::UTF8ToUTF16("Name"));
content->AddChildView(textfield);
+ ASSERT_EQ(0, atk_object_get_n_accessible_children(
+ textfield->GetNativeViewAccessible()))
+ << "Text fields should be leaf nodes on this platform, otherwise no "
+ "descendants will be recognized by assistive software.";
AtkText* atk_text = ATK_TEXT(textfield->GetNativeViewAccessible());
ASSERT_NE(nullptr, atk_text);
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index cf4bc178a1d..51fd747d4bc 100644
--- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -40,6 +40,28 @@ class TestButton : public Button {
~TestButton() override = default;
};
+class TestAXEventObserver : public AXEventObserver {
+ public:
+ explicit TestAXEventObserver(AXAuraObjCache* cache) : cache_(cache) {
+ AXEventManager::Get()->AddObserver(this);
+ }
+ TestAXEventObserver(const TestAXEventObserver&) = delete;
+ TestAXEventObserver& operator=(const TestAXEventObserver&) = delete;
+ ~TestAXEventObserver() override {
+ AXEventManager::Get()->RemoveObserver(this);
+ }
+
+ // AXEventObserver:
+ void OnViewEvent(View* view, ax::mojom::Event event_type) override {
+ std::vector<AXAuraObjWrapper*> out_children;
+ AXAuraObjWrapper* ax_obj = cache_->GetOrCreate(view->GetWidget());
+ ax_obj->GetChildren(&out_children);
+ }
+
+ private:
+ AXAuraObjCache* cache_;
+};
+
} // namespace
class ViewAXPlatformNodeDelegateTest : public ViewsTestBase {
@@ -412,28 +434,6 @@ class DerivedTestView : public View {
void OnBlur() override { SetVisible(false); }
};
-class TestAXEventObserver : public AXEventObserver {
- public:
- explicit TestAXEventObserver(AXAuraObjCache* cache) : cache_(cache) {
- AXEventManager::Get()->AddObserver(this);
- }
- TestAXEventObserver(const TestAXEventObserver&) = delete;
- TestAXEventObserver& operator=(const TestAXEventObserver&) = delete;
- ~TestAXEventObserver() override {
- AXEventManager::Get()->RemoveObserver(this);
- }
-
- // AXEventObserver:
- void OnViewEvent(View* view, ax::mojom::Event event_type) override {
- std::vector<AXAuraObjWrapper*> out_children;
- AXAuraObjWrapper* ax_obj = cache_->GetOrCreate(view->GetWidget());
- ax_obj->GetChildren(&out_children);
- }
-
- private:
- AXAuraObjCache* cache_;
-};
-
using ViewAccessibilityTest = ViewsTestBase;
// Check if the destruction of the widget ends successfully if |view|'s
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc
index 00ffe077cd7..460b47fe4b2 100644
--- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc
@@ -89,6 +89,11 @@ TEST_F(ViewAXPlatformNodeDelegateWinTest, TextfieldAccessibility) {
content_accessible->get_accChild(child_index, &textfield_dispatch));
ASSERT_EQ(S_OK, textfield_dispatch.As(&textfield_accessible));
+ ASSERT_EQ(S_OK, textfield_accessible->get_accChildCount(&child_count));
+ EXPECT_EQ(0, child_count)
+ << "Text fields should be leaf nodes on this platform, otherwise no "
+ "descendants will be recognized by assistive software.";
+
ScopedBstr name;
ScopedVariant childid_self(CHILDID_SELF);
ASSERT_EQ(S_OK,
diff --git a/chromium/ui/views/accessibility/views_ax_tree_manager.h b/chromium/ui/views/accessibility/views_ax_tree_manager.h
index 5212d3d2a44..324af2d35b5 100644
--- a/chromium/ui/views/accessibility/views_ax_tree_manager.h
+++ b/chromium/ui/views/accessibility/views_ax_tree_manager.h
@@ -87,7 +87,7 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
ui::AXNode* GetRootAsAXNode() const override;
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
- // AXActionHandler implementation.
+ // AXActionHandlerBase implementation.
void PerformAction(const ui::AXActionData& data) override;
// AXEventObserver implementation.
diff --git a/chromium/ui/views/accessible_pane_view_unittest.cc b/chromium/ui/views/accessible_pane_view_unittest.cc
index 08e7c98394a..7ca93164512 100644
--- a/chromium/ui/views/accessible_pane_view_unittest.cc
+++ b/chromium/ui/views/accessible_pane_view_unittest.cc
@@ -142,7 +142,7 @@ TEST_F(AccessiblePaneViewTest, SetPaneFocusAndRestore) {
// predictable. On Mac, Deactivate() is not implemented. Note that
// TestBarView calls set_allow_deactivate_on_esc(true), which is only
// otherwise used in Ash.
-#if !defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#if !defined(OS_APPLE) || defined(OS_CHROMEOS)
// Esc should deactivate the widget.
test_view_bar->AcceleratorPressed(test_view_bar->escape_key());
EXPECT_TRUE(widget_main->IsActive());
diff --git a/chromium/ui/views/animation/OWNERS b/chromium/ui/views/animation/OWNERS
index a6dc7101dd4..5bbbafffbe6 100644
--- a/chromium/ui/views/animation/OWNERS
+++ b/chromium/ui/views/animation/OWNERS
@@ -1,2 +1 @@
-per-file *ink*=mohsen@chromium.org
per-file *ink*=pbos@chromium.org
diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc b/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
index f61dae86e7d..b32909215cb 100644
--- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
+++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc
@@ -4,6 +4,8 @@
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
+#include <cmath>
+
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
@@ -137,10 +139,8 @@ TEST(FloodFillInkDropRippleTest, TransformIsPixelAligned) {
dsf_transform.Scale(dsf, dsf);
dsf_transform.TransformPoint(&ripple_origin);
- EXPECT_NEAR(ripple_origin.x(), gfx::ToRoundedInt(ripple_origin.x()),
- kEpsilon);
- EXPECT_NEAR(ripple_origin.y(), gfx::ToRoundedInt(ripple_origin.y()),
- kEpsilon);
+ EXPECT_NEAR(ripple_origin.x(), std::round(ripple_origin.x()), kEpsilon);
+ EXPECT_NEAR(ripple_origin.y(), std::round(ripple_origin.y()), kEpsilon);
}
}
diff --git a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc
index 76808324026..f11aa8ca496 100644
--- a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc
+++ b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc
@@ -4,6 +4,7 @@
#include "ui/views/animation/ink_drop_highlight.h"
+#include <cmath>
#include <memory>
#include <utility>
@@ -226,9 +227,9 @@ TEST_F(InkDropHighlightTest, TransformIsPixelAligned) {
dsf_transform.Scale(dsf, dsf);
dsf_transform.TransformPoint(&transformed_layer_origin);
EXPECT_NEAR(transformed_layer_origin.x(),
- gfx::ToRoundedInt(transformed_layer_origin.x()), kEpsilon);
+ std::round(transformed_layer_origin.x()), kEpsilon);
EXPECT_NEAR(transformed_layer_origin.y(),
- gfx::ToRoundedInt(transformed_layer_origin.y()), kEpsilon);
+ std::round(transformed_layer_origin.y()), kEpsilon);
}
}
diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc
index 48e61528b74..87f91d14c20 100644
--- a/chromium/ui/views/animation/ink_drop_host_view.cc
+++ b/chromium/ui/views/animation/ink_drop_host_view.cc
@@ -4,6 +4,8 @@
#include "ui/views/animation/ink_drop_host_view.h"
+#include <utility>
+
#include "ui/events/event.h"
#include "ui/events/scoped_target_handler.h"
#include "ui/gfx/color_palette.h"
@@ -130,6 +132,24 @@ InkDrop* InkDropHostView::GetInkDrop() {
return ink_drop_.get();
}
+bool InkDropHostView::GetHighlighted() const {
+ return ink_drop_ && ink_drop_->IsHighlightFadingInOrVisible();
+}
+
+PropertyChangedSubscription InkDropHostView::AddHighlightedChangedCallback(
+ PropertyChangedCallback callback) {
+ // Since the highlight state is not directly represented by a member, use the
+ // applicable member (|ink_drop_|) as the property key. Note that this won't
+ // suffice if a future InkDrop-related property is added.
+ return AddPropertyChangedCallback(&ink_drop_, std::move(callback));
+}
+
+void InkDropHostView::OnInkDropHighlightedChanged() {
+ // See comments in AddHighlightedChangedCallback() re: using |ink_drop_| as
+ // the key.
+ OnPropertyChanged(&ink_drop_, kPropertyEffectsNone);
+}
+
std::unique_ptr<InkDropImpl> InkDropHostView::CreateDefaultInkDropImpl() {
auto ink_drop = std::make_unique<InkDropImpl>(this, size());
ink_drop->SetAutoHighlightMode(
@@ -215,6 +235,15 @@ gfx::Size InkDropHostView::CalculateLargeInkDropSize(
return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale);
}
+void InkDropHostView::OnLayerTransformed(const gfx::Transform& old_transform,
+ ui::PropertyChangeReason reason) {
+ View::OnLayerTransformed(old_transform, reason);
+
+ // Notify the ink drop that we have transformed so it can adapt accordingly.
+ if (HasInkDrop())
+ GetInkDrop()->HostTransformChanged(GetTransform());
+}
+
const InkDropEventHandler* InkDropHostView::GetEventHandler() const {
if (ink_drop_event_handler_override_)
return ink_drop_event_handler_override_;
@@ -228,6 +257,7 @@ InkDropEventHandler* InkDropHostView::GetEventHandler() {
BEGIN_METADATA(InkDropHostView)
METADATA_PARENT_CLASS(View)
+ADD_READONLY_PROPERTY_METADATA(InkDropHostView, bool, Highlighted)
END_METADATA()
} // namespace views
diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h
index b711c5f08f8..e6062daf0f1 100644
--- a/chromium/ui/views/animation/ink_drop_host_view.h
+++ b/chromium/ui/views/animation/ink_drop_host_view.h
@@ -138,6 +138,17 @@ class VIEWS_EXPORT InkDropHostView : public View {
// this isn't necessary anymore.
virtual InkDrop* GetInkDrop();
+ // Returns whether the ink drop should be considered "highlighted" (in or
+ // animating into "highlight visible" steady state).
+ bool GetHighlighted() const;
+
+ PropertyChangedSubscription AddHighlightedChangedCallback(
+ PropertyChangedCallback callback);
+
+ // Should be called by InkDrop implementations when their highlight state
+ // changes, to trigger the corresponding property change notification here.
+ void OnInkDropHighlightedChanged();
+
protected:
// Size used for the default SquareInkDropRipple.
static constexpr gfx::Size kDefaultInkDropSize = gfx::Size(24, 24);
@@ -187,6 +198,10 @@ class VIEWS_EXPORT InkDropHostView : public View {
// with the SquareInkDropRipple animation durations.
static gfx::Size CalculateLargeInkDropSize(const gfx::Size& small_size);
+ // View:
+ void OnLayerTransformed(const gfx::Transform& old_transform,
+ ui::PropertyChangeReason reason) override;
+
private:
friend class test::InkDropHostViewTestApi;
diff --git a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc
index 303547f1bed..f0c503a0fda 100644
--- a/chromium/ui/views/animation/ink_drop_host_view_unittest.cc
+++ b/chromium/ui/views/animation/ink_drop_host_view_unittest.cc
@@ -4,10 +4,13 @@
#include "ui/views/animation/ink_drop_host_view.h"
+#include <memory>
+
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/layer.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
@@ -18,8 +21,11 @@
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
+#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/test/ink_drop_host_view_test_api.h"
+#include "ui/views/animation/test/ink_drop_impl_test_api.h"
#include "ui/views/animation/test/test_ink_drop.h"
+#include "ui/views/controls/highlight_path_generator.h"
namespace views {
namespace test {
@@ -276,5 +282,96 @@ TEST_F(InkDropHostViewTest, DismissInkDropOnTouchOrGestureEvents) {
}
#endif
+// Verifies that calling OnInkDropHighlightedChanged() triggers a property
+// changed notification for the highlighted property.
+TEST_F(InkDropHostViewTest, HighlightedChangedFired) {
+ bool callback_called = false;
+ auto subscription =
+ host_view_.AddHighlightedChangedCallback(base::BindRepeating(
+ [](bool* called) { *called = true; }, &callback_called));
+ host_view_.OnInkDropHighlightedChanged();
+ EXPECT_TRUE(callback_called);
+}
+
+// A very basic InkDropHostView that only changes the GetInkDropBaseColor to
+// avoid hitting a NOTREACHED.
+class BasicTestInkDropHostView : public InkDropHostView {
+ public:
+ BasicTestInkDropHostView() = default;
+ BasicTestInkDropHostView(const BasicTestInkDropHostView&) = delete;
+ BasicTestInkDropHostView& operator=(const BasicTestInkDropHostView&) = delete;
+ ~BasicTestInkDropHostView() override = default;
+
+ protected:
+ // InkDropHostView:
+ SkColor GetInkDropBaseColor() const override {
+ return gfx::kPlaceholderColor;
+ }
+};
+
+// Tests the existence of layer clipping or layer masking when certain path
+// generators are applied on an InkDropHostView.
+class InkDropHostViewClippingTest : public testing::Test {
+ public:
+ InkDropHostViewClippingTest() : host_view_test_api_(&host_view_) {
+ // Set up an InkDropHostView. Clipping is based on the size of the view, so
+ // make sure the size is non empty.
+ host_view_test_api_.SetInkDropMode(InkDropMode::ON);
+ host_view_.SetSize(gfx::Size(20, 20));
+
+ // The root layer of the ink drop is created the first time GetInkDrop is
+ // called and then kept alive until the host view is destroyed.
+ ink_drop_ = static_cast<InkDropImpl*>(host_view_.GetInkDrop());
+ ink_drop_test_api_ = std::make_unique<test::InkDropImplTestApi>(ink_drop_);
+ }
+ InkDropHostViewClippingTest(const InkDropHostViewClippingTest&) = delete;
+ InkDropHostViewClippingTest& operator=(const InkDropHostViewClippingTest&) =
+ delete;
+ ~InkDropHostViewClippingTest() override = default;
+
+ ui::Layer* GetRootLayer() { return ink_drop_test_api_->GetRootLayer(); }
+
+ protected:
+ // Test target.
+ BasicTestInkDropHostView host_view_;
+
+ // Provides internal access to |host_view_| test target.
+ InkDropHostViewTestApi host_view_test_api_;
+
+ InkDropImpl* ink_drop_ = nullptr;
+
+ // Provides internal access to |host_view_|'s ink drop.
+ std::unique_ptr<test::InkDropImplTestApi> ink_drop_test_api_;
+};
+
+// Tests that by default (no highlight path generator applied), the root layer
+// will be masked.
+TEST_F(InkDropHostViewClippingTest, DefaultInkDropMasksRootLayer) {
+ ink_drop_->SetHovered(true);
+ EXPECT_TRUE(GetRootLayer()->layer_mask_layer());
+ EXPECT_TRUE(GetRootLayer()->clip_rect().IsEmpty());
+}
+
+// Tests that when adding a non empty highlight path generator, the root layer
+// is clipped instead of masked.
+TEST_F(InkDropHostViewClippingTest,
+ HighlightPathGeneratorClipsRootLayerWithoutMask) {
+ views::InstallRectHighlightPathGenerator(&host_view_);
+ ink_drop_->SetHovered(true);
+ EXPECT_FALSE(GetRootLayer()->layer_mask_layer());
+ EXPECT_FALSE(GetRootLayer()->clip_rect().IsEmpty());
+}
+
+// An empty highlight path generator is used for views who do not want their
+// highlight or ripple constrained by their size. Test that the views' ink
+// drop root layers have neither a clip or mask.
+TEST_F(InkDropHostViewClippingTest,
+ EmptyHighlightPathGeneratorUsesNeitherMaskNorClipsRootLayer) {
+ views::InstallEmptyHighlightPathGenerator(&host_view_);
+ ink_drop_->SetHovered(true);
+ EXPECT_FALSE(GetRootLayer()->layer_mask_layer());
+ EXPECT_TRUE(GetRootLayer()->clip_rect().IsEmpty());
+}
+
} // namespace test
} // namespace views
diff --git a/chromium/ui/views/animation/ink_drop_impl.cc b/chromium/ui/views/animation/ink_drop_impl.cc
index 631a69e84be..635e1dda4de 100644
--- a/chromium/ui/views/animation/ink_drop_impl.cc
+++ b/chromium/ui/views/animation/ink_drop_impl.cc
@@ -720,6 +720,11 @@ void InkDropImpl::CreateInkDropRipple() {
void InkDropImpl::DestroyInkDropRipple() {
if (!ink_drop_ripple_)
return;
+
+ // Ensures no observer callback happens from removing from |root_layer_|
+ // or destroying |ink_drop_ripple_|. Speculative fix for crashes in
+ // https://crbug.com/1088432 and https://crbug.com/1099844.
+ ink_drop_ripple_->set_observer(nullptr);
root_layer_->Remove(ink_drop_ripple_->GetRootLayer());
ink_drop_ripple_.reset();
RemoveRootLayerFromHostIfNeeded();
@@ -746,8 +751,12 @@ void InkDropImpl::CreateInkDropHighlight() {
void InkDropImpl::DestroyInkDropHighlight() {
if (!highlight_)
return;
- root_layer_->Remove(highlight_->layer());
+
+ // Ensures no observer callback happens from removing from |root_layer_|
+ // or destroying |highlight_|. Speculative fix for crashes in
+ // https://crbug.com/1088432 and https://crbug.com/1099844.
highlight_->set_observer(nullptr);
+ root_layer_->Remove(highlight_->layer());
highlight_.reset();
RemoveRootLayerFromHostIfNeeded();
}
@@ -772,6 +781,9 @@ void InkDropImpl::RemoveRootLayerFromHostIfNeeded() {
// views::InkDropRippleObserver:
void InkDropImpl::AnimationStarted(InkDropState ink_drop_state) {
+ // AnimationStarted should only be called from |ink_drop_ripple_|.
+ DCHECK(ink_drop_ripple_);
+
highlight_state_->AnimationStarted(ink_drop_state);
NotifyInkDropAnimationStarted();
}
@@ -822,6 +834,8 @@ void InkDropImpl::SetHighlight(bool should_highlight,
} else {
highlight_->FadeOut(animation_duration);
}
+
+ ink_drop_host_->OnInkDropHighlightedChanged();
}
bool InkDropImpl::ShouldHighlight() const {
diff --git a/chromium/ui/views/animation/ink_drop_impl_unittest.cc b/chromium/ui/views/animation/ink_drop_impl_unittest.cc
index 6fa064a0770..421da867179 100644
--- a/chromium/ui/views/animation/ink_drop_impl_unittest.cc
+++ b/chromium/ui/views/animation/ink_drop_impl_unittest.cc
@@ -28,13 +28,20 @@ class InkDropImplTest : public testing::Test {
~InkDropImplTest() override;
protected:
- TestInkDropHost* ink_drop_host() { return ink_drop_host_.get(); }
+ TestInkDropHost* ink_drop_host() { return &ink_drop_host_; }
+ const TestInkDropHost* ink_drop_host() const { return &ink_drop_host_; }
- InkDropImpl* ink_drop() { return ink_drop_.get(); }
+ InkDropImpl* ink_drop() {
+ return static_cast<InkDropImpl*>(ink_drop_host()->GetInkDrop());
+ }
- InkDropRipple* ink_drop_ripple() { return ink_drop_->ink_drop_ripple_.get(); }
+ InkDropRipple* ink_drop_ripple() {
+ return ink_drop()->ink_drop_ripple_.get();
+ }
- InkDropHighlight* ink_drop_highlight() { return ink_drop_->highlight_.get(); }
+ InkDropHighlight* ink_drop_highlight() {
+ return ink_drop()->highlight_.get();
+ }
test::InkDropImplTestApi* test_api() { return test_api_.get(); }
@@ -50,37 +57,30 @@ class InkDropImplTest : public testing::Test {
void DestroyInkDrop();
// Used to control the tasks scheduled by the InkDropImpl's Timer.
- scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_ =
+ base::MakeRefCounted<base::TestSimpleTaskRunner>();
// Required by base::Timer's.
- std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
+ std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_ =
+ std::make_unique<base::ThreadTaskRunnerHandle>(task_runner_);
private:
- std::unique_ptr<TestInkDropHost> ink_drop_host_;
-
- // The test target.
- std::unique_ptr<InkDropImpl> ink_drop_;
+ TestInkDropHost ink_drop_host_;
// Allows privileged access to the the |ink_drop_highlight_|.
std::unique_ptr<test::InkDropImplTestApi> test_api_;
std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>>
- animation_mode_reset_;
+ animation_mode_reset_ = gfx::AnimationTestApi::SetRichAnimationRenderMode(
+ gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED);
DISALLOW_COPY_AND_ASSIGN(InkDropImplTest);
};
-InkDropImplTest::InkDropImplTest()
- : task_runner_(new base::TestSimpleTaskRunner),
- thread_task_runner_handle_(
- new base::ThreadTaskRunnerHandle(task_runner_)),
- ink_drop_host_(std::make_unique<TestInkDropHost>()),
- ink_drop_(
- std::make_unique<InkDropImpl>(ink_drop_host_.get(), gfx::Size())),
- test_api_(std::make_unique<test::InkDropImplTestApi>(ink_drop_.get())),
- animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
- gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) {
- ink_drop_host_->set_disable_timers_for_test(true);
+InkDropImplTest::InkDropImplTest() {
+ ink_drop_host()->SetInkDropMode(InkDropHostView::InkDropMode::ON);
+ test_api_ = std::make_unique<test::InkDropImplTestApi>(ink_drop());
+ ink_drop_host()->set_disable_timers_for_test(true);
}
InkDropImplTest::~InkDropImplTest() = default;
@@ -91,12 +91,12 @@ void InkDropImplTest::RunPendingTasks() {
}
bool InkDropImplTest::AreLayersAddedToHost() const {
- return ink_drop_host_->num_ink_drop_layers() >= 1;
+ return ink_drop_host()->num_ink_drop_layers() >= 1;
}
void InkDropImplTest::DestroyInkDrop() {
test_api_.reset();
- ink_drop_.reset();
+ ink_drop_host()->SetInkDropMode(InkDropHostView::InkDropMode::OFF);
}
// AutoHighlightMode parameterized test fixture.
@@ -264,7 +264,7 @@ TEST_F(InkDropImplTest,
test_api()->SetHighlightState(nullptr);
}
-// Verifies there is no use after free errors.
+// Verifies there are no use after free errors.
TEST_F(InkDropImplTest,
TearingDownHighlightStateThatAccessesTheStateFactoryIsSafe) {
test::InkDropImplTestApi::AccessFactoryOnExitHighlightState::Install(
@@ -273,8 +273,8 @@ TEST_F(InkDropImplTest,
test_api()->state_factory());
}
-// Tests that if during destruction, a rippl animation is successfully ended, no
-// crash happens (see https://crbug.com/663579).
+// Tests that if during destruction, a ripple animation is successfully ended,
+// no crash happens (see https://crbug.com/663579).
TEST_F(InkDropImplTest, SuccessfulAnimationEndedDuringDestruction) {
// Start a ripple animation with non-zero duration.
ink_drop()->AnimateToState(InkDropState::ACTION_PENDING);
@@ -302,10 +302,6 @@ TEST_F(InkDropImplTest, RippleAndHighlightRecreatedOnSizeChange) {
const gfx::Rect bounds(5, 6, 7, 8);
ink_drop_host()->SetBoundsRect(bounds);
- // SetBoundsRect() calls HostSizeChanged(), but only when
- // InkDropHostView::ink_drop_ is set, but it's not in testing. So call this
- // function manually.
- ink_drop()->HostSizeChanged(ink_drop_host()->size());
EXPECT_EQ(2, ink_drop_host()->num_ink_drop_ripples_created());
EXPECT_EQ(2, ink_drop_host()->num_ink_drop_highlights_created());
EXPECT_EQ(ink_drop_host()->last_ink_drop_ripple(), ink_drop_ripple());
@@ -314,6 +310,25 @@ TEST_F(InkDropImplTest, RippleAndHighlightRecreatedOnSizeChange) {
EXPECT_EQ(bounds.size(), ink_drop_highlight()->layer()->size());
}
+// Verifies that the host's GetHighlighted() method reflects the ink drop's
+// highlight state, and when the state changes the ink drop notifies the host.
+TEST_F(InkDropImplTest, HostTracksHighlightState) {
+ bool callback_called = false;
+ auto subscription =
+ ink_drop_host()->AddHighlightedChangedCallback(base::BindRepeating(
+ [](bool* called) { *called = true; }, &callback_called));
+ EXPECT_FALSE(ink_drop_host()->GetHighlighted());
+
+ test_api()->SetShouldHighlight(true);
+ EXPECT_TRUE(callback_called);
+ EXPECT_TRUE(ink_drop_host()->GetHighlighted());
+ callback_called = false;
+
+ test_api()->SetShouldHighlight(false);
+ EXPECT_TRUE(callback_called);
+ EXPECT_FALSE(ink_drop_host()->GetHighlighted());
+}
+
////////////////////////////////////////////////////////////////////////////////
//
// Common AutoHighlightMode tests
diff --git a/chromium/ui/views/animation/ink_drop_mask.cc b/chromium/ui/views/animation/ink_drop_mask.cc
index 5376fd058f9..197a62a1a60 100644
--- a/chromium/ui/views/animation/ink_drop_mask.cc
+++ b/chromium/ui/views/animation/ink_drop_mask.cc
@@ -10,8 +10,6 @@
namespace views {
-// InkDropMask
-
InkDropMask::InkDropMask(const gfx::Size& layer_size)
: layer_(ui::LAYER_TEXTURED) {
layer_.set_delegate(this);
@@ -27,52 +25,6 @@ InkDropMask::~InkDropMask() {
void InkDropMask::OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {}
-// RoundRectInkDropMask
-
-RoundRectInkDropMask::RoundRectInkDropMask(const gfx::Size& layer_size,
- const gfx::InsetsF& mask_insets,
- float corner_radius)
- : InkDropMask(layer_size),
- mask_insets_(mask_insets),
- corner_radius_(corner_radius) {}
-
-void RoundRectInkDropMask::OnPaintLayer(const ui::PaintContext& context) {
- cc::PaintFlags flags;
- flags.setAlpha(255);
- flags.setStyle(cc::PaintFlags::kFill_Style);
- flags.setAntiAlias(true);
-
- ui::PaintRecorder recorder(context, layer()->size());
- const float dsf = recorder.canvas()->UndoDeviceScaleFactor();
-
- gfx::RectF masking_bound(layer()->bounds());
- masking_bound.Inset(mask_insets_);
-
- recorder.canvas()->DrawRoundRect(gfx::ScaleRect(masking_bound, dsf),
- corner_radius_ * dsf, flags);
-}
-
-// CircleInkDropMask
-
-CircleInkDropMask::CircleInkDropMask(const gfx::Size& layer_size,
- const gfx::Point& mask_center,
- int mask_radius)
- : InkDropMask(layer_size),
- mask_center_(mask_center),
- mask_radius_(mask_radius) {}
-
-void CircleInkDropMask::OnPaintLayer(const ui::PaintContext& context) {
- cc::PaintFlags flags;
- flags.setAlpha(255);
- flags.setStyle(cc::PaintFlags::kFill_Style);
- flags.setAntiAlias(true);
-
- ui::PaintRecorder recorder(context, layer()->size());
- recorder.canvas()->DrawCircle(mask_center_, mask_radius_, flags);
-}
-
-// PathInkDropMask
-
PathInkDropMask::PathInkDropMask(const gfx::Size& layer_size,
const SkPath& path)
: InkDropMask(layer_size), path_(path) {}
diff --git a/chromium/ui/views/animation/ink_drop_mask.h b/chromium/ui/views/animation/ink_drop_mask.h
index 5254f656c65..4b9b50fc7d6 100644
--- a/chromium/ui/views/animation/ink_drop_mask.h
+++ b/chromium/ui/views/animation/ink_drop_mask.h
@@ -5,12 +5,10 @@
#ifndef UI_VIEWS_ANIMATION_INK_DROP_MASK_H_
#define UI_VIEWS_ANIMATION_INK_DROP_MASK_H_
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_delegate.h"
-#include "ui/gfx/geometry/insets_f.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/rect.h"
#include "ui/views/views_export.h"
class SkPath;
@@ -40,46 +38,14 @@ class VIEWS_EXPORT InkDropMask : public ui::LayerDelegate {
DISALLOW_COPY_AND_ASSIGN(InkDropMask);
};
-// A rectangular ink drop mask with rounded corners.
-class VIEWS_EXPORT RoundRectInkDropMask : public InkDropMask {
- public:
- RoundRectInkDropMask(const gfx::Size& layer_size,
- const gfx::InsetsF& mask_insets,
- float corner_radius);
-
- private:
- // InkDropMask:
- void OnPaintLayer(const ui::PaintContext& context) override;
-
- gfx::InsetsF mask_insets_;
- float corner_radius_;
-
- DISALLOW_COPY_AND_ASSIGN(RoundRectInkDropMask);
-};
-
-// A circular ink drop mask.
-class VIEWS_EXPORT CircleInkDropMask : public InkDropMask {
- public:
- CircleInkDropMask(const gfx::Size& layer_size,
- const gfx::Point& mask_center,
- int mask_radius);
-
- private:
- // InkDropMask:
- void OnPaintLayer(const ui::PaintContext& context) override;
-
- gfx::Point mask_center_;
- int mask_radius_;
-
- DISALLOW_COPY_AND_ASSIGN(CircleInkDropMask);
-};
-
// An ink-drop mask that paints a specified path.
class VIEWS_EXPORT PathInkDropMask : public InkDropMask {
public:
PathInkDropMask(const gfx::Size& layer_size, const SkPath& path);
private:
+ FRIEND_TEST_ALL_PREFIXES(InkDropMaskTest, PathInkDropMaskPaintsTriangle);
+
// InkDropMask:
void OnPaintLayer(const ui::PaintContext& context) override;
diff --git a/chromium/ui/views/animation/ink_drop_mask_unittest.cc b/chromium/ui/views/animation/ink_drop_mask_unittest.cc
new file mode 100644
index 00000000000..99f69eb872e
--- /dev/null
+++ b/chromium/ui/views/animation/ink_drop_mask_unittest.cc
@@ -0,0 +1,51 @@
+// 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 "ui/views/animation/ink_drop_mask.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/paint_record.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "ui/compositor/paint_context.h"
+
+namespace views {
+
+TEST(InkDropMaskTest, PathInkDropMaskPaintsTriangle) {
+ gfx::Size layer_size(10, 10);
+ SkPath path;
+ SkPoint p1 = SkPoint::Make(3, 3);
+ SkPoint p2 = SkPoint::Make(5, 6);
+ SkPoint p3 = SkPoint::Make(8, 1);
+ path.moveTo(p1.x(), p1.y());
+ path.lineTo(p2.x(), p2.y());
+ path.lineTo(p3.x(), p3.y());
+ path.close();
+ PathInkDropMask mask(layer_size, path);
+
+ auto list = base::MakeRefCounted<cc::DisplayItemList>();
+ mask.OnPaintLayer(
+ ui::PaintContext(list.get(), 1.f, gfx::Rect(layer_size), false));
+ EXPECT_EQ(1u, list->num_paint_ops()) << list->ToString();
+
+ sk_sp<cc::PaintRecord> record = list->ReleaseAsRecord();
+ const auto* draw_op = record->GetOpAtForTesting<cc::DrawPathOp>(0);
+ ASSERT_NE(nullptr, draw_op);
+ ASSERT_EQ(3, draw_op->path.countPoints());
+
+ SkPoint points[3];
+ ASSERT_EQ(3, draw_op->path.getPoints(points, 3));
+ std::sort(points, points + 3,
+ [](const SkPoint& a, const SkPoint& b) { return a.x() < b.x(); });
+ EXPECT_EQ(p1, points[0]);
+ EXPECT_EQ(p2, points[1]);
+ EXPECT_EQ(p3, points[2]);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/animation/ink_drop_ripple_unittest.cc b/chromium/ui/views/animation/ink_drop_ripple_unittest.cc
index 5ddd67b7f1e..aa76b08c422 100644
--- a/chromium/ui/views/animation/ink_drop_ripple_unittest.cc
+++ b/chromium/ui/views/animation/ink_drop_ripple_unittest.cc
@@ -360,17 +360,14 @@ TEST_P(InkDropRippleTest, AnimateToVisibleFromHidden) {
// the most recent value passed to AnimateToState() when notifying observers
// that an animation has started within the AnimateToState() function call.
TEST_P(InkDropRippleTest, TargetInkDropStateOnAnimationStarted) {
- // TODO(bruthig): Re-enable! For some reason these tests fail on some win
- // trunk builds. See crbug.com/731811.
- if (!gfx::Animation::ShouldRenderRichAnimation())
- return;
-
ink_drop_ripple_->AnimateToState(views::InkDropState::ACTION_PENDING);
EXPECT_TRUE(observer_.AnimationHasStarted());
EXPECT_EQ(views::InkDropState::ACTION_PENDING,
observer_.target_state_at_last_animation_started());
- EXPECT_FALSE(observer_.AnimationHasEnded());
+ // Animation would end if rich_animation_rendering_mode is disabled.
+ if (gfx::Animation::ShouldRenderRichAnimation())
+ EXPECT_FALSE(observer_.AnimationHasEnded());
ink_drop_ripple_->AnimateToState(views::InkDropState::HIDDEN);
@@ -383,14 +380,11 @@ TEST_P(InkDropRippleTest, TargetInkDropStateOnAnimationStarted) {
// the most recent value passed to AnimateToState() when notifying observers
// that an animation has ended within the AnimateToState() function call.
TEST_P(InkDropRippleTest, TargetInkDropStateOnAnimationEnded) {
- // TODO(bruthig): Re-enable! For some reason these tests fail on some win
- // trunk builds. See crbug.com/731811.
- if (!gfx::Animation::ShouldRenderRichAnimation())
- return;
-
ink_drop_ripple_->AnimateToState(views::InkDropState::ACTION_PENDING);
- EXPECT_FALSE(observer_.AnimationHasEnded());
+ // Animation would end if rich_animation_rendering_mode is disabled.
+ if (gfx::Animation::ShouldRenderRichAnimation())
+ EXPECT_FALSE(observer_.AnimationHasEnded());
ink_drop_ripple_->AnimateToState(views::InkDropState::HIDDEN);
diff --git a/chromium/ui/views/animation/ink_drop_util.cc b/chromium/ui/views/animation/ink_drop_util.cc
index 72ce6649793..51bc6a8b2a6 100644
--- a/chromium/ui/views/animation/ink_drop_util.cc
+++ b/chromium/ui/views/animation/ink_drop_util.cc
@@ -4,11 +4,10 @@
#include "ui/views/animation/ink_drop_util.h"
-#include <math.h>
+#include <cmath>
#include "base/check_op.h"
#include "ui/gfx/geometry/point3_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/transform.h"
#include "ui/native_theme/native_theme.h"
@@ -30,8 +29,8 @@ gfx::Transform GetTransformSubpixelCorrection(const gfx::Transform& transform,
// Compute the rounded offset in screen space and finally unscale it back to
// DIP space.
gfx::Vector2dF aligned_offset_in_dip = origin.AsPointF().OffsetFromOrigin();
- aligned_offset_in_dip.set_x(gfx::ToRoundedInt(aligned_offset_in_dip.x()));
- aligned_offset_in_dip.set_y(gfx::ToRoundedInt(aligned_offset_in_dip.y()));
+ aligned_offset_in_dip.set_x(std::round(aligned_offset_in_dip.x()));
+ aligned_offset_in_dip.set_y(std::round(aligned_offset_in_dip.y()));
aligned_offset_in_dip.Scale(1.f / device_scale_factor);
// Compute the subpixel offset correction and apply it to the transform.
@@ -47,9 +46,9 @@ gfx::Transform GetTransformSubpixelCorrection(const gfx::Transform& transform,
offset.Scale(device_scale_factor);
if (!std::isnan(offset.x()))
- DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.x()) - offset.x()), kEpsilon);
+ DCHECK_LT(std::abs(std::round(offset.x()) - offset.x()), kEpsilon);
if (!std::isnan(offset.y()))
- DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.y()) - offset.y()), kEpsilon);
+ DCHECK_LT(std::abs(std::round(offset.y()) - offset.y()), kEpsilon);
#endif
return subpixel_correction;
}
diff --git a/chromium/ui/views/animation/installable_ink_drop.cc b/chromium/ui/views/animation/installable_ink_drop.cc
index ee67a05be7f..2701886375c 100644
--- a/chromium/ui/views/animation/installable_ink_drop.cc
+++ b/chromium/ui/views/animation/installable_ink_drop.cc
@@ -5,6 +5,7 @@
#include "ui/views/animation/installable_ink_drop.h"
#include <algorithm>
+#include <utility>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
@@ -86,6 +87,13 @@ InstallableInkDrop::InstallableInkDrop(InkDropHostView* ink_drop_host_view)
// To get all events, we must override InkDropHostView's event handler.
ink_drop_host_view->set_ink_drop_event_handler_override(&event_handler_);
ink_drop_host_view_ = ink_drop_host_view;
+
+ // TODO(crbug.com/931964): When this is removed, classes relying on property
+ // changed notifications from InkDropHostView for the highlighted state will
+ // need to register here instead.
+ RegisterHighlightedChangedCallback(
+ base::BindRepeating(&InkDropHostView::OnInkDropHighlightedChanged,
+ base::Unretained(ink_drop_host_view_)));
}
InstallableInkDrop::~InstallableInkDrop() {
@@ -101,6 +109,12 @@ void InstallableInkDrop::SetConfig(InstallableInkDropConfig config) {
SchedulePaint();
}
+std::unique_ptr<base::RepeatingClosureList::Subscription>
+InstallableInkDrop::RegisterHighlightedChangedCallback(
+ base::RepeatingClosure callback) {
+ return highlighted_changed_list_.Add(std::move(callback));
+}
+
void InstallableInkDrop::HostSizeChanged(const gfx::Size& new_size) {
layer_->SetBounds(gfx::Rect(new_size) + layer_->bounds().OffsetFromOrigin());
layer_->SchedulePaint(gfx::Rect(layer_->size()));
@@ -143,11 +157,15 @@ void InstallableInkDrop::SnapToHidden() {
}
void InstallableInkDrop::SetHovered(bool is_hovered) {
+ if (is_hovered_ == is_hovered)
+ return;
is_hovered_ = is_hovered;
UpdateAnimatorHighlight();
}
void InstallableInkDrop::SetFocused(bool is_focused) {
+ if (is_focused_ == is_focused)
+ return;
is_focused_ = is_focused;
UpdateAnimatorHighlight();
}
@@ -201,6 +219,7 @@ void InstallableInkDrop::SchedulePaint() {
void InstallableInkDrop::UpdateAnimatorHighlight() {
animator_.AnimateHighlight(is_hovered_ || is_focused_);
+ highlighted_changed_list_.Notify();
}
} // namespace views
diff --git a/chromium/ui/views/animation/installable_ink_drop.h b/chromium/ui/views/animation/installable_ink_drop.h
index 1fa261a776d..c46a3929cc2 100644
--- a/chromium/ui/views/animation/installable_ink_drop.h
+++ b/chromium/ui/views/animation/installable_ink_drop.h
@@ -62,6 +62,10 @@ class VIEWS_EXPORT InstallableInkDrop : public InkDrop,
void SetConfig(InstallableInkDropConfig config);
InstallableInkDropConfig config() const { return config_; }
+ // Registers |callback| to be called whenever the highlighted state changes.
+ std::unique_ptr<base::RepeatingClosureList::Subscription>
+ RegisterHighlightedChangedCallback(base::RepeatingClosure callback);
+
// Should only be used for inspecting properties of the layer in tests.
const ui::Layer* layer_for_testing() const { return layer_.get(); }
@@ -130,6 +134,8 @@ class VIEWS_EXPORT InstallableInkDrop : public InkDrop,
// Manages our animations and maniuplates |visual_state_| for us.
InstallableInkDropAnimator animator_;
+ base::RepeatingClosureList highlighted_changed_list_;
+
bool is_hovered_ = false;
bool is_focused_ = false;
};
diff --git a/chromium/ui/views/animation/square_ink_drop_ripple_unittest.cc b/chromium/ui/views/animation/square_ink_drop_ripple_unittest.cc
index fe7f89dc94f..14ec7cac08d 100644
--- a/chromium/ui/views/animation/square_ink_drop_ripple_unittest.cc
+++ b/chromium/ui/views/animation/square_ink_drop_ripple_unittest.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -267,10 +268,10 @@ TEST_F(SquareInkDropRippleCalculateTransformsTest, RippleIsPixelAligned) {
float float_max_x = rect.right();
float float_max_y = rect.bottom();
- int min_x = gfx::ToRoundedInt(float_min_x);
- int min_y = gfx::ToRoundedInt(float_min_y);
- int max_x = gfx::ToRoundedInt(float_max_x);
- int max_y = gfx::ToRoundedInt(float_max_y);
+ int min_x = base::ClampRound(float_min_x);
+ int min_y = base::ClampRound(float_min_y);
+ int max_x = base::ClampRound(float_max_x);
+ int max_y = base::ClampRound(float_max_y);
EXPECT_LT(std::abs(min_x - float_min_x), 0.01f);
EXPECT_LT(std::abs(min_y - float_min_y), 0.01f);
diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h
index 0ff9ae66b4b..2aca6e2353a 100644
--- a/chromium/ui/views/bubble/bubble_border.h
+++ b/chromium/ui/views/bubble/bubble_border.h
@@ -69,7 +69,7 @@ class VIEWS_EXPORT BubbleBorder : public Border {
NO_ASSETS,
SHADOW_COUNT,
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, the native window server should provide its own shadow for
// windows that could overlap the browser window.
DIALOG_SHADOW = NO_ASSETS,
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc
index e082f15fcbc..17e11883777 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -15,8 +15,11 @@
#include "ui/accessibility/ax_role_properties.h"
#include "ui/base/default_style.h"
#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/layer_animation_element.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/layout/layout_manager.h"
@@ -30,8 +33,11 @@
#include "ui/base/win/shell.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/views/widget/widget_utils_mac.h"
+#else
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
#endif
namespace views {
@@ -122,7 +128,7 @@ Widget* CreateBubbleWidget(BubbleDialogDelegate* bubble) {
: Widget::InitParams::ACTIVATABLE_NO;
bubble->OnBeforeBubbleWidgetInit(&bubble_params, bubble_widget);
bubble_widget->Init(std::move(bubble_params));
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// On Mac, having a parent window creates a permanent stacking order, so
// there's no need to do this. Also, calling StackAbove() on Mac shows the
// bubble implicitly, for which the bubble is currently not ready.
@@ -174,16 +180,29 @@ class BubbleDialogDelegate::AnchorViewObserver : public ViewObserver {
// This class is responsible for observing events on a BubbleDialogDelegate's
// anchor widget and notifying the BubbleDialogDelegate of them.
+#if defined(OS_APPLE)
class BubbleDialogDelegate::AnchorWidgetObserver : public WidgetObserver {
+#else
+class BubbleDialogDelegate::AnchorWidgetObserver : public WidgetObserver,
+ public aura::WindowObserver {
+#endif
+
public:
AnchorWidgetObserver(BubbleDialogDelegate* owner, Widget* widget)
: owner_(owner) {
- observer_.Add(widget);
+ widget_observer_.Add(widget);
+#if !defined(OS_APPLE)
+ window_observer_.Add(widget->GetNativeWindow());
+#endif
}
~AnchorWidgetObserver() override = default;
+ // WidgetObserver:
void OnWidgetDestroying(Widget* widget) override {
- observer_.Remove(widget);
+#if !defined(OS_APPLE)
+ window_observer_.Remove(widget->GetNativeWindow());
+#endif
+ widget_observer_.Remove(widget);
owner_->OnAnchorWidgetDestroying();
// |this| may be destroyed here!
}
@@ -196,9 +215,28 @@ class BubbleDialogDelegate::AnchorWidgetObserver : public WidgetObserver {
owner_->OnAnchorBoundsChanged();
}
+#if !defined(OS_APPLE)
+ // aura::WindowObserver:
+ void OnWindowTransformed(aura::Window* window,
+ ui::PropertyChangeReason reason) override {
+ if (window->is_destroying())
+ return;
+
+ // Update the anchor bounds when the transform animation is complete, or
+ // when the transform is set without animation.
+ if (!window->layer()->GetAnimator()->IsAnimatingOnePropertyOf(
+ ui::LayerAnimationElement::TRANSFORM)) {
+ owner_->OnAnchorBoundsChanged();
+ }
+ }
+#endif
+
private:
BubbleDialogDelegate* owner_;
- ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
+ ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
+#if !defined(OS_APPLE)
+ ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
+#endif
};
// This class is responsible for observing events on a BubbleDialogDelegate's
@@ -217,11 +255,11 @@ class BubbleDialogDelegate::BubbleWidgetObserver : public WidgetObserver {
}
void OnWidgetDestroying(Widget* widget) override {
- observer_.Remove(widget);
owner_->OnWidgetDestroying(widget);
}
void OnWidgetDestroyed(Widget* widget) override {
+ observer_.Remove(widget);
owner_->OnWidgetDestroyed(widget);
}
@@ -250,10 +288,6 @@ class BubbleDialogDelegate::BubbleWidgetObserver : public WidgetObserver {
owner_->OnWidgetActivationChanged(widget, active);
}
- void OnWidgetPaintAsActiveChanged(Widget* widget, bool as_active) override {
- owner_->OnBubbleWidgetPaintAsActiveChanged(as_active);
- }
-
private:
BubbleDialogDelegate* owner_;
ScopedObserver<views::Widget, views::WidgetObserver> observer_{this};
@@ -283,7 +317,7 @@ Widget* BubbleDialogDelegate::CreateBubble(
bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView());
Widget* bubble_widget = CreateBubbleWidget(bubble_delegate);
-#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX)
+#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_APPLE)
// Linux clips bubble windows that extend outside their parent window bounds.
// Mac never adjusts.
bubble_delegate->set_adjust_if_offscreen(false);
@@ -292,6 +326,10 @@ Widget* BubbleDialogDelegate::CreateBubble(
bubble_delegate->SizeToContents();
bubble_delegate->bubble_widget_observer_ =
std::make_unique<BubbleWidgetObserver>(bubble_delegate, bubble_widget);
+ bubble_delegate->paint_as_active_subscription_ =
+ bubble_widget->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
+ &BubbleDialogDelegate::OnBubbleWidgetPaintAsActiveChanged,
+ base::Unretained(bubble_delegate)));
return bubble_widget;
}
@@ -330,9 +368,9 @@ BubbleDialogDelegate* BubbleDialogDelegate::AsBubbleDialogDelegate() {
return this;
}
-NonClientFrameView* BubbleDialogDelegate::CreateNonClientFrameView(
- Widget* widget) {
- BubbleFrameView* frame = new BubbleDialogFrameView(title_margins_);
+std::unique_ptr<NonClientFrameView>
+BubbleDialogDelegate::CreateNonClientFrameView(Widget* widget) {
+ auto frame = std::make_unique<BubbleDialogFrameView>(title_margins_);
LayoutProvider* provider = LayoutProvider::Get();
frame->set_footnote_margins(
@@ -341,12 +379,8 @@ NonClientFrameView* BubbleDialogDelegate::CreateNonClientFrameView(
std::unique_ptr<BubbleBorder> border =
std::make_unique<BubbleBorder>(arrow(), GetShadow(), color());
- if (CustomShadowsSupported() && GetParams().round_corners) {
- border->SetCornerRadius(
- base::FeatureList::IsEnabled(features::kEnableMDRoundedCornersOnDialogs)
- ? provider->GetCornerRadiusMetric(views::EMPHASIS_HIGH)
- : 2);
- }
+ if (CustomShadowsSupported() && GetParams().round_corners)
+ border->SetCornerRadius(GetCornerRadius());
frame->SetBubbleBorder(std::move(border));
return frame;
@@ -354,13 +388,17 @@ NonClientFrameView* BubbleDialogDelegate::CreateNonClientFrameView(
ClientView* BubbleDialogDelegate::CreateClientView(Widget* widget) {
client_view_ = DialogDelegate::CreateClientView(widget);
- // In order for the |client_view|'s content view hierarchy to respect its clip
- // mask we must paint to a layer. This is necessary because layers do not
- // respect the clip of a non-layer backed parent.
+ // In order for the |client_view|'s content view hierarchy to respect its
+ // rounded corner clip we must paint the client view to a layer. This is
+ // necessary because layers do not respect the clip of a non-layer backed
+ // parent.
if (base::FeatureList::IsEnabled(
features::kEnableMDRoundedCornersOnDialogs) &&
GetProperty(kPaintClientToLayer)) {
client_view_->SetPaintToLayer();
+ client_view_->layer()->SetRoundedCornerRadius(
+ gfx::RoundedCornersF(GetCornerRadius()));
+ client_view_->layer()->SetIsFastRoundedCorner(true);
}
return client_view_;
@@ -418,7 +456,7 @@ void BubbleDialogDelegate::OnBubbleWidgetActivationChanged(bool active) {
if (devtools_dismiss_override_)
return;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Install |mac_bubble_closer_| the first time the widget becomes active.
if (active && !mac_bubble_closer_) {
mac_bubble_closer_ = std::make_unique<ui::BubbleCloser>(
@@ -437,8 +475,15 @@ void BubbleDialogDelegate::OnAnchorWidgetBoundsChanged() {
SizeToContents();
}
-void BubbleDialogDelegate::OnBubbleWidgetPaintAsActiveChanged(bool as_active) {
- if (!as_active) {
+void BubbleDialogDelegate::OnBubbleWidgetPaintAsActiveChanged() {
+ // It's possible for GetWidget() to return null here when the Widget's
+ // ownership model is WIDGET_OWNS_NATIVE_WIDGET. In that case, the View
+ // hierarchy is torn down, which detaches rather than destroys |this| due to
+ // set_owned_by_client(). Then the native widget is destroyed, which calls
+ // back here. Since GetWidget() is implemented in terms of View::GetWidget(),
+ // which no longer has a RootView, it returns null. While there are other
+ // ways to address this, they all seem more fragile than null-checking.
+ if (!GetWidget() || !GetWidget()->ShouldPaintAsActive()) {
paint_as_active_lock_.reset();
return;
}
@@ -504,6 +549,21 @@ gfx::Rect BubbleDialogDelegate::GetAnchorRect() const {
anchor_rect_ = GetAnchorView()->GetAnchorBoundsInScreen();
anchor_rect_->Inset(anchor_view_insets_);
+
+#if !defined(OS_APPLE)
+ // GetAnchorBoundsInScreen returns values that take anchor widget's
+ // translation into account, so undo that here. Without this, features which
+ // apply transforms on windows such as ChromeOS overview mode will see bubbles
+ // offset.
+ // TODO(sammiequon): Investigate if we can remove |anchor_widget_| and just
+ // replace its calls with GetAnchorView()->GetWidget().
+ DCHECK_EQ(anchor_widget_, GetAnchorView()->GetWidget());
+ gfx::Transform transform =
+ anchor_widget_->GetNativeWindow()->layer()->GetTargetTransform();
+ if (!transform.IsIdentity())
+ anchor_rect_->Offset(-gfx::ToRoundedVector2d(transform.To2dTranslation()));
+#endif
+
return anchor_rect_.value();
}
@@ -634,7 +694,7 @@ void BubbleDialogDelegate::SetAnchorRect(const gfx::Rect& rect) {
void BubbleDialogDelegate::SizeToContents() {
gfx::Rect bubble_bounds = GetBubbleBounds();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// GetBubbleBounds() doesn't take the Mac NativeWindow's style mask into
// account, so we need to adjust the size.
gfx::Size actual_size =
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h
index 4653b6a184e..237d2a11168 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -20,7 +20,7 @@
#include "ui/views/widget/widget_observer.h"
#include "ui/views/window/dialog_delegate.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/cocoa/bubble_closer.h"
#endif
@@ -59,7 +59,8 @@ class VIEWS_EXPORT BubbleDialogDelegate : public DialogDelegate,
// DialogDelegate:
BubbleDialogDelegate* AsBubbleDialogDelegate() override;
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override;
ClientView* CreateClientView(Widget* widget) override;
ax::mojom::Role GetAccessibleWindowRole() override;
@@ -310,7 +311,7 @@ class VIEWS_EXPORT BubbleDialogDelegate : public DialogDelegate,
void OnBubbleWidgetClosing();
void OnBubbleWidgetVisibilityChanged(bool visible);
void OnBubbleWidgetActivationChanged(bool active);
- void OnBubbleWidgetPaintAsActiveChanged(bool as_active);
+ void OnBubbleWidgetPaintAsActiveChanged();
void OnDeactivate();
@@ -327,6 +328,8 @@ class VIEWS_EXPORT BubbleDialogDelegate : public DialogDelegate,
std::unique_ptr<AnchorViewObserver> anchor_view_observer_;
std::unique_ptr<AnchorWidgetObserver> anchor_widget_observer_;
std::unique_ptr<BubbleWidgetObserver> bubble_widget_observer_;
+ std::unique_ptr<Widget::PaintAsActiveCallbackList::Subscription>
+ paint_as_active_subscription_;
std::unique_ptr<Widget::PaintAsActiveLock> paint_as_active_lock_;
bool adjust_if_offscreen_ = true;
bool focus_traversable_from_anchor_view_ = true;
@@ -350,7 +353,7 @@ class VIEWS_EXPORT BubbleDialogDelegate : public DialogDelegate,
// Pointer to this bubble's ClientView.
ClientView* client_view_ = nullptr;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Special handler for close_on_deactivate() on Mac. Window (de)activation is
// suppressed by the WindowServer when clicking rapidly, so the bubble must
// monitor clicks as well for the desired behavior.
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index f2d4cc73ada..1a742163a61 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -573,13 +573,13 @@ TEST_F(BubbleDialogDelegateViewTest, VisibleAnchorChanges) {
Widget* bubble_widget =
BubbleDialogDelegateView::CreateBubble(bubble_delegate);
bubble_widget->Show();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// All child widgets make the parent paint as active on Mac.
// See https://crbug.com/1046540
EXPECT_TRUE(anchor_widget->ShouldPaintAsActive());
#else
EXPECT_FALSE(anchor_widget->ShouldPaintAsActive());
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
bubble_delegate->SetAnchorView(anchor_widget->GetContentsView());
EXPECT_TRUE(anchor_widget->ShouldPaintAsActive());
diff --git a/chromium/ui/views/bubble/bubble_dialog_model_host.cc b/chromium/ui/views/bubble/bubble_dialog_model_host.cc
new file mode 100644
index 00000000000..331d71845b9
--- /dev/null
+++ b/chromium/ui/views/bubble/bubble_dialog_model_host.cc
@@ -0,0 +1,347 @@
+// 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 "ui/views/bubble/bubble_dialog_model_host.h"
+
+#include <utility>
+
+#include "ui/base/models/combobox_model.h"
+#include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_provider.h"
+
+namespace views {
+namespace {
+// Note that textfields and comboboxes share column sets.
+constexpr int kTextfieldColumnSetId = 0;
+// Column sets used for fields where an individual control spans the entire
+// dialog width.
+constexpr int kSingleColumnSetId = 1;
+
+DialogContentType FieldTypeToContentType(ui::DialogModelField::Type type) {
+ switch (type) {
+ case ui::DialogModelField::kButton:
+ return DialogContentType::CONTROL;
+ case ui::DialogModelField::kBodyText:
+ return DialogContentType::TEXT;
+ case ui::DialogModelField::kTextfield:
+ return DialogContentType::CONTROL;
+ case ui::DialogModelField::kCombobox:
+ return DialogContentType::CONTROL;
+ }
+ NOTREACHED();
+ return DialogContentType::CONTROL;
+}
+
+} // namespace
+
+BubbleDialogModelHost::BubbleDialogModelHost(
+ std::unique_ptr<ui::DialogModel> model)
+ : model_(std::move(model)) {
+ model_->set_host(GetPassKey(), this);
+
+ ConfigureGridLayout();
+
+ SetAcceptCallback(base::BindOnce(&ui::DialogModel::OnDialogAccepted,
+ base::Unretained(model_.get()),
+ GetPassKey()));
+ SetCancelCallback(base::BindOnce(&ui::DialogModel::OnDialogCancelled,
+ base::Unretained(model_.get()),
+ GetPassKey()));
+ SetCloseCallback(base::BindOnce(&ui::DialogModel::OnDialogClosed,
+ base::Unretained(model_.get()),
+ GetPassKey()));
+ RegisterWindowClosingCallback(
+ base::BindOnce(&ui::DialogModel::OnWindowClosing,
+ base::Unretained(model_.get()), GetPassKey()));
+
+ int button_mask = ui::DIALOG_BUTTON_NONE;
+ auto* ok_button = model_->ok_button(GetPassKey());
+ if (ok_button) {
+ button_mask |= ui::DIALOG_BUTTON_OK;
+ if (!ok_button->label(GetPassKey()).empty())
+ SetButtonLabel(ui::DIALOG_BUTTON_OK, ok_button->label(GetPassKey()));
+ }
+
+ auto* cancel_button = model_->cancel_button(GetPassKey());
+ if (cancel_button) {
+ button_mask |= ui::DIALOG_BUTTON_CANCEL;
+ if (!cancel_button->label(GetPassKey()).empty())
+ SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
+ cancel_button->label(GetPassKey()));
+ }
+
+ // TODO(pbos): Consider refactoring ::SetExtraView() so it can be called after
+ // the Widget is created and still be picked up. Moving this to
+ // OnDialogInitialized() will not work until then.
+ auto* extra_button = model_->extra_button(GetPassKey());
+ if (extra_button) {
+ OnViewCreatedForField(SetExtraView(std::make_unique<MdTextButton>(
+ this, extra_button->label(GetPassKey()))),
+ extra_button);
+ }
+
+ SetButtons(button_mask);
+
+ WidgetDelegate::SetTitle(model_->title(GetPassKey()));
+ WidgetDelegate::SetShowCloseButton(model_->show_close_button(GetPassKey()));
+
+ AddInitialFields();
+}
+
+BubbleDialogModelHost::~BubbleDialogModelHost() {
+ // Remove children as they may refer to the soon-to-be-destructed model.
+ RemoveAllChildViews(true);
+}
+
+View* BubbleDialogModelHost::GetInitiallyFocusedView() {
+ base::Optional<int> unique_id = model_->initially_focused_field(GetPassKey());
+
+ if (!unique_id)
+ return BubbleDialogDelegateView::GetInitiallyFocusedView();
+
+ return FieldToView(model_->GetFieldByUniqueId(unique_id.value()));
+}
+
+void BubbleDialogModelHost::OnDialogInitialized() {
+ // Dialog buttons are added on dialog initialization.
+ if (GetOkButton())
+ OnViewCreatedForField(GetOkButton(), model_->ok_button(GetPassKey()));
+
+ if (GetCancelButton()) {
+ OnViewCreatedForField(GetCancelButton(),
+ model_->cancel_button(GetPassKey()));
+ }
+}
+
+gfx::Size BubbleDialogModelHost::CalculatePreferredSize() const {
+ // TODO(pbos): Move DISTANCE_BUBBLE_PREFERRED_WIDTH into views.
+ const int width = 320 - margins().width();
+ return gfx::Size(width, GetHeightForWidth(width));
+}
+
+void BubbleDialogModelHost::Close() {
+ // TODO(pbos): Synchronously destroy model here, as-if closing immediately.
+ DCHECK(GetWidget());
+ GetWidget()->Close();
+}
+
+void BubbleDialogModelHost::SelectAllText(int unique_id) {
+ static_cast<Textfield*>(
+ FieldToView(model_->GetTextfieldByUniqueId(unique_id)))
+ ->SelectAll(false);
+}
+
+void BubbleDialogModelHost::OnFieldAdded(ui::DialogModelField* field) {
+ // TODO(pbos): Add support for adding fields while the model is hosted.
+ NOTREACHED();
+}
+
+void BubbleDialogModelHost::AddInitialFields() {
+ // TODO(pbos): Turn this method into consecutive OnFieldAdded(field) calls.
+
+ DCHECK(children().empty()) << "This should only be called once.";
+
+ bool first_row = true;
+ const auto& fields = model_->fields(GetPassKey());
+ const DialogContentType first_field_content_type =
+ fields.empty()
+ ? DialogContentType::CONTROL
+ : FieldTypeToContentType(fields.front()->type(GetPassKey()));
+ DialogContentType last_field_content_type = first_field_content_type;
+ for (const auto& field : fields) {
+ // TODO(pbos): This needs to take previous field type + next field type into
+ // account to do this properly.
+ if (!first_row) {
+ // TODO(pbos): Move DISTANCE_CONTROL_LIST_VERTICAL to
+ // views::LayoutProvider and replace "12" here.
+ GetGridLayout()->AddPaddingRow(GridLayout::kFixedSize, 12);
+ }
+
+ View* last_view = nullptr;
+ switch (field->type(GetPassKey())) {
+ case ui::DialogModelField::kButton:
+ // TODO(pbos): Add support for buttons that are part of content area.
+ continue;
+ case ui::DialogModelField::kBodyText:
+ last_view = AddOrUpdateBodyText(field->AsBodyText(GetPassKey()));
+ break;
+ case ui::DialogModelField::kCombobox:
+ last_view = AddOrUpdateCombobox(field->AsCombobox(GetPassKey()));
+ break;
+ case ui::DialogModelField::kTextfield:
+ last_view = AddOrUpdateTextfield(field->AsTextfield(GetPassKey()));
+ break;
+ }
+
+ DCHECK(last_view);
+ OnViewCreatedForField(last_view, field.get());
+ last_field_content_type = FieldTypeToContentType(field->type(GetPassKey()));
+
+ // TODO(pbos): Update logic here when mixing types.
+ first_row = false;
+ }
+
+ set_margins(LayoutProvider::Get()->GetDialogInsetsForContentType(
+ first_field_content_type, last_field_content_type));
+}
+
+GridLayout* BubbleDialogModelHost::GetGridLayout() {
+ return static_cast<GridLayout*>(GetLayoutManager());
+}
+
+void BubbleDialogModelHost::ConfigureGridLayout() {
+ SetLayoutManager(std::make_unique<GridLayout>());
+ LayoutProvider* const provider = LayoutProvider::Get();
+ const int between_padding =
+ provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_HORIZONTAL);
+
+ ColumnSet* const textfield_column_set =
+ GetGridLayout()->AddColumnSet(kTextfieldColumnSetId);
+ textfield_column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
+ GridLayout::kFixedSize,
+ GridLayout::ColumnSize::kUsePreferred, 0, 0);
+ textfield_column_set->AddPaddingColumn(GridLayout::kFixedSize,
+ between_padding);
+
+ textfield_column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0,
+ GridLayout::ColumnSize::kFixed, 0, 0);
+
+ GetGridLayout()
+ ->AddColumnSet(kSingleColumnSetId)
+ ->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0,
+ GridLayout::ColumnSize::kUsePreferred, 0, 0);
+}
+
+Textfield* BubbleDialogModelHost::AddOrUpdateTextfield(
+ ui::DialogModelTextfield* model) {
+ // TODO(pbos): Support updates to the existing model.
+
+ auto textfield = std::make_unique<Textfield>();
+ textfield->SetAccessibleName(model->accessible_name(GetPassKey()).empty()
+ ? model->label(GetPassKey())
+ : model->accessible_name(GetPassKey()));
+ textfield->SetText(model->text());
+
+ property_changed_subscriptions_.push_back(textfield->AddTextChangedCallback(
+ base::BindRepeating(&BubbleDialogModelHost::NotifyTextfieldTextChanged,
+ base::Unretained(this), textfield.get())));
+
+ auto* textfield_ptr = textfield.get();
+ AddLabelAndField(model->label(GetPassKey()), std::move(textfield),
+ textfield_ptr->GetFontList());
+
+ return textfield_ptr;
+}
+
+Label* BubbleDialogModelHost::AddOrUpdateBodyText(
+ ui::DialogModelBodyText* field) {
+ // TODO(pbos): Handle updating existing field.
+
+ auto text_label = std::make_unique<Label>(
+ field->text(GetPassKey()), style::CONTEXT_MESSAGE_BOX_BODY_TEXT,
+ field->is_secondary(GetPassKey()) ? style::STYLE_SECONDARY
+ : style::STYLE_PRIMARY);
+ text_label->SetMultiLine(true);
+ text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+
+ auto* layout = GetGridLayout();
+ layout->StartRow(1.0, kSingleColumnSetId);
+
+ return layout->AddView(std::move(text_label));
+}
+
+Combobox* BubbleDialogModelHost::AddOrUpdateCombobox(
+ ui::DialogModelCombobox* model) {
+ // TODO(pbos): Handle updating existing field.
+
+ auto combobox = std::make_unique<Combobox>(model->combobox_model());
+ combobox->SetAccessibleName(model->accessible_name(GetPassKey()).empty()
+ ? model->label(GetPassKey())
+ : model->accessible_name(GetPassKey()));
+ combobox->set_listener(this);
+ // TODO(pbos): Add subscription to combobox selected-index changes.
+ combobox->SetSelectedIndex(model->selected_index());
+ auto* combobox_ptr = combobox.get();
+ AddLabelAndField(model->label(GetPassKey()), std::move(combobox),
+ combobox_ptr->GetFontList());
+ return combobox_ptr;
+}
+
+void BubbleDialogModelHost::AddLabelAndField(const base::string16& label_text,
+ std::unique_ptr<View> field,
+ const gfx::FontList& field_font) {
+ constexpr int kFontContext = style::CONTEXT_LABEL;
+ constexpr int kFontStyle = style::STYLE_PRIMARY;
+
+ int row_height = LayoutProvider::GetControlHeightForFont(
+ kFontContext, kFontStyle, field_font);
+ GridLayout* const layout = GetGridLayout();
+ layout->StartRow(GridLayout::kFixedSize, kTextfieldColumnSetId, row_height);
+ layout->AddView(
+ std::make_unique<Label>(label_text, kFontContext, kFontStyle));
+ layout->AddView(std::move(field));
+}
+
+void BubbleDialogModelHost::NotifyTextfieldTextChanged(Textfield* textfield) {
+ view_to_field_[textfield]
+ ->AsTextfield(GetPassKey())
+ ->OnTextChanged(GetPassKey(), textfield->GetText());
+}
+
+void BubbleDialogModelHost::NotifyComboboxSelectedIndexChanged(
+ Combobox* combobox) {
+ view_to_field_[combobox]
+ ->AsCombobox(GetPassKey())
+ ->OnSelectedIndexChanged(GetPassKey(), combobox->GetSelectedIndex());
+}
+
+void BubbleDialogModelHost::ButtonPressed(Button* sender,
+ const ui::Event& event) {
+ view_to_field_[sender]
+ ->AsButton(GetPassKey())
+ ->OnPressed(GetPassKey(), event);
+}
+
+void BubbleDialogModelHost::OnPerformAction(Combobox* combobox) {
+ // TODO(pbos): This should be a subscription through the Combobox directly,
+ // but Combobox right now doesn't support listening to selected-index changes.
+ NotifyComboboxSelectedIndexChanged(combobox);
+
+ view_to_field_[combobox]
+ ->AsCombobox(GetPassKey())
+ ->OnPerformAction(GetPassKey());
+}
+
+void BubbleDialogModelHost::OnViewCreatedForField(View* view,
+ ui::DialogModelField* field) {
+#if DCHECK_IS_ON()
+ // Make sure neither view nor field has been previously used.
+ for (const auto& kv : view_to_field_) {
+ DCHECK_NE(kv.first, view);
+ DCHECK_NE(kv.second, field);
+ }
+#endif // DCHECK_IS_ON()
+ view_to_field_[view] = field;
+ for (const auto& accelerator : field->accelerators(GetPassKey()))
+ view->AddAccelerator(accelerator);
+}
+
+View* BubbleDialogModelHost::FieldToView(ui::DialogModelField* field) {
+ DCHECK(field);
+ for (auto& kv : view_to_field_) {
+ if (kv.second == field)
+ return kv.first;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+} // namespace views
diff --git a/chromium/ui/views/bubble/bubble_dialog_model_host.h b/chromium/ui/views/bubble/bubble_dialog_model_host.h
new file mode 100644
index 00000000000..3730aa27a78
--- /dev/null
+++ b/chromium/ui/views/bubble/bubble_dialog_model_host.h
@@ -0,0 +1,88 @@
+// 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 UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
+#define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/combobox/combobox_listener.h"
+
+namespace views {
+class Combobox;
+class GridLayout;
+class Label;
+class Textfield;
+
+// BubbleDialogModelHost is a views implementation of ui::DialogModelHost which
+// hosts a ui::DialogModel as a BubbleDialogDelegateView. This exposes
+// views-specific methods such as SetAnchorView(), SetArrow() and
+// SetHighlightedButton(). For methods that are reflected in ui::DialogModelHost
+// (such as ::Close()), preferusing the ui::DialogModelHost to avoid
+// platform-specific code (GetWidget()->Close()) where unnecessary. For those
+// methods, note that this can be retrieved as a ui::DialogModelHost through
+// DialogModel::host(). This helps minimize platform-specific code from
+// platform-agnostic model-delegate code.
+class VIEWS_EXPORT BubbleDialogModelHost : public BubbleDialogDelegateView,
+ public ui::DialogModelHost,
+ public ButtonListener,
+ public ComboboxListener {
+ public:
+ // Constructs a BubbleDialogModelHost, which for most purposes is to used as a
+ // BubbleDialogDelegateView. The BubbleDialogDelegateView is nominally handed
+ // to BubbleDialogDelegateView::CreateBubble() which returns a Widget that has
+ // taken ownership of the bubble. Widget::Show() finally shows the bubble.
+ explicit BubbleDialogModelHost(std::unique_ptr<ui::DialogModel> model);
+ ~BubbleDialogModelHost() override;
+
+ // BubbleDialogDelegateView:
+ // TODO(pbos): Populate initparams with initial view instead of overriding
+ // GetInitiallyFocusedView().
+ View* GetInitiallyFocusedView() override;
+ void OnDialogInitialized() override;
+ gfx::Size CalculatePreferredSize() const override;
+
+ // ui::DialogModelHost:
+ void Close() override;
+ void SelectAllText(int unique_id) override;
+ void OnFieldAdded(ui::DialogModelField* field) override;
+
+ // ButtonListener:
+ void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+ // ComboboxListener:
+ void OnPerformAction(views::Combobox* combobox) override;
+
+ private:
+ GridLayout* GetGridLayout();
+ void ConfigureGridLayout();
+
+ void AddInitialFields();
+ Label* AddOrUpdateBodyText(ui::DialogModelBodyText* field);
+ Combobox* AddOrUpdateCombobox(ui::DialogModelCombobox* field);
+ Textfield* AddOrUpdateTextfield(ui::DialogModelTextfield* field);
+ void AddLabelAndField(const base::string16& label_text,
+ std::unique_ptr<views::View> field,
+ const gfx::FontList& field_font);
+
+ void OnViewCreatedForField(View* view, ui::DialogModelField* field);
+
+ void NotifyTextfieldTextChanged(views::Textfield* textfield);
+ void NotifyComboboxSelectedIndexChanged(views::Combobox* combobox);
+
+ View* FieldToView(ui::DialogModelField* field);
+
+ std::unique_ptr<ui::DialogModel> model_;
+ base::flat_map<View*, ui::DialogModelField*> view_to_field_;
+ std::vector<PropertyChangedSubscription> property_changed_subscriptions_;
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc
index 7f94147a5ce..fe4765b61c1 100644
--- a/chromium/ui/views/bubble/bubble_frame_view.cc
+++ b/chromium/ui/views/bubble/bubble_frame_view.cc
@@ -19,6 +19,7 @@
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/skia_util.h"
#include "ui/native_theme/native_theme.h"
@@ -184,29 +185,30 @@ bool BubbleFrameView::GetClientMask(const gfx::Size& size, SkPath* path) const {
DCHECK_EQ(GetBoundsForClientView().size(), size);
DCHECK_EQ(GetWidget()->client_view()->size(), size);
- const int radius = bubble_border_->corner_radius();
- const gfx::Insets insets =
- GetClientInsetsForFrameWidth(GetContentsBounds().width());
-
- // A mask is not needed if the content does not overlap the rounded corners.
- if ((insets.top() > radius && insets.bottom() > radius) ||
- (insets.left() > radius && insets.right() > radius)) {
+ // BubbleFrameView only returns a SkPath for the purpose of clipping the
+ // client view's corners so that it fits within the borders of its rounded
+ // frame. With MD rounded coners if a client view is painted to a layer the
+ // rounding is handled by the |SetRoundedCornerRadius()| layer API, so we
+ // return false here.
+ if (base::FeatureList::IsEnabled(
+ features::kEnableMDRoundedCornersOnDialogs) &&
+ GetWidget()->client_view()->layer()) {
return false;
}
- // We want to clip the client view to a rounded rect that's consistent with
- // the bubble's rounded border. However, if there is a header, the top of the
- // client view should be straight and flush with that. Likewise, if there is
- // a footer, the client view should be straight and flush with that. Therefore
- // we set the corner radii separately for top and bottom.
- const SkRect rect = SkRect::MakeIWH(size.width(), size.height());
- const SkScalar top_radius = header_view_ ? 0.0f : radius;
- const SkScalar bottom_radius = footnote_container_ ? 0.0f : radius;
+ const gfx::RoundedCornersF corner_radii = GetClientCornerRadii();
+
+ // If corner radii are all zero we do not need to apply a mask.
+ if (corner_radii.IsEmpty())
+ return false;
+
// Format is upper-left x, upper-left y, upper-right x, and so forth,
// clockwise around the boundary.
- SkScalar radii[]{top_radius, top_radius, top_radius, top_radius,
- bottom_radius, bottom_radius, bottom_radius, bottom_radius};
- path->addRoundRect(rect, radii);
+ SkScalar radii[]{corner_radii.upper_left(), corner_radii.upper_left(),
+ corner_radii.upper_right(), corner_radii.upper_right(),
+ corner_radii.lower_right(), corner_radii.lower_right(),
+ corner_radii.lower_left(), corner_radii.lower_left()};
+ path->addRoundRect(SkRect::MakeIWH(size.width(), size.height()), radii);
return true;
}
@@ -342,7 +344,7 @@ gfx::Size BubbleFrameView::GetMaximumSize() const {
// the OS doesn't give the user controls to resize a bubble.
return gfx::Size();
#else
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Allow BubbleFrameView dialogs to be resizable on Mac.
if (GetWidget()->widget_delegate()->CanResize()) {
gfx::Size client_size = GetWidget()->client_view()->GetMaximumSize();
@@ -350,7 +352,7 @@ gfx::Size BubbleFrameView::GetMaximumSize() const {
return client_size;
return GetWindowBoundsForClientBounds(gfx::Rect(client_size)).size();
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
// Non-dialog bubbles should be non-resizable, so its max size is its
// preferred size.
return GetPreferredSize();
@@ -459,8 +461,16 @@ void BubbleFrameView::OnThemeChanged() {
void BubbleFrameView::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
- if (details.is_add && details.child == this)
+ if (details.is_add && details.child == this) {
OnThemeChanged();
+ UpdateClientLayerCornerRadius();
+ }
+
+ // We need to update the client view's corner radius whenever the header or
+ // footer are added/removed from the bubble frame so that the client view
+ // sits flush with both.
+ if (details.parent == this)
+ UpdateClientLayerCornerRadius();
if (!details.is_add && details.parent == footnote_container_ &&
footnote_container_->children().size() == 1 &&
@@ -532,9 +542,13 @@ void BubbleFrameView::SetFootnoteView(std::unique_ptr<View> view) {
delete footnote_container_;
footnote_container_ = nullptr;
if (view) {
+ // Insert the footnote container before |close_| so that the footnote is
+ // inserted before caption buttons in the focus cycle.
int radius = bubble_border_ ? bubble_border_->corner_radius() : 0;
- footnote_container_ = AddChildView(std::make_unique<FootnoteContainerView>(
- footnote_margins_, std::move(view), radius));
+ footnote_container_ =
+ AddChildViewAt(std::make_unique<FootnoteContainerView>(
+ footnote_margins_, std::move(view), radius),
+ GetIndexOf(close_));
}
InvalidateLayout();
}
@@ -549,6 +563,7 @@ View* BubbleFrameView::GetFootnoteView() const {
void BubbleFrameView::SetCornerRadius(int radius) {
bubble_border_->SetCornerRadius(radius);
+ UpdateClientLayerCornerRadius();
}
void BubbleFrameView::SetArrow(BubbleBorder::Arrow arrow) {
@@ -660,6 +675,34 @@ gfx::Rect BubbleFrameView::GetCloseButtonMirroredBounds() const {
return close_->GetMirroredBounds();
}
+gfx::RoundedCornersF BubbleFrameView::GetClientCornerRadii() const {
+ DCHECK(bubble_border_);
+ const int radius = bubble_border_->corner_radius();
+ const gfx::Insets insets =
+ GetClientInsetsForFrameWidth(GetContentsBounds().width());
+
+ // Rounded corners do not need to be applied to the client view if the client
+ // view is sufficiently inset such that its unclipped bounds will not
+ // intersect with the corners of the containing bubble frame view.
+ if ((insets.top() > radius && insets.bottom() > radius) ||
+ (insets.left() > radius && insets.right() > radius)) {
+ return gfx::RoundedCornersF();
+ }
+
+ // We want to clip the client view to a rounded rect that's consistent with
+ // the bubble's rounded border. However, if there is a header, the top of the
+ // client view should be straight and flush with that. Likewise, if there is
+ // a footer, the client view should be straight and flush with that. Therefore
+ // we set the corner radii separately for top and bottom.
+ gfx::RoundedCornersF corner_radii;
+ corner_radii.set_upper_left(header_view_ ? 0 : radius);
+ corner_radii.set_upper_right(header_view_ ? 0 : radius);
+ corner_radii.set_lower_left(footnote_container_ ? 0 : radius);
+ corner_radii.set_lower_right(footnote_container_ ? 0 : radius);
+
+ return corner_radii;
+}
+
void BubbleFrameView::MirrorArrowIfOutOfBounds(
bool vertical,
const gfx::Rect& anchor_rect,
@@ -838,4 +881,13 @@ int BubbleFrameView::GetHeaderHeightForFrameWidth(int frame_width) const {
: 0;
}
+void BubbleFrameView::UpdateClientLayerCornerRadius() {
+ if (GetWidget() && GetWidget()->client_view()->layer() &&
+ base::FeatureList::IsEnabled(
+ features::kEnableMDRoundedCornersOnDialogs)) {
+ GetWidget()->client_view()->layer()->SetRoundedCornerRadius(
+ GetClientCornerRadii());
+ }
+}
+
} // namespace views
diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h
index 8e71b915b65..117f032e858 100644
--- a/chromium/ui/views/bubble/bubble_frame_view.h
+++ b/chromium/ui/views/bubble/bubble_frame_view.h
@@ -20,6 +20,10 @@
#include "ui/views/input_event_activation_protector.h"
#include "ui/views/window/non_client_view.h"
+namespace gfx {
+class RoundedCornersF;
+}
+
namespace views {
class FootnoteContainerView;
@@ -179,6 +183,12 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView,
bool IsCloseButtonVisible() const;
gfx::Rect GetCloseButtonMirroredBounds() const;
+ // Helper function that gives the corner radius values that should be applied
+ // to the BubbleFrameView's client view. These values depend on the amount of
+ // inset present on the client view and the presence of header and footer
+ // views.
+ gfx::RoundedCornersF GetClientCornerRadii() const;
+
BubbleBorder* bubble_border_for_testing() const { return bubble_border_; }
private:
@@ -231,6 +241,12 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView,
// if there is no header view or if it is not visible.
int GetHeaderHeightForFrameWidth(int frame_width) const;
+ // Updates the corner radius of a layer backed client view for MD rounded
+ // corners.
+ // TODO(tluk): Use this and remove the need for GetClientMask() for clipping
+ // client views to the bubble border's bounds.
+ void UpdateClientLayerCornerRadius();
+
// The bubble border.
BubbleBorder* bubble_border_ = nullptr;
diff --git a/chromium/ui/views/bubble/info_bubble.cc b/chromium/ui/views/bubble/info_bubble.cc
index 9bcded002c6..4a857e712c6 100644
--- a/chromium/ui/views/bubble/info_bubble.cc
+++ b/chromium/ui/views/bubble/info_bubble.cc
@@ -5,6 +5,7 @@
#include "ui/views/bubble/info_bubble.h"
#include <memory>
+#include <utility>
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
@@ -58,10 +59,9 @@ InfoBubble::InfoBubble(View* anchor, const base::string16& message)
SetCanActivate(false);
SetLayoutManager(std::make_unique<FillLayout>());
- Label* label = new Label(message);
- label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- label->SetMultiLine(true);
- AddChildView(label);
+ label_ = AddChildView(std::make_unique<Label>(message));
+ label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ label_->SetMultiLine(true);
}
InfoBubble::~InfoBubble() = default;
@@ -78,13 +78,15 @@ void InfoBubble::Hide() {
widget->Close();
}
-NonClientFrameView* InfoBubble::CreateNonClientFrameView(Widget* widget) {
+std::unique_ptr<NonClientFrameView> InfoBubble::CreateNonClientFrameView(
+ Widget* widget) {
DCHECK(!frame_);
- frame_ = new InfoBubbleFrame(margins());
- frame_->set_available_bounds(anchor_widget()->GetWindowBoundsInScreen());
- frame_->SetBubbleBorder(
+ auto frame = std::make_unique<InfoBubbleFrame>(margins());
+ frame->set_available_bounds(anchor_widget()->GetWindowBoundsInScreen());
+ frame->SetBubbleBorder(
std::make_unique<BubbleBorder>(arrow(), GetShadow(), color()));
- return frame_;
+ frame_ = frame.get();
+ return frame;
}
gfx::Size InfoBubble::CalculatePreferredSize() const {
diff --git a/chromium/ui/views/bubble/info_bubble.h b/chromium/ui/views/bubble/info_bubble.h
index d5514aa03ea..0007d73eab6 100644
--- a/chromium/ui/views/bubble/info_bubble.h
+++ b/chromium/ui/views/bubble/info_bubble.h
@@ -5,6 +5,8 @@
#ifndef UI_VIEWS_BUBBLE_INFO_BUBBLE_H_
#define UI_VIEWS_BUBBLE_INFO_BUBBLE_H_
+#include <memory>
+
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/strings/string16.h"
@@ -13,9 +15,10 @@
namespace views {
class InfoBubbleFrame;
+class Label;
// Class to create and manage an information bubble for errors or tooltips.
-class InfoBubble : public BubbleDialogDelegateView {
+class VIEWS_EXPORT InfoBubble : public BubbleDialogDelegateView {
public:
METADATA_HEADER(InfoBubble);
@@ -29,7 +32,8 @@ class InfoBubble : public BubbleDialogDelegateView {
void Hide();
// BubbleDialogDelegateView:
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override;
gfx::Size CalculatePreferredSize() const override;
void OnWidgetDestroyed(Widget* widget) override;
void OnWidgetBoundsChanged(Widget* widget,
@@ -42,6 +46,8 @@ class InfoBubble : public BubbleDialogDelegateView {
preferred_width_ = preferred_width;
}
+ const Label* label_for_testing() const { return label_; }
+
private:
// Updates the position of the bubble.
void UpdatePosition();
@@ -49,6 +55,7 @@ class InfoBubble : public BubbleDialogDelegateView {
Widget* widget_; // Weak, may be NULL.
View* const anchor_; // Weak.
InfoBubbleFrame* frame_; // Weak, owned by widget.
+ Label* label_;
// The width this bubble prefers to be. Default is 0 (no preference).
int preferred_width_;
diff --git a/chromium/ui/views/bubble/info_bubble_unittest.cc b/chromium/ui/views/bubble/info_bubble_unittest.cc
new file mode 100644
index 00000000000..b5299d99ec8
--- /dev/null
+++ b/chromium/ui/views/bubble/info_bubble_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/bubble/info_bubble.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/test/test_widget_observer.h"
+#include "ui/views/test/view_metadata_test_utils.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+namespace test {
+
+class InfoBubbleTest : public ViewsTestBase {
+ public:
+ InfoBubbleTest() = default;
+ InfoBubbleTest(const InfoBubbleTest&) = delete;
+ InfoBubbleTest& operator=(const InfoBubbleTest&) = delete;
+ ~InfoBubbleTest() override = default;
+
+ // ViewsTestBase:
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+
+ Widget::InitParams params =
+ CreateParamsForTestWidget(Widget::InitParams::TYPE_WINDOW);
+ anchor_widget_ = std::make_unique<Widget>();
+ anchor_widget_->Init(std::move(params));
+ anchor_widget_->Show();
+ }
+
+ void TearDown() override {
+ anchor_widget_.reset();
+ ViewsTestBase::TearDown();
+ }
+
+ Widget* anchor_widget() { return anchor_widget_.get(); }
+
+ private:
+ std::unique_ptr<Widget> anchor_widget_;
+};
+
+TEST_F(InfoBubbleTest, CreateInfoBubble) {
+ base::string16 text = base::ASCIIToUTF16("test message");
+
+ InfoBubble* info_bubble =
+ new InfoBubble(anchor_widget()->GetContentsView(), text);
+ info_bubble->Show();
+ TestWidgetObserver bubble_observer(info_bubble->GetWidget());
+
+ EXPECT_EQ(info_bubble->anchor(), anchor_widget()->GetContentsView());
+ EXPECT_EQ(info_bubble->anchor()->GetWidget(), anchor_widget());
+ EXPECT_EQ(text, info_bubble->label_for_testing()->GetText());
+ EXPECT_TRUE(info_bubble->GetVisible());
+ EXPECT_FALSE(bubble_observer.widget_closed());
+
+ info_bubble->Hide();
+ RunPendingMessages();
+ EXPECT_TRUE(bubble_observer.widget_closed());
+}
+
+// Ensure the InfoBubble is still sized if not supplied with a preferred width.
+TEST_F(InfoBubbleTest, TestPreferredWidthNull) {
+ InfoBubble* info_bubble =
+ new InfoBubble(anchor_widget()->GetContentsView(), base::string16());
+
+ auto child = std::make_unique<View>();
+ child->SetPreferredSize(gfx::Size(50, 50));
+
+ info_bubble->AddChildView(std::move(child));
+ info_bubble->Show();
+ EXPECT_LT(0, info_bubble->GetLocalBounds().width());
+ info_bubble->Hide();
+ RunPendingMessages();
+}
+
+TEST_F(InfoBubbleTest, TestPreferredWidth) {
+ constexpr int kPreferredWidthLarge = 800;
+ constexpr int kPreferredWidthSmall = 50;
+
+ InfoBubble* info_bubble =
+ new InfoBubble(anchor_widget()->GetContentsView(), base::string16());
+ info_bubble->Show();
+ info_bubble->set_preferred_width(kPreferredWidthLarge);
+ info_bubble->SizeToPreferredSize();
+
+ // Test to make sure the resulting |info_bubble| honors the preferred size.
+ // |info_bubble| may be slightly smaller due to having to account for margins
+ // and bubble border size.
+ EXPECT_GE(kPreferredWidthLarge, info_bubble->GetLocalBounds().width());
+ EXPECT_LT(kPreferredWidthSmall, info_bubble->GetLocalBounds().width());
+
+ info_bubble->set_preferred_width(kPreferredWidthSmall);
+ info_bubble->SizeToPreferredSize();
+
+ // |info_bubble| should now be at or smaller than the smaller preferred width.
+ EXPECT_GE(kPreferredWidthSmall, info_bubble->GetLocalBounds().width());
+ info_bubble->Hide();
+ RunPendingMessages();
+}
+
+TEST_F(InfoBubbleTest, TestInfoBubbleVisibilityHiddenAnchor) {
+ anchor_widget()->Hide();
+
+ InfoBubble* info_bubble =
+ new InfoBubble(anchor_widget()->GetContentsView(), base::string16());
+ info_bubble->Show();
+
+ EXPECT_FALSE(info_bubble->GetWidget()->IsVisible());
+ info_bubble->Hide();
+ RunPendingMessages();
+}
+
+TEST_F(InfoBubbleTest, TestInfoBubbleAnchorBoundsChanged) {
+ InfoBubble* info_bubble = new InfoBubble(anchor_widget()->GetContentsView(),
+ base::ASCIIToUTF16(""));
+ info_bubble->Show();
+
+ gfx::Rect original_bounds =
+ info_bubble->GetWidget()->GetWindowBoundsInScreen();
+
+ anchor_widget()->SetBounds(original_bounds - gfx::Vector2d(5, 5));
+
+ EXPECT_NE(original_bounds,
+ info_bubble->GetWidget()->GetWindowBoundsInScreen());
+ info_bubble->Hide();
+ RunPendingMessages();
+}
+
+// Iterate through the metadata for InfoBubble to ensure it all works.
+TEST_F(InfoBubbleTest, MetadataTest) {
+ InfoBubble* info_bubble = new InfoBubble(anchor_widget()->GetContentsView(),
+ base::ASCIIToUTF16(""));
+ info_bubble->Show();
+
+ test::TestViewMetadata(info_bubble);
+ info_bubble->Hide();
+ RunPendingMessages();
+}
+
+} // namespace test
+} // namespace views
diff --git a/chromium/ui/views/cocoa/OWNERS b/chromium/ui/views/cocoa/OWNERS
index 5668cc2de00..97725b4abe5 100644
--- a/chromium/ui/views/cocoa/OWNERS
+++ b/chromium/ui/views/cocoa/OWNERS
@@ -1,3 +1,5 @@
tapted@chromium.org
ellyjones@chromium.org
ccameron@chromium.org
+avi@chromium.org
+lgrey@chromium.org
diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.h b/chromium/ui/views/cocoa/drag_drop_client_mac.h
index 608e73a075c..6e48ad442cc 100644
--- a/chromium/ui/views/cocoa/drag_drop_client_mac.h
+++ b/chromium/ui/views/cocoa/drag_drop_client_mac.h
@@ -12,7 +12,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "components/remote_cocoa/app_shim/drag_drop_client.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/views/views_export.h"
#include "ui/views/widget/drop_helper.h"
@@ -46,7 +46,7 @@ class VIEWS_EXPORT DragDropClientMac : public remote_cocoa::DragDropClient {
void StartDragAndDrop(View* view,
std::unique_ptr<ui::OSExchangeData> data,
int operation,
- ui::DragDropTypes::DragEventSource source);
+ ui::mojom::DragEventSource source);
DropHelper* drop_helper() { return &drop_helper_; }
diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.mm b/chromium/ui/views/cocoa/drag_drop_client_mac.mm
index 16ed882e3e9..eb361a94c65 100644
--- a/chromium/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/chromium/ui/views/cocoa/drag_drop_client_mac.mm
@@ -9,6 +9,7 @@
#include "base/strings/sys_string_conversions.h"
#import "components/remote_cocoa/app_shim/bridged_content_view.h"
#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
#import "ui/base/dragdrop/os_exchange_data_provider_mac.h"
#include "ui/gfx/image/image_skia_util_mac.h"
#include "ui/views/drag_utils.h"
@@ -29,7 +30,7 @@ void DragDropClientMac::StartDragAndDrop(
View* view,
std::unique_ptr<ui::OSExchangeData> data,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
exchange_data_ = std::move(data);
source_operation_ = operation;
is_drag_source_ = true;
diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm
index 17c83e4edbb..8fb1b382134 100644
--- a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm
+++ b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -12,6 +12,8 @@
#include "base/threading/thread_task_runner_handle.h"
#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
#import "ui/base/clipboard/clipboard_util_mac.h"
+#import "ui/base/dragdrop/drag_drop_types.h"
+#import "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/gfx/image/image_unittest_util.h"
#import "ui/views/cocoa/native_widget_mac_ns_window_host.h"
#include "ui/views/test/widget_test.h"
@@ -262,8 +264,8 @@ TEST_F(DragDropClientMacTest, ReleaseCapture) {
base::Unretained(drag_drop_client())));
// It will call ReleaseCapture().
- drag_drop_client()->StartDragAndDrop(
- target_, std::move(data), 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+ drag_drop_client()->StartDragAndDrop(target_, std::move(data), 0,
+ ui::mojom::DragEventSource::kMouse);
// The capture should be released.
EXPECT_FALSE(ns_window_host_->IsMouseCaptureActive());
diff --git a/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h
index 06427b2009c..492386c61ac 100644
--- a/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h
+++ b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.h
@@ -299,7 +299,7 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost
bool GetShouldShowWindowTitle(bool* should_show_window_title) override;
bool GetCanWindowBecomeKey(bool* can_window_become_key) override;
bool GetAlwaysRenderWindowAsKey(bool* always_render_as_key) override;
- bool GetCanWindowClose(bool* can_window_close) override;
+ bool OnWindowCloseRequested(bool* can_window_close) override;
bool GetWindowFrameTitlebarHeight(bool* override_titlebar_height,
float* titlebar_height) override;
void OnFocusWindowToolbar() override;
@@ -345,7 +345,7 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost
void GetCanWindowBecomeKey(GetCanWindowBecomeKeyCallback callback) override;
void GetAlwaysRenderWindowAsKey(
GetAlwaysRenderWindowAsKeyCallback callback) override;
- void GetCanWindowClose(GetCanWindowCloseCallback callback) override;
+ void OnWindowCloseRequested(OnWindowCloseRequestedCallback callback) override;
void GetWindowFrameTitlebarHeight(
GetWindowFrameTitlebarHeightCallback callback) override;
void GetRootViewAccessibilityToken(
diff --git a/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm
index 2256c365af5..39c39c1585e 100644
--- a/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm
+++ b/chromium/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -155,7 +155,8 @@ class BridgedNativeWidgetHostDummy
bool always_render_as_key = false;
std::move(callback).Run(always_render_as_key);
}
- void GetCanWindowClose(GetCanWindowCloseCallback callback) override {
+ void OnWindowCloseRequested(
+ OnWindowCloseRequestedCallback callback) override {
bool can_window_close = false;
std::move(callback).Run(can_window_close);
}
@@ -1094,12 +1095,14 @@ bool NativeWidgetMacNSWindowHost::GetAlwaysRenderWindowAsKey(
return true;
}
-bool NativeWidgetMacNSWindowHost::GetCanWindowClose(bool* can_window_close) {
+bool NativeWidgetMacNSWindowHost::OnWindowCloseRequested(
+ bool* can_window_close) {
*can_window_close = true;
views::NonClientView* non_client_view =
root_view_ ? root_view_->GetWidget()->non_client_view() : nullptr;
if (non_client_view)
- *can_window_close = non_client_view->CanClose();
+ *can_window_close = non_client_view->OnWindowCloseRequested() ==
+ CloseRequestResult::kCanClose;
return true;
}
@@ -1272,10 +1275,10 @@ void NativeWidgetMacNSWindowHost::GetAlwaysRenderWindowAsKey(
std::move(callback).Run(always_render_as_key);
}
-void NativeWidgetMacNSWindowHost::GetCanWindowClose(
- GetCanWindowCloseCallback callback) {
+void NativeWidgetMacNSWindowHost::OnWindowCloseRequested(
+ OnWindowCloseRequestedCallback callback) {
bool can_window_close = false;
- GetCanWindowClose(&can_window_close);
+ OnWindowCloseRequested(&can_window_close);
std::move(callback).Run(can_window_close);
}
diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.mm b/chromium/ui/views/cocoa/tooltip_manager_mac.mm
index da34d6035bb..113b610cb4f 100644
--- a/chromium/ui/views/cocoa/tooltip_manager_mac.mm
+++ b/chromium/ui/views/cocoa/tooltip_manager_mac.mm
@@ -10,6 +10,7 @@
#include "ui/base/cocoa/cocoa_base_utils.h"
#include "ui/gfx/font_list.h"
#import "ui/gfx/mac/coordinate_conversion.h"
+#include "ui/gfx/platform_font_mac.h"
namespace {
@@ -32,8 +33,10 @@ int TooltipManagerMac::GetMaxWidth(const gfx::Point& location) const {
}
const gfx::FontList& TooltipManagerMac::GetFontList() const {
- static base::NoDestructor<gfx::FontList> font_list(
- []() { return gfx::Font([NSFont toolTipsFontOfSize:0]); }());
+ static base::NoDestructor<gfx::FontList> font_list([]() {
+ return gfx::Font(new gfx::PlatformFontMac(
+ gfx::PlatformFontMac::SystemFontType::kToolTip));
+ }());
return *font_list;
}
diff --git a/chromium/ui/views/controls/base_control_test_widget.cc b/chromium/ui/views/controls/base_control_test_widget.cc
new file mode 100644
index 00000000000..edad02693bd
--- /dev/null
+++ b/chromium/ui/views/controls/base_control_test_widget.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/base_control_test_widget.h"
+
+#include <memory>
+#include <utility>
+
+namespace views {
+
+namespace test {
+
+BaseControlTestWidget::BaseControlTestWidget() = default;
+BaseControlTestWidget::~BaseControlTestWidget() = default;
+
+void BaseControlTestWidget::SetUp() {
+ ViewsTestBase::SetUp();
+
+ widget_ = std::make_unique<Widget>();
+ Widget::InitParams params =
+ CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.bounds = gfx::Rect(200, 200);
+ widget_->Init(std::move(params));
+ auto* container = widget_->SetContentsView(std::make_unique<View>());
+
+ CreateWidgetContent(container);
+
+ widget_->Show();
+}
+
+void BaseControlTestWidget::TearDown() {
+ widget_.reset();
+ ViewsTestBase::TearDown();
+}
+
+void BaseControlTestWidget::CreateWidgetContent(View* container) {}
+
+} // namespace test
+} // namespace views
diff --git a/chromium/ui/views/controls/base_control_test_widget.h b/chromium/ui/views/controls/base_control_test_widget.h
new file mode 100644
index 00000000000..4a8686ca7e2
--- /dev/null
+++ b/chromium/ui/views/controls/base_control_test_widget.h
@@ -0,0 +1,41 @@
+// 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 UI_VIEWS_CONTROLS_BASE_CONTROL_TEST_WIDGET_H_
+#define UI_VIEWS_CONTROLS_BASE_CONTROL_TEST_WIDGET_H_
+
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/unique_widget_ptr.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_utils.h"
+
+namespace views {
+
+namespace test {
+
+class BaseControlTestWidget : public ViewsTestBase {
+ public:
+ BaseControlTestWidget();
+ BaseControlTestWidget(const BaseControlTestWidget&) = delete;
+ BaseControlTestWidget& operator=(const BaseControlTestWidget&) = delete;
+ ~BaseControlTestWidget() override;
+
+ // ViewsTestBase:
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ virtual void CreateWidgetContent(View* container);
+
+ Widget* widget() { return widget_.get(); }
+
+ private:
+ UniqueWidgetPtr widget_;
+};
+
+} // namespace test
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_BASE_CONTROL_TEST_WIDGET_H_
diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc
index 5bb670ce30a..f930117e231 100644
--- a/chromium/ui/views/controls/button/button.cc
+++ b/chromium/ui/views/controls/button/button.cc
@@ -20,7 +20,6 @@
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/controls/button/button_controller.h"
#include "ui/views/controls/button/button_controller_delegate.h"
-#include "ui/views/controls/button/button_observer.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/label_button.h"
@@ -30,7 +29,6 @@
#include "ui/views/controls/focus_ring.h"
#include "ui/views/painter.h"
#include "ui/views/style/platform_style.h"
-#include "ui/views/widget/widget.h"
#if defined(USE_AURA)
#include "ui/aura/client/capture_client.h"
@@ -45,33 +43,6 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsButtonProperty, false)
} // namespace
-////////////////////////////////////////////////////////////////////////////////
-// WidgetObserverButtonBridge:
-Button::WidgetObserverButtonBridge::WidgetObserverButtonBridge(Button* button)
- : owner_(button) {
- DCHECK(button->GetWidget());
- button->GetWidget()->AddObserver(this);
-}
-
-Button::WidgetObserverButtonBridge::~WidgetObserverButtonBridge() {
- if (owner_)
- owner_->GetWidget()->RemoveObserver(this);
- CHECK(!IsInObserverList());
-}
-
-void Button::WidgetObserverButtonBridge::OnWidgetPaintAsActiveChanged(
- Widget* widget,
- bool paint_as_active) {
- owner_->WidgetPaintAsActiveChanged(widget, paint_as_active);
-}
-
-void Button::WidgetObserverButtonBridge::OnWidgetDestroying(Widget* widget) {
- widget->RemoveObserver(this);
- owner_ = nullptr;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ButtonControllerDelegate:
Button::DefaultButtonControllerDelegate::DefaultButtonControllerDelegate(
Button* button)
: ButtonControllerDelegate(button) {}
@@ -120,8 +91,6 @@ bool Button::DefaultButtonControllerDelegate::InDrag() {
return button()->InDrag();
}
-////////////////////////////////////////////////////////////////////////////////
-
// static
constexpr Button::ButtonState Button::kButtonStates[STATE_COUNT];
@@ -154,13 +123,10 @@ Button::ButtonState Button::GetButtonStateFrom(ui::NativeTheme::State state) {
return Button::STATE_NORMAL;
}
-////////////////////////////////////////////////////////////////////////////////
-// Button, public:
-
Button::~Button() = default;
void Button::SetFocusForPlatform() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, buttons are focusable only in full keyboard access mode.
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
#else
@@ -174,6 +140,7 @@ void Button::SetTooltipText(const base::string16& tooltip_text) {
tooltip_text_ = tooltip_text;
OnSetTooltipText(tooltip_text);
TooltipTextChanged();
+ NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
}
void Button::SetAccessibleName(const base::string16& name) {
@@ -185,6 +152,10 @@ const base::string16& Button::GetAccessibleName() const {
return accessible_name_.empty() ? tooltip_text_ : accessible_name_;
}
+Button::ButtonState Button::GetState() const {
+ return state_;
+}
+
void Button::SetState(ButtonState state) {
if (state == state_)
return;
@@ -211,15 +182,7 @@ void Button::SetState(ButtonState state) {
ButtonState old_state = state_;
state_ = state;
StateChanged(old_state);
- SchedulePaint();
-}
-
-Button::ButtonState Button::GetVisualState() const {
- if (PlatformStyle::kInactiveWidgetControlsAppearDisabled && GetWidget() &&
- !GetWidget()->ShouldPaintAsActive()) {
- return STATE_DISABLED;
- }
- return state();
+ OnPropertyChanged(&state_, kPropertyEffectsPaint);
}
void Button::StartThrobbing(int cycles_til_stop) {
@@ -275,22 +238,21 @@ void Button::SetHighlighted(bool bubble_visible) {
AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED
: views::InkDropState::DEACTIVATED,
nullptr);
- for (ButtonObserver& observer : button_observers_)
- observer.OnHighlightChanged(this, bubble_visible);
}
-void Button::AddButtonObserver(ButtonObserver* observer) {
- button_observers_.AddObserver(observer);
-}
-
-void Button::RemoveButtonObserver(ButtonObserver* observer) {
- button_observers_.RemoveObserver(observer);
+PropertyChangedSubscription Button::AddStateChangedCallback(
+ PropertyChangedCallback callback) {
+ return AddPropertyChangedCallback(&state_, std::move(callback));
}
Button::KeyClickAction Button::GetKeyClickActionForEvent(
const ui::KeyEvent& event) {
if (event.key_code() == ui::VKEY_SPACE)
return PlatformStyle::kKeyClickActionOnSpace;
+ // Note that default buttons also have VKEY_RETURN installed as an accelerator
+ // in LabelButton::SetIsDefault(). On platforms where
+ // PlatformStyle::kReturnClicksFocusedControl, the logic here will take
+ // precedence over that.
if (event.key_code() == ui::VKEY_RETURN &&
PlatformStyle::kReturnClicksFocusedControl)
return KeyClickAction::kOnKeyPress;
@@ -328,9 +290,6 @@ gfx::Point Button::GetMenuPosition() const {
return menu_position;
}
-////////////////////////////////////////////////////////////////////////////////
-// Button, View overrides:
-
bool Button::OnMousePressed(const ui::MouseEvent& event) {
return button_controller_->OnMousePressed(event);
}
@@ -507,15 +466,6 @@ void Button::OnBlur() {
SchedulePaint();
}
-void Button::AddedToWidget() {
- if (PlatformStyle::kInactiveWidgetControlsAppearDisabled)
- widget_observer_ = std::make_unique<WidgetObserverButtonBridge>(this);
-}
-
-void Button::RemovedFromWidget() {
- widget_observer_.reset();
-}
-
std::unique_ptr<InkDrop> Button::CreateInkDrop() {
std::unique_ptr<InkDrop> ink_drop = InkDropHostView::CreateInkDrop();
ink_drop->SetShowHighlightOnFocus(!focus_ring_);
@@ -526,16 +476,10 @@ SkColor Button::GetInkDropBaseColor() const {
return ink_drop_base_color_;
}
-////////////////////////////////////////////////////////////////////////////////
-// Button, gfx::AnimationDelegate implementation:
-
void Button::AnimationProgressed(const gfx::Animation* animation) {
SchedulePaint();
}
-////////////////////////////////////////////////////////////////////////////////
-// Button, protected:
-
Button::Button(ButtonListener* listener)
: AnimationDelegateViews(this),
listener_(listener),
@@ -579,11 +523,7 @@ void Button::OnClickCanceled(const ui::Event& event) {
void Button::OnSetTooltipText(const base::string16& tooltip_text) {}
-void Button::StateChanged(ButtonState old_state) {
- button_controller_->OnStateChanged(old_state);
- for (ButtonObserver& observer : button_observers_)
- observer.OnStateChanged(this, old_state);
-}
+void Button::StateChanged(ButtonState old_state) {}
bool Button::IsTriggerableEvent(const ui::Event& event) {
return button_controller_->IsTriggerableEvent(event);
@@ -637,12 +577,16 @@ void Button::OnEnabledChanged() {
}
}
-void Button::WidgetPaintAsActiveChanged(Widget* widget, bool paint_as_active) {
- StateChanged(state());
-}
+DEFINE_ENUM_CONVERTERS(
+ Button::ButtonState,
+ {Button::STATE_NORMAL, base::ASCIIToUTF16("STATE_NORMAL")},
+ {Button::STATE_HOVERED, base::ASCIIToUTF16("STATE_HOVERED")},
+ {Button::STATE_PRESSED, base::ASCIIToUTF16("STATE_PRESSED")},
+ {Button::STATE_DISABLED, base::ASCIIToUTF16("STATE_DISABLED")})
BEGIN_METADATA(Button)
METADATA_PARENT_CLASS(InkDropHostView)
+ADD_PROPERTY_METADATA(Button, ButtonState, State)
END_METADATA()
} // namespace views
diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h
index 6411eb3514b..1095469f066 100644
--- a/chromium/ui/views/controls/button/button.h
+++ b/chromium/ui/views/controls/button/button.h
@@ -19,7 +19,6 @@
#include "ui/views/controls/button/button_controller_delegate.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/painter.h"
-#include "ui/views/widget/widget_observer.h"
namespace views {
namespace test {
@@ -28,7 +27,6 @@ class ButtonTestApi;
class Button;
class ButtonController;
-class ButtonObserver;
class Event;
// An interface implemented by an object to let it know that a button was
@@ -107,19 +105,18 @@ class VIEWS_EXPORT Button : public InkDropHostView,
int tag() const { return tag_; }
void set_tag(int tag) { tag_ = tag; }
+ void set_listener(ButtonListener* listener) { listener_ = listener; }
+
void SetAccessibleName(const base::string16& name);
const base::string16& GetAccessibleName() const;
// Get/sets the current display state of the button.
- ButtonState state() const { return state_; }
+ ButtonState GetState() const;
// Clients passing in STATE_DISABLED should consider calling
// SetEnabled(false) instead because the enabled flag can affect other things
// like event dispatching, focus traversals, etc. Calling SetEnabled(false)
// will also set the state of |this| to STATE_DISABLED.
void SetState(ButtonState state);
- // Returns the visual appearance state of the button. This takes into account
- // both the button's display state and the state of the containing widget.
- ButtonState GetVisualState() const;
// Starts throbbing. See HoverAnimation for a description of cycles_til_stop.
// This method does nothing if |animate_on_state_change_| is false.
@@ -141,7 +138,7 @@ class VIEWS_EXPORT Button : public InkDropHostView,
void set_request_focus_on_press(bool value) {
// On Mac, buttons should not request focus on a mouse press. Hence keep the
// default value i.e. false.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
request_focus_on_press_ = value;
#endif
}
@@ -180,8 +177,8 @@ class VIEWS_EXPORT Button : public InkDropHostView,
// Highlights the ink drop for the button.
void SetHighlighted(bool bubble_visible);
- void AddButtonObserver(ButtonObserver* observer);
- void RemoveButtonObserver(ButtonObserver* observer);
+ PropertyChangedSubscription AddStateChangedCallback(
+ PropertyChangedCallback callback);
// Overridden from View:
bool OnMousePressed(const ui::MouseEvent& event) override;
@@ -209,8 +206,6 @@ class VIEWS_EXPORT Button : public InkDropHostView,
const ViewHierarchyChangedDetails& details) override;
void OnFocus() override;
void OnBlur() override;
- void AddedToWidget() override;
- void RemovedFromWidget() override;
// Overridden from InkDropHostView:
std::unique_ptr<InkDrop> CreateInkDrop() override;
@@ -237,7 +232,7 @@ class VIEWS_EXPORT Button : public InkDropHostView,
// Construct the Button with a Listener. The listener can be null. This can be
// true of buttons that don't have a listener - e.g. menubuttons where there's
// no default action and checkboxes.
- explicit Button(ButtonListener* listener);
+ explicit Button(ButtonListener* listener = nullptr);
// Called when the button has been clicked or tapped and should request focus
// if necessary.
@@ -301,31 +296,8 @@ class VIEWS_EXPORT Button : public InkDropHostView,
friend class test::ButtonTestApi;
FRIEND_TEST_ALL_PREFIXES(BlueButtonTest, Border);
- // Bridge class to allow Button to observe a Widget without being a
- // WidgetObserver. This is desirable because many Button subclasses are
- // themselves WidgetObservers, and if Button is a WidgetObserver, any change
- // to its WidgetObserver overrides requires updating all the subclasses as
- // well.
- class WidgetObserverButtonBridge : public WidgetObserver {
- public:
- explicit WidgetObserverButtonBridge(Button* owner);
- ~WidgetObserverButtonBridge() override;
-
- // WidgetObserver:
- void OnWidgetPaintAsActiveChanged(Widget* widget,
- bool paint_as_active) override;
- void OnWidgetDestroying(Widget* widget) override;
-
- private:
- Button* owner_;
-
- DISALLOW_COPY_AND_ASSIGN(WidgetObserverButtonBridge);
- };
-
void OnEnabledChanged();
- void WidgetPaintAsActiveChanged(Widget* widget, bool active);
-
// The text shown in a tooltip.
base::string16 tooltip_text_;
@@ -372,8 +344,6 @@ class VIEWS_EXPORT Button : public InkDropHostView,
std::unique_ptr<Painter> focus_painter_;
- std::unique_ptr<WidgetObserverButtonBridge> widget_observer_;
-
// ButtonController is responsible for handling events sent to the Button and
// related state changes from the events.
// TODO(cyan): Make sure all state changes are handled within
@@ -384,8 +354,6 @@ class VIEWS_EXPORT Button : public InkDropHostView,
AddEnabledChangedCallback(base::BindRepeating(&Button::OnEnabledChanged,
base::Unretained(this)))};
- base::ObserverList<ButtonObserver> button_observers_;
-
DISALLOW_COPY_AND_ASSIGN(Button);
};
diff --git a/chromium/ui/views/controls/button/button_controller.cc b/chromium/ui/views/controls/button/button_controller.cc
index f639cf93c55..4e90ed44342 100644
--- a/chromium/ui/views/controls/button/button_controller.cc
+++ b/chromium/ui/views/controls/button/button_controller.cc
@@ -21,9 +21,9 @@ ButtonController::ButtonController(
ButtonController::~ButtonController() = default;
bool ButtonController::OnMousePressed(const ui::MouseEvent& event) {
- if (button_->state() == Button::STATE_DISABLED)
+ if (button_->GetState() == Button::STATE_DISABLED)
return true;
- if (button_->state() != Button::STATE_PRESSED &&
+ if (button_->GetState() != Button::STATE_PRESSED &&
button_controller_delegate_->ShouldEnterPushedState(event) &&
button_->HitTestPoint(event.location())) {
button_->SetState(Button::STATE_PRESSED);
@@ -40,7 +40,7 @@ bool ButtonController::OnMousePressed(const ui::MouseEvent& event) {
}
void ButtonController::OnMouseReleased(const ui::MouseEvent& event) {
- if (button_->state() != Button::STATE_DISABLED) {
+ if (button_->GetState() != Button::STATE_DISABLED) {
if (!button_->HitTestPoint(event.location())) {
button_->SetState(Button::STATE_NORMAL);
} else {
@@ -59,7 +59,7 @@ void ButtonController::OnMouseReleased(const ui::MouseEvent& event) {
}
void ButtonController::OnMouseMoved(const ui::MouseEvent& event) {
- if (button_->state() != Button::STATE_DISABLED) {
+ if (button_->GetState() != Button::STATE_DISABLED) {
button_->SetState(button_->HitTestPoint(event.location())
? Button::STATE_HOVERED
: Button::STATE_NORMAL);
@@ -67,19 +67,19 @@ void ButtonController::OnMouseMoved(const ui::MouseEvent& event) {
}
void ButtonController::OnMouseEntered(const ui::MouseEvent& event) {
- if (button_->state() != Button::STATE_DISABLED)
+ if (button_->GetState() != Button::STATE_DISABLED)
button_->SetState(Button::STATE_HOVERED);
}
void ButtonController::OnMouseExited(const ui::MouseEvent& event) {
// Starting a drag results in a MouseExited, we need to ignore it.
- if (button_->state() != Button::STATE_DISABLED &&
+ if (button_->GetState() != Button::STATE_DISABLED &&
!button_controller_delegate_->InDrag())
button_->SetState(Button::STATE_NORMAL);
}
bool ButtonController::OnKeyPressed(const ui::KeyEvent& event) {
- if (button_->state() == Button::STATE_DISABLED)
+ if (button_->GetState() == Button::STATE_DISABLED)
return false;
switch (button_->GetKeyClickActionForEvent(event)) {
@@ -104,7 +104,7 @@ bool ButtonController::OnKeyPressed(const ui::KeyEvent& event) {
}
bool ButtonController::OnKeyReleased(const ui::KeyEvent& event) {
- const bool click_button = button_->state() == Button::STATE_PRESSED &&
+ const bool click_button = button_->GetState() == Button::STATE_PRESSED &&
button_->GetKeyClickActionForEvent(event) ==
Button::KeyClickAction::kOnKeyRelease;
if (!click_button)
@@ -116,7 +116,7 @@ bool ButtonController::OnKeyReleased(const ui::KeyEvent& event) {
}
void ButtonController::OnGestureEvent(ui::GestureEvent* event) {
- if (button_->state() == Button::STATE_DISABLED)
+ if (button_->GetState() == Button::STATE_DISABLED)
return;
if (event->type() == ui::ET_GESTURE_TAP &&
@@ -139,8 +139,6 @@ void ButtonController::OnGestureEvent(ui::GestureEvent* event) {
void ButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) {}
-void ButtonController::OnStateChanged(Button::ButtonState old_state) {}
-
bool ButtonController::IsTriggerableEvent(const ui::Event& event) {
return event.type() == ui::ET_GESTURE_TAP_DOWN ||
event.type() == ui::ET_GESTURE_TAP ||
diff --git a/chromium/ui/views/controls/button/button_controller.h b/chromium/ui/views/controls/button/button_controller.h
index db509f674e2..042bb513ea6 100644
--- a/chromium/ui/views/controls/button/button_controller.h
+++ b/chromium/ui/views/controls/button/button_controller.h
@@ -50,7 +50,6 @@ class VIEWS_EXPORT ButtonController {
virtual void UpdateAccessibleNodeData(ui::AXNodeData* node_data);
// Methods that parallel respective methods in Button:
- virtual void OnStateChanged(Button::ButtonState old_state);
virtual bool IsTriggerableEvent(const ui::Event& event);
protected:
diff --git a/chromium/ui/views/controls/button/button_observer.h b/chromium/ui/views/controls/button/button_observer.h
deleted file mode 100644
index 56efa5d3cff..00000000000
--- a/chromium/ui/views/controls/button/button_observer.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_BUTTON_BUTTON_OBSERVER_H_
-#define UI_VIEWS_CONTROLS_BUTTON_BUTTON_OBSERVER_H_
-
-#include "base/observer_list_types.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/views_export.h"
-
-namespace views {
-
-class VIEWS_EXPORT ButtonObserver : public base::CheckedObserver {
- public:
- virtual void OnHighlightChanged(views::Button* observed_button,
- bool highlighted) {}
-
- virtual void OnStateChanged(views::Button* observed_button,
- views::Button::ButtonState old_state) {}
-
- protected:
- ~ButtonObserver() override = default;
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_BUTTON_BUTTON_OBSERVER_H_
diff --git a/chromium/ui/views/controls/button/button_unittest.cc b/chromium/ui/views/controls/button/button_unittest.cc
index 5850b215f25..d57aa9a2d27 100644
--- a/chromium/ui/views/controls/button/button_unittest.cc
+++ b/chromium/ui/views/controls/button/button_unittest.cc
@@ -5,6 +5,7 @@
#include "ui/views/controls/button/button.h"
#include <memory>
+#include <string>
#include <utility>
#include "base/bind.h"
@@ -14,6 +15,8 @@
#include "base/run_loop.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/base/layout.h"
#include "ui/display/screen.h"
#include "ui/events/event_utils.h"
@@ -25,7 +28,6 @@
#include "ui/views/animation/test/test_ink_drop_host.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/button/button_controller.h"
-#include "ui/views/controls/button/button_observer.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/label_button.h"
@@ -35,6 +37,7 @@
#include "ui/views/controls/link.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/style/platform_style.h"
+#include "ui/views/test/test_ax_event_observer.h"
#include "ui/views/test/view_metadata_test_utils.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget_utils.h"
@@ -104,6 +107,7 @@ class TestButton : public Button, public ButtonListener {
bool canceled() { return canceled_; }
int ink_drop_layer_add_count() { return ink_drop_layer_add_count_; }
int ink_drop_layer_remove_count() { return ink_drop_layer_remove_count_; }
+ ButtonListener* listener() const { return listener_; }
void set_custom_key_click_action(KeyClickAction custom_key_click_action) {
custom_key_click_action_ = custom_key_click_action;
@@ -136,45 +140,57 @@ class TestButton : public Button, public ButtonListener {
DISALLOW_COPY_AND_ASSIGN(TestButton);
};
-class TestButtonObserver : public ButtonObserver {
+class TestButtonObserver {
public:
- TestButtonObserver() = default;
- ~TestButtonObserver() override = default;
-
- void OnHighlightChanged(views::Button* observed_button,
- bool highlighted) override {
- observed_button_ = observed_button;
- highlighted_ = highlighted;
- }
-
- void OnStateChanged(views::Button* observed_button,
- views::Button::ButtonState old_state) override {
- observed_button_ = observed_button;
- state_changed_ = true;
+ explicit TestButtonObserver(Button* button) {
+ highlighted_changed_subscription_ =
+ button->AddHighlightedChangedCallback(base::BindRepeating(
+ [](TestButtonObserver* obs) { obs->highlighted_changed_ = true; },
+ base::Unretained(this)));
+ state_changed_subscription_ =
+ button->AddStateChangedCallback(base::BindRepeating(
+ [](TestButtonObserver* obs) { obs->state_changed_ = true; },
+ base::Unretained(this)));
}
+ ~TestButtonObserver() = default;
void Reset() {
- observed_button_ = nullptr;
- highlighted_ = false;
+ highlighted_changed_ = false;
state_changed_ = false;
}
- views::Button* observed_button() { return observed_button_; }
- bool highlighted() const { return highlighted_; }
+ bool highlighted_changed() const { return highlighted_changed_; }
bool state_changed() const { return state_changed_; }
private:
- views::Button* observed_button_ = nullptr;
- bool highlighted_ = false;
+ bool highlighted_changed_ = false;
bool state_changed_ = false;
- private:
+ PropertyChangedSubscription highlighted_changed_subscription_;
+ PropertyChangedSubscription state_changed_subscription_;
+
DISALLOW_COPY_AND_ASSIGN(TestButtonObserver);
};
+class TestButtonListener : public ButtonListener {
+ public:
+ void ButtonPressed(Button* sender, const ui::Event& event) override {
+ pressed_ = true;
+ sender_ = sender;
+ }
+
+ bool pressed() const { return pressed_; }
+ Button* sender() const { return sender_; }
+
+ private:
+ bool pressed_ = false;
+ Button* sender_ = nullptr;
+};
+
TestInkDrop* AddTestInkDrop(TestButton* button) {
auto owned_ink_drop = std::make_unique<TestInkDrop>();
TestInkDrop* ink_drop = owned_ink_drop.get();
+ button->SetInkDropMode(InkDropHostView::InkDropMode::ON);
InkDropHostViewTestApi(button).SetInkDrop(std::move(owned_ink_drop));
return ink_drop;
}
@@ -207,10 +223,6 @@ class ButtonTest : public ViewsTestBase {
}
void TearDown() override {
- if (button_observer_)
- button_->RemoveButtonObserver(button_observer_.get());
-
- button_observer_.reset();
widget_.reset();
ViewsTestBase::TearDown();
@@ -222,16 +234,10 @@ class ButtonTest : public ViewsTestBase {
return AddTestInkDrop(button_);
}
- void CreateButtonWithRealInkDrop() {
- button_ = widget()->SetContentsView(std::make_unique<TestButton>(false));
- InkDropHostViewTestApi(button_).SetInkDrop(
- std::make_unique<InkDropImpl>(button_, button_->size()));
- }
-
void CreateButtonWithObserver() {
button_ = widget()->SetContentsView(std::make_unique<TestButton>(false));
- button_observer_ = std::make_unique<TestButtonObserver>();
- button_->AddButtonObserver(button_observer_.get());
+ button_->SetInkDropMode(InkDropHostView::InkDropMode::ON);
+ button_observer_ = std::make_unique<TestButtonObserver>(button_);
}
protected:
@@ -248,7 +254,6 @@ class ButtonTest : public ViewsTestBase {
TestButton* button_;
std::unique_ptr<TestButtonObserver> button_observer_;
std::unique_ptr<ui::test::EventGenerator> event_generator_;
-
DISALLOW_COPY_AND_ASSIGN(ButtonTest);
};
@@ -261,22 +266,22 @@ TEST_F(ButtonTest, MetadataTest) {
TEST_F(ButtonTest, HoverStateOnVisibilityChange) {
event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
event_generator()->PressLeftButton();
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
event_generator()->ReleaseLeftButton();
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
button()->SetEnabled(false);
- EXPECT_EQ(Button::STATE_DISABLED, button()->state());
+ EXPECT_EQ(Button::STATE_DISABLED, button()->GetState());
button()->SetEnabled(true);
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
button()->SetVisible(false);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
button()->SetVisible(true);
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
#if defined(USE_AURA)
{
@@ -291,22 +296,22 @@ TEST_F(ButtonTest, HoverStateOnVisibilityChange) {
second_widget.GetNativeWindow()->SetCapture();
button()->SetEnabled(false);
- EXPECT_EQ(Button::STATE_DISABLED, button()->state());
+ EXPECT_EQ(Button::STATE_DISABLED, button()->GetState());
button()->SetEnabled(true);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
button()->SetVisible(false);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
button()->SetVisible(true);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
}
#endif
// Disabling cursor events occurs for touch events and the Ash magnifier. There
// is no touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
aura::test::TestCursorClient cursor_client(GetRootWindow(widget()));
// In Aura views, no new hover effects are invoked if mouse events
@@ -314,17 +319,17 @@ TEST_F(ButtonTest, HoverStateOnVisibilityChange) {
cursor_client.DisableMouseEvents();
button()->SetEnabled(false);
- EXPECT_EQ(Button::STATE_DISABLED, button()->state());
+ EXPECT_EQ(Button::STATE_DISABLED, button()->GetState());
button()->SetEnabled(true);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
button()->SetVisible(false);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
button()->SetVisible(true);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
}
// Tests that the hover state is preserved during a view hierarchy update of a
@@ -332,11 +337,11 @@ TEST_F(ButtonTest, HoverStateOnVisibilityChange) {
TEST_F(ButtonTest, HoverStatePreservedOnDescendantViewHierarchyChange) {
event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
Label* child = new Label(base::string16());
button()->AddChildView(child);
delete child;
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
}
// Tests the different types of NotifyActions.
@@ -347,13 +352,13 @@ TEST_F(ButtonTest, NotifyAction) {
button()->OnMousePressed(ui::MouseEvent(
ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
EXPECT_FALSE(button()->pressed());
button()->OnMouseReleased(ui::MouseEvent(
ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
EXPECT_TRUE(button()->pressed());
// Set the notify action to its listener on mouse press.
@@ -363,7 +368,7 @@ TEST_F(ButtonTest, NotifyAction) {
button()->OnMousePressed(ui::MouseEvent(
ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
EXPECT_TRUE(button()->pressed());
// The button should no longer notify on mouse release.
@@ -371,7 +376,7 @@ TEST_F(ButtonTest, NotifyAction) {
button()->OnMouseReleased(ui::MouseEvent(
ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
EXPECT_FALSE(button()->pressed());
}
@@ -410,7 +415,7 @@ TEST_F(ButtonTest, NotifyActionNoClick) {
}
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
namespace {
@@ -426,16 +431,16 @@ void PerformGesture(Button* button, ui::EventType event_type) {
TEST_F(ButtonTest, GestureEventsSetState) {
aura::test::TestCursorClient cursor_client(GetRootWindow(widget()));
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
PerformGesture(button(), ui::ET_GESTURE_TAP_DOWN);
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
PerformGesture(button(), ui::ET_GESTURE_SHOW_PRESS);
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
PerformGesture(button(), ui::ET_GESTURE_TAP_CANCEL);
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
}
// Tests that if the button was disabled in its button press handler, gesture
@@ -445,12 +450,12 @@ TEST_F(ButtonTest, GestureEventsRespectDisabledState) {
button()->set_on_button_pressed_handler(base::BindRepeating(
[](TestButton* button) { button->SetEnabled(false); }, button()));
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
event_generator()->GestureTapAt(button()->GetBoundsInScreen().CenterPoint());
- EXPECT_EQ(Button::STATE_DISABLED, button()->state());
+ EXPECT_EQ(Button::STATE_DISABLED, button()->GetState());
}
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
// Ensure subclasses of Button are correctly recognized as Button.
TEST_F(ButtonTest, AsButton) {
@@ -459,7 +464,7 @@ TEST_F(ButtonTest, AsButton) {
LabelButton label_button(nullptr, text);
EXPECT_TRUE(Button::AsButton(&label_button));
- ImageButton image_button(nullptr);
+ ImageButton image_button;
EXPECT_TRUE(Button::AsButton(&image_button));
Checkbox checkbox(text);
@@ -468,16 +473,16 @@ TEST_F(ButtonTest, AsButton) {
RadioButton radio_button(text, 0);
EXPECT_TRUE(Button::AsButton(&radio_button));
- MenuButton menu_button(text, nullptr);
+ MenuButton menu_button(nullptr, text);
EXPECT_TRUE(Button::AsButton(&menu_button));
- ToggleButton toggle_button(nullptr);
+ ToggleButton toggle_button;
EXPECT_TRUE(Button::AsButton(&toggle_button));
Label label;
EXPECT_FALSE(Button::AsButton(&label));
- Link link(text);
+ Link link;
EXPECT_FALSE(Button::AsButton(&link));
Textfield textfield;
@@ -508,13 +513,13 @@ TEST_F(ButtonTest, CaptureLossHidesInkDrop) {
event_generator()->PressLeftButton();
EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState());
- EXPECT_EQ(Button::ButtonState::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::ButtonState::STATE_PRESSED, button()->GetState());
SetDraggedView(button());
widget()->SetCapture(button());
widget()->ReleaseCapture();
SetDraggedView(nullptr);
EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState());
- EXPECT_EQ(Button::ButtonState::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::ButtonState::STATE_NORMAL, button()->GetState());
}
TEST_F(ButtonTest, HideInkDropWhenShowingContextMenu) {
@@ -748,6 +753,28 @@ TEST_F(ButtonTest, InkDropStaysHiddenWhileDragging) {
SetDraggedView(nullptr);
}
+// Ensure ButtonListener is dynamically settable.
+TEST_F(ButtonTest, SetListener) {
+ gfx::Point center(10, 10);
+ ButtonListener* old_listener = button()->listener();
+ auto listener = std::make_unique<TestButtonListener>();
+
+ button()->set_listener(listener.get());
+ EXPECT_EQ(listener.get(), button()->listener());
+
+ button()->OnMousePressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ // Default button controller notifies listener at mouse release.
+ button()->OnMouseReleased(ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ EXPECT_TRUE(listener->pressed());
+ EXPECT_EQ(button(), listener->sender());
+
+ button()->set_listener(old_listener);
+}
+
// VisibilityTestButton tests to see if an ink drop or a layer has been added to
// the button at any point during the visibility state changes of its Widget.
class VisibilityTestButton : public TestButton {
@@ -806,23 +833,23 @@ TEST_F(ButtonTest, ActionOnSpace) {
ui::KeyEvent space_press(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE);
EXPECT_TRUE(button()->OnKeyPressed(space_press));
-#if defined(OS_MACOSX)
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+#if defined(OS_APPLE)
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_TRUE(button()->pressed());
#else
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
EXPECT_FALSE(button()->pressed());
#endif
ui::KeyEvent space_release(ui::ET_KEY_RELEASED, ui::VKEY_SPACE, ui::EF_NONE);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_FALSE(button()->OnKeyReleased(space_release));
#else
EXPECT_TRUE(button()->OnKeyReleased(space_release));
#endif
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_TRUE(button()->pressed());
}
@@ -837,13 +864,13 @@ TEST_F(ButtonTest, ActionOnReturn) {
ui::KeyEvent return_press(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_FALSE(button()->OnKeyPressed(return_press));
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_FALSE(button()->pressed());
#else
EXPECT_TRUE(button()->OnKeyPressed(return_press));
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_TRUE(button()->pressed());
#endif
@@ -864,7 +891,7 @@ TEST_F(ButtonTest, CustomActionOnKeyPressedEvent) {
ui::KeyEvent control_press(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, ui::EF_NONE);
EXPECT_TRUE(button()->OnKeyPressed(control_press));
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_TRUE(button()->pressed());
ui::KeyEvent control_release(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL,
@@ -872,57 +899,79 @@ TEST_F(ButtonTest, CustomActionOnKeyPressedEvent) {
EXPECT_FALSE(button()->OnKeyReleased(control_release));
}
-// Verifies that ButtonObserver is notified when the button activition highlight
-// state is changed. Also verifies the |observed_button| and |highlighted|
-// passed to observer are correct.
+// Verifies that button activation highlight state changes trigger property
+// change callbacks.
TEST_F(ButtonTest, ChangingHighlightStateNotifiesListener) {
CreateButtonWithObserver();
- EXPECT_FALSE(button_observer()->highlighted());
+ EXPECT_FALSE(button_observer()->highlighted_changed());
+ EXPECT_FALSE(button()->GetHighlighted());
button()->SetHighlighted(/*bubble_visible=*/true);
- EXPECT_EQ(button_observer()->observed_button(), button());
- EXPECT_TRUE(button_observer()->highlighted());
+ EXPECT_TRUE(button_observer()->highlighted_changed());
+ EXPECT_TRUE(button()->GetHighlighted());
+
+ button_observer()->Reset();
+ EXPECT_FALSE(button_observer()->highlighted_changed());
+ EXPECT_TRUE(button()->GetHighlighted());
button()->SetHighlighted(/*bubble_visible=*/false);
- EXPECT_EQ(button_observer()->observed_button(), button());
- EXPECT_FALSE(button_observer()->highlighted());
+ EXPECT_TRUE(button_observer()->highlighted_changed());
+ EXPECT_FALSE(button()->GetHighlighted());
}
-// Verifies that ButtonObserver is notified when the button state is changed,
-// and that the |observed_button| is passed to observer correctly.
+// Verifies that button state changes trigger property change callbacks.
TEST_F(ButtonTest, ClickingButtonNotifiesObserverOfStateChanges) {
CreateButtonWithObserver();
+ EXPECT_FALSE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
event_generator()->MoveMouseTo(button()->GetBoundsInScreen().CenterPoint());
event_generator()->PressLeftButton();
- EXPECT_EQ(button_observer()->observed_button(), button());
EXPECT_TRUE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
button_observer()->Reset();
- EXPECT_EQ(button_observer()->observed_button(), nullptr);
EXPECT_FALSE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
event_generator()->ReleaseLeftButton();
- EXPECT_EQ(button_observer()->observed_button(), button());
EXPECT_TRUE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
}
-// Verifies the ButtonObserver is notified whenever Button::SetState() is
-// called directly.
+// Verifies that direct calls to Button::SetState() trigger property change
+// callbacks.
TEST_F(ButtonTest, SetStateNotifiesObserver) {
CreateButtonWithObserver();
+ EXPECT_FALSE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
- button()->SetState(Button::ButtonState::STATE_HOVERED);
- EXPECT_EQ(button_observer()->observed_button(), button());
+ button()->SetState(Button::STATE_HOVERED);
EXPECT_TRUE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
button_observer()->Reset();
- EXPECT_EQ(button_observer()->observed_button(), nullptr);
EXPECT_FALSE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
- button()->SetState(Button::ButtonState::STATE_NORMAL);
- EXPECT_EQ(button_observer()->observed_button(), button());
+ button()->SetState(Button::STATE_NORMAL);
EXPECT_TRUE(button_observer()->state_changed());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
+}
+
+// Verifies setting the tooltip text will call NotifyAccessibilityEvent.
+TEST_F(ButtonTest, SetTooltipTextNotifiesAccessibilityEvent) {
+ base::string16 test_tooltip_text = base::ASCIIToUTF16("Test Tooltip Text");
+ test::TestAXEventObserver observer;
+ EXPECT_EQ(0, observer.text_changed_event_count());
+ button()->SetTooltipText(test_tooltip_text);
+ EXPECT_EQ(1, observer.text_changed_event_count());
+ EXPECT_EQ(test_tooltip_text, button()->GetTooltipText(gfx::Point()));
+ ui::AXNodeData data;
+ button()->GetAccessibleNodeData(&data);
+ const std::string& name =
+ data.GetStringAttribute(ax::mojom::StringAttribute::kName);
+ EXPECT_EQ(test_tooltip_text, base::ASCIIToUTF16(name));
}
} // namespace views
diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc
index 06050cc2479..7281bfb9bf3 100644
--- a/chromium/ui/views/controls/button/checkbox.cc
+++ b/chromium/ui/views/controls/button/checkbox.cc
@@ -132,6 +132,24 @@ void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
}
}
+gfx::ImageSkia Checkbox::GetImage(ButtonState for_state) const {
+ int icon_state = 0;
+ if (GetChecked())
+ icon_state |= IconState::CHECKED;
+ if (for_state != STATE_DISABLED)
+ icon_state |= IconState::ENABLED;
+ return gfx::CreateVectorIcon(GetVectorIcon(), 16,
+ GetIconImageColor(icon_state));
+}
+
+std::unique_ptr<LabelButtonBorder> Checkbox::CreateDefaultBorder() const {
+ std::unique_ptr<LabelButtonBorder> border =
+ LabelButton::CreateDefaultBorder();
+ border->set_insets(
+ LayoutProvider::Get()->GetInsetsMetric(INSETS_CHECKBOX_RADIO_BUTTON));
+ return border;
+}
+
void Checkbox::OnThemeChanged() {
LabelButton::OnThemeChanged();
UpdateImage();
@@ -156,24 +174,6 @@ SkColor Checkbox::GetInkDropBaseColor() const {
return GetIconImageColor(IconState::ENABLED);
}
-gfx::ImageSkia Checkbox::GetImage(ButtonState for_state) const {
- int icon_state = 0;
- if (GetChecked())
- icon_state |= IconState::CHECKED;
- if (for_state != STATE_DISABLED)
- icon_state |= IconState::ENABLED;
- return gfx::CreateVectorIcon(GetVectorIcon(), 16,
- GetIconImageColor(icon_state));
-}
-
-std::unique_ptr<LabelButtonBorder> Checkbox::CreateDefaultBorder() const {
- std::unique_ptr<LabelButtonBorder> border =
- LabelButton::CreateDefaultBorder();
- border->set_insets(
- LayoutProvider::Get()->GetInsetsMetric(INSETS_CHECKBOX_RADIO_BUTTON));
- return border;
-}
-
SkPath Checkbox::GetFocusRingPath() const {
SkPath path;
gfx::Rect bounds = image()->GetMirroredBounds();
diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h
index 2b8e2a28372..44b7daa6e3a 100644
--- a/chromium/ui/views/controls/button/checkbox.h
+++ b/chromium/ui/views/controls/button/checkbox.h
@@ -28,7 +28,7 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
METADATA_HEADER(Checkbox);
// |force_md| forces MD even when --secondary-ui-md flag is not set.
- explicit Checkbox(const base::string16& label,
+ explicit Checkbox(const base::string16& label = base::string16(),
ButtonListener* listener = nullptr);
~Checkbox() override;
@@ -50,6 +50,8 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
// LabelButton:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+ gfx::ImageSkia GetImage(ButtonState for_state) const override;
+ std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const override;
protected:
// Bitmask constants for GetIconImageColor.
@@ -60,8 +62,6 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
std::unique_ptr<InkDrop> CreateInkDrop() override;
std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
SkColor GetInkDropBaseColor() const override;
- gfx::ImageSkia GetImage(ButtonState for_state) const override;
- std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const override;
// Returns the path to draw the focus ring around for this Checkbox.
virtual SkPath GetFocusRingPath() const;
diff --git a/chromium/ui/views/controls/button/checkbox_unittest.cc b/chromium/ui/views/controls/button/checkbox_unittest.cc
index a468b3d7212..bb554d2ec77 100644
--- a/chromium/ui/views/controls/button/checkbox_unittest.cc
+++ b/chromium/ui/views/controls/button/checkbox_unittest.cc
@@ -32,8 +32,7 @@ class CheckboxTest : public ViewsTestBase {
widget_->Init(std::move(params));
widget_->Show();
- checkbox_ =
- widget_->SetContentsView(std::make_unique<Checkbox>(base::string16()));
+ checkbox_ = widget_->SetContentsView(std::make_unique<Checkbox>());
}
void TearDown() override {
diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc
index e026cd2fd95..5e3087cd429 100644
--- a/chromium/ui/views/controls/button/image_button.cc
+++ b/chromium/ui/views/controls/button/image_button.cc
@@ -172,7 +172,7 @@ gfx::ImageSkia ImageButton::GetImageToPaint() {
images_[STATE_NORMAL], images_[STATE_HOVERED],
hover_animation().GetCurrentValue());
} else {
- img = images_[state()];
+ img = images_[GetState()];
}
return !img.isNull() ? img : images_[STATE_NORMAL];
@@ -233,7 +233,7 @@ void ToggleImageButton::SetToggledImage(ButtonState image_state,
const gfx::ImageSkia* image) {
if (toggled_) {
images_[image_state] = image ? *image : gfx::ImageSkia();
- if (state() == image_state)
+ if (GetState() == image_state)
SchedulePaint();
} else {
alternate_images_[image_state] = image ? *image : gfx::ImageSkia();
@@ -244,6 +244,10 @@ void ToggleImageButton::SetToggledTooltipText(const base::string16& tooltip) {
toggled_tooltip_text_ = tooltip;
}
+void ToggleImageButton::SetToggledAccessibleName(const base::string16& name) {
+ toggled_accessible_name_ = name;
+}
+
////////////////////////////////////////////////////////////////////////////////
// ToggleImageButton, ImageButton overrides:
@@ -260,7 +264,7 @@ void ToggleImageButton::SetImage(ButtonState image_state,
alternate_images_[image_state] = image;
} else {
images_[image_state] = image;
- if (state() == image_state)
+ if (GetState() == image_state)
SchedulePaint();
}
PreferredSizeChanged();
@@ -277,7 +281,13 @@ base::string16 ToggleImageButton::GetTooltipText(const gfx::Point& p) const {
void ToggleImageButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
ImageButton::GetAccessibleNodeData(node_data);
- node_data->SetName(GetTooltipText(gfx::Point()));
+ if (!toggled_)
+ return;
+
+ if (!toggled_accessible_name_.empty())
+ node_data->SetName(toggled_accessible_name_);
+ else if (!toggled_tooltip_text_.empty())
+ node_data->SetName(toggled_tooltip_text_);
// Use the visual pressed image as a cue for making this control into an
// accessible toggle button.
@@ -289,10 +299,6 @@ void ToggleImageButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
}
}
-bool ToggleImageButton::toggled_for_testing() const {
- return toggled_;
-}
-
DEFINE_ENUM_CONVERTERS(ImageButton::HorizontalAlignment,
{ImageButton::HorizontalAlignment::ALIGN_LEFT,
base::ASCIIToUTF16("ALIGN_LEFT")},
diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h
index 81ad539d661..0f19b6c84f6 100644
--- a/chromium/ui/views/controls/button/image_button.h
+++ b/chromium/ui/views/controls/button/image_button.h
@@ -29,7 +29,7 @@ class VIEWS_EXPORT ImageButton : public Button {
// An enum describing the vertical alignment of images on Buttons.
enum VerticalAlignment { ALIGN_TOP = 0, ALIGN_MIDDLE, ALIGN_BOTTOM };
- explicit ImageButton(ButtonListener* listener);
+ explicit ImageButton(ButtonListener* listener = nullptr);
~ImageButton() override;
// Returns the image for a given |state|.
@@ -122,6 +122,8 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton {
explicit ToggleImageButton(ButtonListener* listener);
~ToggleImageButton() override;
+ bool toggled() const { return toggled_; }
+
// Change the toggled state.
void SetToggled(bool toggled);
@@ -133,6 +135,9 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton {
// Set the tooltip text displayed when the button is toggled.
void SetToggledTooltipText(const base::string16& tooltip);
+ // Set the accessible text used when the button is toggled.
+ void SetToggledAccessibleName(const base::string16& name);
+
// Overridden from ImageButton:
const gfx::ImageSkia& GetImage(ButtonState state) const override;
void SetImage(ButtonState state, const gfx::ImageSkia& image) override;
@@ -141,8 +146,6 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton {
base::string16 GetTooltipText(const gfx::Point& p) const override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
- bool toggled_for_testing() const;
-
private:
// The parent class's images_ member is used for the current images,
// and this array is used to hold the alternative images.
@@ -156,6 +159,10 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton {
// this one is shown when toggled.
base::string16 toggled_tooltip_text_;
+ // The parent class's accessibility data is used when not toggled, and this
+ // one is used when toggled.
+ base::string16 toggled_accessible_name_;
+
DISALLOW_COPY_AND_ASSIGN(ToggleImageButton);
};
diff --git a/chromium/ui/views/controls/button/image_button_unittest.cc b/chromium/ui/views/controls/button/image_button_unittest.cc
index 9b372f638a6..e1872623d63 100644
--- a/chromium/ui/views/controls/button/image_button_unittest.cc
+++ b/chromium/ui/views/controls/button/image_button_unittest.cc
@@ -40,7 +40,7 @@ namespace views {
using ImageButtonTest = ViewsTestBase;
TEST_F(ImageButtonTest, Basics) {
- ImageButton button(nullptr);
+ ImageButton button;
// Our image to paint starts empty.
EXPECT_TRUE(button.GetImageToPaint().isNull());
@@ -88,7 +88,7 @@ TEST_F(ImageButtonTest, Basics) {
}
TEST_F(ImageButtonTest, SetAndGetImage) {
- ImageButton button(nullptr);
+ ImageButton button;
// Images start as null.
EXPECT_TRUE(button.GetImage(Button::STATE_NORMAL).isNull());
@@ -114,7 +114,7 @@ TEST_F(ImageButtonTest, SetAndGetImage) {
}
TEST_F(ImageButtonTest, ImagePositionWithBorder) {
- ImageButton button(nullptr);
+ ImageButton button;
gfx::ImageSkia image = CreateTestImage(20, 30);
button.SetImage(Button::STATE_NORMAL, &image);
@@ -143,7 +143,7 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) {
}
TEST_F(ImageButtonTest, LeftAlignedMirrored) {
- ImageButton button(nullptr);
+ ImageButton button;
gfx::ImageSkia image = CreateTestImage(20, 30);
button.SetImage(Button::STATE_NORMAL, &image);
button.SetBounds(0, 0, 50, 30);
@@ -156,7 +156,7 @@ TEST_F(ImageButtonTest, LeftAlignedMirrored) {
}
TEST_F(ImageButtonTest, RightAlignedMirrored) {
- ImageButton button(nullptr);
+ ImageButton button;
gfx::ImageSkia image = CreateTestImage(20, 30);
button.SetImage(Button::STATE_NORMAL, &image);
button.SetBounds(0, 0, 50, 30);
@@ -171,7 +171,7 @@ TEST_F(ImageButtonTest, RightAlignedMirrored) {
TEST_F(ImageButtonTest, PreferredSizeInvalidation) {
Parent parent;
- ImageButton button(nullptr);
+ ImageButton button;
gfx::ImageSkia first_image = CreateTestImage(20, 30);
gfx::ImageSkia second_image = CreateTestImage(50, 50);
button.SetImage(Button::STATE_NORMAL, &first_image);
diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc
index 3aef54f32cd..77c7f6830f9 100644
--- a/chromium/ui/views/controls/button/label_button.cc
+++ b/chromium/ui/views/controls/button/label_button.cc
@@ -20,6 +20,7 @@
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/native_theme/themed_vector_icon.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/label_button_border.h"
@@ -45,8 +46,8 @@ LabelButton::LabelButton(ButtonListener* listener,
image_ = AddChildView(std::make_unique<ImageView>());
image_->set_can_process_events_within_subtree(false);
- label_ =
- AddChildView(std::make_unique<LabelButtonLabel>(text, button_context));
+ label_ = AddChildView(
+ std::make_unique<internal::LabelButtonLabel>(text, button_context));
label_->SetAutoColorReadabilityEnabled(false);
label_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
@@ -57,14 +58,36 @@ LabelButton::LabelButton(ButtonListener* listener,
LabelButton::~LabelButton() = default;
gfx::ImageSkia LabelButton::GetImage(ButtonState for_state) const {
- if (for_state != STATE_NORMAL && button_state_images_[for_state].isNull())
- return button_state_images_[STATE_NORMAL];
- return button_state_images_[for_state];
+ for_state = ImageStateForState(for_state);
+
+ const auto& image_model = button_state_image_models_[for_state];
+ if (image_model.IsImage())
+ return image_model.GetImage().AsImageSkia();
+
+ if (image_model.IsVectorIcon()) {
+ return ui::ThemedVectorIcon(image_model.GetVectorIcon())
+ .GetImageSkia(GetNativeTheme());
+ }
+
+ return gfx::ImageSkia();
}
void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) {
- button_state_images_[for_state] = image;
- UpdateImage();
+ SetImageModel(for_state, ui::ImageModel::FromImageSkia(image));
+}
+
+void LabelButton::SetImageModel(ButtonState for_state,
+ const ui::ImageModel& image_model) {
+ if (button_state_image_models_[for_state] == image_model)
+ return;
+
+ const auto old_image_state = ImageStateForState(GetVisualState());
+
+ button_state_image_models_[for_state] = image_model;
+
+ if (for_state == old_image_state ||
+ for_state == ImageStateForState(GetVisualState()))
+ UpdateImage();
}
const base::string16& LabelButton::GetText() const {
@@ -89,7 +112,7 @@ void LabelButton::SetTextColor(ButtonState for_state, SkColor color) {
button_state_colors_[for_state] = color;
if (for_state == STATE_DISABLED)
label_->SetDisabledColor(color);
- else if (for_state == state())
+ else if (for_state == GetState())
label_->SetEnabledColor(color);
explicitly_set_colors_[for_state] = true;
}
@@ -106,6 +129,10 @@ void LabelButton::SetEnabledTextColors(base::Optional<SkColor> color) {
ResetColorsFromNativeTheme();
}
+SkColor LabelButton::GetCurrentTextColor() const {
+ return label_->GetEnabledColor();
+}
+
void LabelButton::SetTextShadows(const gfx::ShadowValues& shadows) {
label_->SetShadows(shadows);
}
@@ -138,8 +165,7 @@ void LabelButton::SetMinSize(const gfx::Size& min_size) {
if (GetMinSize() == min_size)
return;
min_size_ = min_size;
- ResetCachedPreferredSize();
- OnPropertyChanged(&min_size_, kPropertyEffectsNone);
+ OnPropertyChanged(&min_size_, kPropertyEffectsPreferredSizeChanged);
}
gfx::Size LabelButton::GetMaxSize() const {
@@ -150,8 +176,7 @@ void LabelButton::SetMaxSize(const gfx::Size& max_size) {
if (GetMaxSize() == max_size)
return;
max_size_ = max_size;
- ResetCachedPreferredSize();
- OnPropertyChanged(&max_size_, kPropertyEffectsNone);
+ OnPropertyChanged(&max_size_, kPropertyEffectsPreferredSizeChanged);
}
bool LabelButton::GetIsDefault() const {
@@ -163,6 +188,11 @@ void LabelButton::SetIsDefault(bool is_default) {
if (GetIsDefault() == is_default)
return;
is_default_ = is_default;
+
+ // The default button has an accelerator for VKEY_RETURN, which clicks it.
+ // Note that if PlatformStyle::kReturnClicksFocusedControl is true and another
+ // button is focused, that button's VKEY_RETURN handler will take precedence.
+ // See Button::GetKeyClickActionForEvent().
ui::Accelerator accel(ui::VKEY_RETURN, ui::EF_NONE);
if (is_default)
AddAccelerator(accel);
@@ -179,8 +209,8 @@ void LabelButton::SetImageLabelSpacing(int spacing) {
if (GetImageLabelSpacing() == spacing)
return;
image_label_spacing_ = spacing;
- ResetCachedPreferredSize();
- OnPropertyChanged(&image_label_spacing_, kPropertyEffectsLayout);
+ OnPropertyChanged(&image_label_spacing_,
+ kPropertyEffectsPreferredSizeChanged);
}
bool LabelButton::GetImageCentered() const {
@@ -203,7 +233,6 @@ std::unique_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const {
void LabelButton::SetBorder(std::unique_ptr<Border> border) {
border_is_themed_border_ = false;
View::SetBorder(std::move(border));
- ResetCachedPreferredSize();
}
void LabelButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
@@ -212,34 +241,27 @@ void LabelButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
}
gfx::Size LabelButton::CalculatePreferredSize() const {
- // Cache the computed size, as recomputing it is an expensive operation.
- if (!cached_preferred_size_) {
- gfx::Size size = GetUnclampedSizeWithoutLabel();
-
- // Disregard label in the preferred size if the button is shrinking down to
- // show no label soon.
- if (!shrinking_down_label_) {
- const gfx::Size preferred_label_size = label_->GetPreferredSize();
- size.Enlarge(preferred_label_size.width(), 0);
-
- // Increase the height of the label (with insets) if larger.
- size.set_height(std::max(
- preferred_label_size.height() + GetInsets().height(), size.height()));
- }
-
- size.SetToMax(GetMinSize());
+ gfx::Size size = GetUnclampedSizeWithoutLabel();
+
+ // Account for the label only when the button is not shrinking down to hide
+ // the label entirely.
+ if (!shrinking_down_label_) {
+ const gfx::Size preferred_label_size = label_->GetPreferredSize();
+ size.Enlarge(preferred_label_size.width(), 0);
+ size.SetToMax(
+ gfx::Size(0, preferred_label_size.height() + GetInsets().height()));
+ }
- // Clamp size to max size (if valid).
- const gfx::Size max_size = GetMaxSize();
- if (max_size.width() > 0)
- size.set_width(std::min(max_size.width(), size.width()));
- if (max_size.height() > 0)
- size.set_height(std::min(max_size.height(), size.height()));
+ size.SetToMax(GetMinSize());
- cached_preferred_size_ = size;
- }
+ // Clamp size to max size (if valid).
+ const gfx::Size max_size = GetMaxSize();
+ if (max_size.width() > 0)
+ size.set_width(std::min(max_size.width(), size.width()));
+ if (max_size.height() > 0)
+ size.set_height(std::min(max_size.height(), size.height()));
- return cached_preferred_size_.value();
+ return size;
}
gfx::Size LabelButton::GetMinimumSize() const {
@@ -372,7 +394,7 @@ gfx::Rect LabelButton::GetThemePaintRect() const {
ui::NativeTheme::State LabelButton::GetThemeState(
ui::NativeTheme::ExtraParams* params) const {
GetExtraParams(params);
- switch (state()) {
+ switch (GetState()) {
case STATE_NORMAL:
return ui::NativeTheme::kNormal;
case STATE_HOVERED:
@@ -382,7 +404,7 @@ ui::NativeTheme::State LabelButton::GetThemeState(
case STATE_DISABLED:
return ui::NativeTheme::kDisabled;
case STATE_COUNT:
- NOTREACHED() << "Unknown state: " << state();
+ NOTREACHED();
}
return ui::NativeTheme::kNormal;
}
@@ -405,16 +427,6 @@ ui::NativeTheme::State LabelButton::GetForegroundThemeState(
void LabelButton::UpdateImage() {
image_->SetImage(GetImage(GetVisualState()));
- ResetCachedPreferredSize();
-}
-
-void LabelButton::UpdateThemedBorder() {
- // Don't override borders set by others.
- if (!border_is_themed_border_)
- return;
-
- SetBorder(PlatformStyle::CreateThemedLabelButtonBorder(this));
- border_is_themed_border_ = true;
}
void LabelButton::AddLayerBeneathView(ui::Layer* new_layer) {
@@ -451,16 +463,25 @@ PropertyEffects LabelButton::UpdateStyleToIndicateDefaultStatus() {
label_->SetFontList(GetIsDefault() ? cached_default_button_font_list_
: cached_normal_font_list_);
ResetLabelEnabledColor();
- return kPropertyEffectsLayout;
+ return kPropertyEffectsPreferredSizeChanged;
}
void LabelButton::ChildPreferredSizeChanged(View* child) {
PreferredSizeChanged();
}
-void LabelButton::PreferredSizeChanged() {
- ResetCachedPreferredSize();
- Button::PreferredSizeChanged();
+void LabelButton::AddedToWidget() {
+ if (PlatformStyle::kInactiveWidgetControlsAppearDisabled) {
+ paint_as_active_subscription_ =
+ GetWidget()->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
+ &LabelButton::VisualStateChanged, base::Unretained(this)));
+ // Set the initial state correctly.
+ VisualStateChanged();
+ }
+}
+
+void LabelButton::RemovedFromWidget() {
+ paint_as_active_subscription_.reset();
}
void LabelButton::OnFocus() {
@@ -478,23 +499,19 @@ void LabelButton::OnBlur() {
void LabelButton::OnThemeChanged() {
Button::OnThemeChanged();
ResetColorsFromNativeTheme();
- UpdateThemedBorder();
+ UpdateImage();
+ if (border_is_themed_border_)
+ View::SetBorder(PlatformStyle::CreateThemedLabelButtonBorder(this));
ResetLabelEnabledColor();
- // Invalidate the layout to pickup the new insets from the border.
- InvalidateLayout();
// The entire button has to be repainted here, since the native theme can
// define the tint for the entire background/border/focus ring.
SchedulePaint();
}
void LabelButton::StateChanged(ButtonState old_state) {
- const gfx::Size previous_image_size(image_->GetPreferredSize());
- UpdateImage();
- ResetLabelEnabledColor();
- label_->SetEnabled(state() != STATE_DISABLED);
- if (image_->GetPreferredSize() != previous_image_size)
- InvalidateLayout();
Button::StateChanged(old_state);
+ ResetLabelEnabledColor();
+ VisualStateChanged();
}
void LabelButton::SetTextInternal(const base::string16& text) {
@@ -502,32 +519,26 @@ void LabelButton::SetTextInternal(const base::string16& text) {
label_->SetText(text);
// Setting text cancels ShrinkDownThenClearText().
- if (shrinking_down_label_) {
- shrinking_down_label_ = false;
- PreferredSizeChanged();
- }
+ const auto effects = shrinking_down_label_
+ ? kPropertyEffectsPreferredSizeChanged
+ : kPropertyEffectsNone;
+ shrinking_down_label_ = false;
// TODO(pkasting): Remove this and forward callback subscriptions to the
// underlying label property when Label is converted to properties.
- OnPropertyChanged(label_, kPropertyEffectsNone);
+ OnPropertyChanged(label_, effects);
}
void LabelButton::ClearTextIfShrunkDown() {
- if (!cached_preferred_size_)
- CalculatePreferredSize();
- if (shrinking_down_label_ && width() <= cached_preferred_size_->width() &&
- height() <= cached_preferred_size_->height()) {
+ const gfx::Size preferred_size = GetPreferredSize();
+ if (shrinking_down_label_ && width() <= preferred_size.width() &&
+ height() <= preferred_size.height()) {
// Once the button shrinks down to its preferred size (that disregards the
// current text), we finish the operation by clearing the text.
- shrinking_down_label_ = false;
SetText(base::string16());
}
}
-void LabelButton::ResetCachedPreferredSize() {
- cached_preferred_size_ = base::nullopt;
-}
-
gfx::Size LabelButton::GetUnclampedSizeWithoutLabel() const {
const gfx::Size image_size = image_->GetPreferredSize();
gfx::Size size = image_size;
@@ -545,6 +556,20 @@ gfx::Size LabelButton::GetUnclampedSizeWithoutLabel() const {
return size;
}
+Button::ButtonState LabelButton::GetVisualState() const {
+ const auto* widget = GetWidget();
+ if (PlatformStyle::kInactiveWidgetControlsAppearDisabled && widget &&
+ widget->CanActivate() && !widget->ShouldPaintAsActive())
+ return STATE_DISABLED;
+ return GetState();
+}
+
+void LabelButton::VisualStateChanged() {
+ UpdateImage();
+ UpdateBackgroundColor();
+ label_->SetEnabled(GetVisualState() != STATE_DISABLED);
+}
+
void LabelButton::ResetColorsFromNativeTheme() {
const ui::NativeTheme* theme = GetNativeTheme();
// Since this is a LabelButton, use the label colors.
@@ -567,11 +592,17 @@ void LabelButton::ResetColorsFromNativeTheme() {
}
void LabelButton::ResetLabelEnabledColor() {
- const SkColor color = button_state_colors_[state()];
- if (state() != STATE_DISABLED && label_->GetEnabledColor() != color)
+ const SkColor color = button_state_colors_[GetState()];
+ if (GetState() != STATE_DISABLED && label_->GetEnabledColor() != color)
label_->SetEnabledColor(color);
}
+Button::ButtonState LabelButton::ImageStateForState(
+ ButtonState for_state) const {
+ return button_state_image_models_[for_state].IsEmpty() ? STATE_NORMAL
+ : for_state;
+}
+
BEGIN_METADATA(LabelButton)
METADATA_PARENT_CLASS(Button)
ADD_PROPERTY_METADATA(LabelButton, base::string16, Text)
diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h
index ac348aa7dd4..45692ab8b7c 100644
--- a/chromium/ui/views/controls/button/label_button.h
+++ b/chromium/ui/views/controls/button/label_button.h
@@ -21,6 +21,7 @@
#include "ui/views/layout/layout_provider.h"
#include "ui/views/native_theme_delegate.h"
#include "ui/views/style/typography.h"
+#include "ui/views/widget/widget.h"
namespace views {
@@ -35,15 +36,17 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// Creates a LabelButton with ButtonPressed() events sent to |listener| and
// label |text|. |button_context| is a value from views::style::TextContext
// and determines the appearance of |text|.
- LabelButton(ButtonListener* listener,
- const base::string16& text,
- int button_context = style::CONTEXT_BUTTON);
+ explicit LabelButton(ButtonListener* listener = nullptr,
+ const base::string16& text = base::string16(),
+ int button_context = style::CONTEXT_BUTTON);
~LabelButton() override;
// Gets or sets the image shown for the specified button state.
// GetImage returns the image for STATE_NORMAL if the state's image is empty.
virtual gfx::ImageSkia GetImage(ButtonState for_state) const;
+ // TODO(http://crbug.com/1100034) prefer SetImageModel over SetImage().
void SetImage(ButtonState for_state, const gfx::ImageSkia& image);
+ void SetImageModel(ButtonState for_state, const ui::ImageModel& image_model);
// Gets or sets the text shown on the button.
const base::string16& GetText() const;
@@ -64,6 +67,9 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// Sets the text colors shown for the non-disabled states to |color|.
virtual void SetEnabledTextColors(base::Optional<SkColor> color);
+ // Gets the current state text color.
+ SkColor GetCurrentTextColor() const;
+
// Sets drop shadows underneath the text.
void SetTextShadows(const gfx::ShadowValues& shadows);
@@ -145,9 +151,13 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// Updates the image view to contain the appropriate button state image.
void UpdateImage();
- // Updates the border as per the NativeTheme, unless a different border was
- // set with SetBorder.
- void UpdateThemedBorder();
+ // Updates the background color, if the background color is state-sensitive.
+ virtual void UpdateBackgroundColor() {}
+
+ // Returns the current visual appearance of the button. This takes into
+ // account both the button's underlying state and the state of the containing
+ // widget.
+ ButtonState GetVisualState() const;
// Fills |params| with information about the button.
virtual void GetExtraParams(ui::NativeTheme::ExtraParams* params) const;
@@ -158,7 +168,8 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// Button:
void ChildPreferredSizeChanged(View* child) override;
- void PreferredSizeChanged() override;
+ void AddedToWidget() override;
+ void RemovedFromWidget() override;
void OnFocus() override;
void OnBlur() override;
void OnThemeChanged() override;
@@ -169,9 +180,6 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
void ClearTextIfShrunkDown();
- // Resets |cached_preferred_size_|.
- void ResetCachedPreferredSize();
-
// Gets the preferred size (without respecting min_size_ or max_size_), but
// does not account for the label. This is shared between GetHeightForWidth
// and CalculatePreferredSize. GetHeightForWidth will subtract the width
@@ -181,6 +189,10 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// height as total height, and clamp to min/max sizes as appropriate.
gfx::Size GetUnclampedSizeWithoutLabel() const;
+ // Updates the portions of the object that might change in response to a
+ // change in the value returned by GetVisualState().
+ void VisualStateChanged();
+
// Resets colors from the NativeTheme, explicitly set colors are unchanged.
void ResetColorsFromNativeTheme();
@@ -189,9 +201,13 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// correct for the current background.
void ResetLabelEnabledColor();
+ // Returns the state whose image is shown for |for_state|, by falling back to
+ // STATE_NORMAL when |for_state|'s image is empty.
+ ButtonState ImageStateForState(ButtonState for_state) const;
+
// The image and label shown in the button.
ImageView* image_;
- LabelButtonLabel* label_;
+ internal::LabelButtonLabel* label_;
// A separate view is necessary to hold the ink drop layer so that it can
// be stacked below |image_| and on top of |label_|, without resorting to
@@ -203,8 +219,8 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
gfx::FontList cached_normal_font_list_;
gfx::FontList cached_default_button_font_list_;
- // The images and colors for each button state.
- gfx::ImageSkia button_state_images_[STATE_COUNT] = {};
+ // The image models and colors for each button state.
+ ui::ImageModel button_state_image_models_[STATE_COUNT] = {};
SkColor button_state_colors_[STATE_COUNT] = {};
// Used to track whether SetTextColor() has been invoked.
@@ -214,9 +230,6 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
gfx::Size min_size_;
gfx::Size max_size_;
- // Cache the last computed preferred size.
- mutable base::Optional<gfx::Size> cached_preferred_size_;
-
// A flag indicating that this button should not include the label in its
// desired size. Furthermore, once the bounds of the button adapt to this
// desired size, the text in the label should get cleared.
@@ -245,6 +258,9 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate {
// UI direction).
gfx::HorizontalAlignment horizontal_alignment_ = gfx::ALIGN_LEFT;
+ std::unique_ptr<Widget::PaintAsActiveCallbackList::Subscription>
+ paint_as_active_subscription_;
+
DISALLOW_COPY_AND_ASSIGN(LabelButton);
};
diff --git a/chromium/ui/views/controls/button/label_button_label.cc b/chromium/ui/views/controls/button/label_button_label.cc
index 7e40c80e5a2..a1ec3993631 100644
--- a/chromium/ui/views/controls/button/label_button_label.cc
+++ b/chromium/ui/views/controls/button/label_button_label.cc
@@ -6,6 +6,8 @@
namespace views {
+namespace internal {
+
LabelButtonLabel::LabelButtonLabel(const base::string16& text, int text_context)
: Label(text, text_context, style::STYLE_PRIMARY) {}
@@ -42,4 +44,6 @@ void LabelButtonLabel::SetColorForEnableState() {
}
}
+} // namespace internal
+
} // namespace views
diff --git a/chromium/ui/views/controls/button/label_button_label.h b/chromium/ui/views/controls/button/label_button_label.h
index 49d88b6023c..88ab37127f1 100644
--- a/chromium/ui/views/controls/button/label_button_label.h
+++ b/chromium/ui/views/controls/button/label_button_label.h
@@ -16,6 +16,8 @@
namespace views {
+namespace internal {
+
// A Label subclass that can be disabled. This is only used internally for
// views::LabelButton.
class VIEWS_EXPORT LabelButtonLabel : public Label {
@@ -48,6 +50,8 @@ class VIEWS_EXPORT LabelButtonLabel : public Label {
DISALLOW_COPY_AND_ASSIGN(LabelButtonLabel);
};
+} // namespace internal
+
} // namespace views
#endif // UI_VIEWS_CONTROLS_BUTTON_LABEL_BUTTON_LABEL_H_
diff --git a/chromium/ui/views/controls/button/label_button_label_unittest.cc b/chromium/ui/views/controls/button/label_button_label_unittest.cc
index 8e880de4eaf..f42bbf7bfe8 100644
--- a/chromium/ui/views/controls/button/label_button_label_unittest.cc
+++ b/chromium/ui/views/controls/button/label_button_label_unittest.cc
@@ -36,7 +36,7 @@ class TestNativeTheme : public ui::NativeThemeBase {
// LabelButtonLabel subclass that reports its text color whenever a paint is
// scheduled.
-class TestLabel : public LabelButtonLabel {
+class TestLabel : public internal::LabelButtonLabel {
public:
explicit TestLabel(SkColor* last_color)
: LabelButtonLabel(base::string16(), views::style::CONTEXT_BUTTON),
diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc
index 38443891fff..88953b032f0 100644
--- a/chromium/ui/views/controls/button/label_button_unittest.cc
+++ b/chromium/ui/views/controls/button/label_button_unittest.cc
@@ -93,11 +93,15 @@ class LabelButtonTest : public test::WidgetTest {
// used (which could be derived from the Widget's NativeTheme).
test_widget_ = CreateTopLevelPlatformWidget();
+ // Ensure the Widget is active, since LabelButton appearance in inactive
+ // Windows is platform-dependent.
+ test_widget_->Show();
+
// The test code below is not prepared to handle dark mode.
test_widget_->GetNativeTheme()->set_use_dark_colors(false);
- button_ = new TestLabelButton;
- test_widget_->GetContentsView()->AddChildView(button_);
+ button_ = test_widget_->GetContentsView()->AddChildView(
+ std::make_unique<TestLabelButton>());
// Establish the expected text colors for testing changes due to state.
themed_normal_text_color_ = button_->GetNativeTheme()->GetSystemColor(
@@ -107,7 +111,8 @@ class LabelButtonTest : public test::WidgetTest {
// NativeTheme and use a hardcoded black or (on Mac) have a NativeTheme that
// reliably returns black.
styled_normal_text_color_ = SK_ColorBLACK;
-#if defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && \
+ BUILDFLAG(ENABLE_DESKTOP_AURA)
// The Linux theme provides a non-black highlight text color, but it's not
// used for styled buttons.
styled_highlight_text_color_ = styled_normal_text_color_ =
@@ -154,7 +159,7 @@ TEST_F(LabelButtonTest, Init) {
ax::mojom::StringAttribute::kName));
EXPECT_FALSE(button.GetIsDefault());
- EXPECT_EQ(Button::STATE_NORMAL, button.state());
+ EXPECT_EQ(Button::STATE_NORMAL, button.GetState());
EXPECT_EQ(button.image()->parent(), &button);
EXPECT_EQ(button.label()->parent(), &button);
@@ -686,6 +691,36 @@ TEST_F(LabelButtonTest, ImageOrLabelGetClipped) {
EXPECT_GE(button_->label()->height(), image_size);
}
+TEST_F(LabelButtonTest, UpdateImageAfterSettingImageModel) {
+ auto is_showing_image = [&](const gfx::ImageSkia& image) {
+ return button_->image()->GetImage().BackedBySameObjectAs(image);
+ };
+
+ auto normal_image = CreateTestImage(16, 16);
+ button_->SetImageModel(Button::STATE_NORMAL,
+ ui::ImageModel::FromImageSkia(normal_image));
+ EXPECT_TRUE(is_showing_image(normal_image));
+
+ // When the button has no specific disabled image, changing the normal image
+ // while the button is disabled should update the currently-visible image.
+ normal_image = CreateTestImage(16, 16);
+ button_->SetState(Button::STATE_DISABLED);
+ button_->SetImageModel(Button::STATE_NORMAL,
+ ui::ImageModel::FromImageSkia(normal_image));
+ EXPECT_TRUE(is_showing_image(normal_image));
+
+ // Any specific disabled image should take precedence over the normal image.
+ auto disabled_image = CreateTestImage(16, 16);
+ button_->SetImageModel(Button::STATE_DISABLED,
+ ui::ImageModel::FromImageSkia(disabled_image));
+ EXPECT_TRUE(is_showing_image(disabled_image));
+
+ // Removing the disabled image should result in falling back to the normal
+ // image again.
+ button_->SetImageModel(Button::STATE_DISABLED, ui::ImageModel());
+ EXPECT_TRUE(is_showing_image(normal_image));
+}
+
// Test fixture for a LabelButton that has an ink drop configured.
class InkDropLabelButtonTest : public ViewsTestBase {
public:
diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc
index 79c42e7b9ac..53b978caed4 100644
--- a/chromium/ui/views/controls/button/md_text_button.cc
+++ b/chromium/ui/views/controls/button/md_text_button.cc
@@ -30,15 +30,32 @@
namespace views {
-// static
-std::unique_ptr<MdTextButton> MdTextButton::Create(ButtonListener* listener,
- const base::string16& text,
- int button_context) {
- auto button = base::WrapUnique<MdTextButton>(
- new MdTextButton(listener, button_context));
- button->SetText(text);
-
- return button;
+MdTextButton::MdTextButton(ButtonListener* listener,
+ const base::string16& text,
+ int button_context)
+ : LabelButton(listener, text, button_context) {
+ SetInkDropMode(InkDropMode::ON);
+ set_has_ink_drop_action_on_click(true);
+ set_show_ink_drop_when_hot_tracked(true);
+ SetCornerRadius(LayoutProvider::Get()->GetCornerRadiusMetric(EMPHASIS_LOW));
+ SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ SetFocusForPlatform();
+
+ const int minimum_width = LayoutProvider::Get()->GetDistanceMetric(
+ DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
+ SetMinSize(gfx::Size(minimum_width, 0));
+ SetInstallFocusRingOnFocus(true);
+ label()->SetAutoColorReadabilityEnabled(false);
+ set_request_focus_on_press(false);
+ set_animate_on_state_change(true);
+
+ // Paint to a layer so that the canvas is snapped to pixel boundaries (useful
+ // for fractional DSF).
+ SetPaintToLayer();
+ layer()->SetFillsBoundsOpaquely(false);
+
+ // Call this to calculate the border given text.
+ UpdatePadding();
}
MdTextButton::~MdTextButton() = default;
@@ -111,7 +128,7 @@ std::unique_ptr<views::InkDropHighlight> MdTextButton::CreateInkDropHighlight()
is_prominent_
? ui::NativeTheme::kColorId_ProminentButtonInkDropShadowColor
: ui::NativeTheme::kColorId_ButtonInkDropShadowColor;
- if (state() == STATE_HOVERED) {
+ if (GetState() == STATE_HOVERED) {
fill_color_id = is_prominent_
? ui::NativeTheme::kColorId_ProminentButtonHoverColor
: ui::NativeTheme::kColorId_ButtonHoverColor;
@@ -158,29 +175,6 @@ PropertyEffects MdTextButton::UpdateStyleToIndicateDefaultStatus() {
return kPropertyEffectsNone;
}
-MdTextButton::MdTextButton(ButtonListener* listener, int button_context)
- : LabelButton(listener, base::string16(), button_context) {
- SetInkDropMode(InkDropMode::ON);
- set_has_ink_drop_action_on_click(true);
- set_show_ink_drop_when_hot_tracked(true);
- SetCornerRadius(LayoutProvider::Get()->GetCornerRadiusMetric(EMPHASIS_LOW));
- SetHorizontalAlignment(gfx::ALIGN_CENTER);
- SetFocusForPlatform();
- const int minimum_width = LayoutProvider::Get()->GetDistanceMetric(
- DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
- SetMinSize(gfx::Size(minimum_width, 0));
- SetInstallFocusRingOnFocus(true);
- label()->SetAutoColorReadabilityEnabled(false);
- set_request_focus_on_press(false);
-
- set_animate_on_state_change(true);
-
- // Paint to a layer so that the canvas is snapped to pixel boundaries (useful
- // for fractional DSF).
- SetPaintToLayer();
- layer()->SetFillsBoundsOpaquely(false);
-}
-
void MdTextButton::UpdatePadding() {
// Don't use font-based padding when there's no text visible.
if (GetText().empty()) {
@@ -227,27 +221,31 @@ gfx::Insets MdTextButton::CalculateDefaultPadding() const {
horizontal_padding);
}
-void MdTextButton::UpdateColors() {
- bool is_disabled = state() == STATE_DISABLED;
+void MdTextButton::UpdateTextColor() {
+ if (explicitly_set_normal_color())
+ return;
+
SkColor enabled_text_color =
style::GetColor(*this, label()->GetTextContext(),
is_prominent_ ? style::STYLE_DIALOG_BUTTON_DEFAULT
: style::STYLE_PRIMARY);
- if (!explicitly_set_normal_color()) {
- const auto colors = explicitly_set_colors();
- LabelButton::SetEnabledTextColors(enabled_text_color);
- // Disabled buttons need the disabled color explicitly set.
- // This ensures that label()->GetEnabledColor() returns the correct color as
- // the basis for calculating the stroke color. enabled_text_color isn't used
- // since a descendant could have overridden the label enabled color.
- if (is_disabled) {
- LabelButton::SetTextColor(
- STATE_DISABLED, style::GetColor(*this, label()->GetTextContext(),
- style::STYLE_DISABLED));
- }
- set_explicitly_set_colors(colors);
+
+ const auto colors = explicitly_set_colors();
+ LabelButton::SetEnabledTextColors(enabled_text_color);
+ // Disabled buttons need the disabled color explicitly set.
+ // This ensures that label()->GetEnabledColor() returns the correct color as
+ // the basis for calculating the stroke color. enabled_text_color isn't used
+ // since a descendant could have overridden the label enabled color.
+ if (GetState() == STATE_DISABLED) {
+ LabelButton::SetTextColor(STATE_DISABLED,
+ style::GetColor(*this, label()->GetTextContext(),
+ style::STYLE_DISABLED));
}
+ set_explicitly_set_colors(colors);
+}
+void MdTextButton::UpdateBackgroundColor() {
+ bool is_disabled = GetVisualState() == STATE_DISABLED;
ui::NativeTheme* theme = GetNativeTheme();
SkColor bg_color =
theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonColor);
@@ -264,7 +262,7 @@ void MdTextButton::UpdateColors() {
}
}
- if (state() == STATE_PRESSED) {
+ if (GetState() == STATE_PRESSED) {
bg_color = theme->GetSystemButtonPressedColor(bg_color);
}
@@ -272,14 +270,19 @@ void MdTextButton::UpdateColors() {
if (is_prominent_) {
stroke_color = SK_ColorTRANSPARENT;
} else {
- stroke_color = SkColorSetA(
- theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonBorderColor),
- is_disabled ? 0x43 : SK_AlphaOPAQUE);
+ stroke_color = theme->GetSystemColor(
+ is_disabled ? ui::NativeTheme::kColorId_DisabledButtonBorderColor
+ : ui::NativeTheme::kColorId_ButtonBorderColor);
}
SetBackground(
CreateBackgroundFromPainter(Painter::CreateRoundRectWith1PxBorderPainter(
bg_color, stroke_color, corner_radius_)));
+}
+
+void MdTextButton::UpdateColors() {
+ UpdateTextColor();
+ UpdateBackgroundColor();
SchedulePaint();
}
diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h
index 1d293a70166..81b342becf7 100644
--- a/chromium/ui/views/controls/button/md_text_button.h
+++ b/chromium/ui/views/controls/button/md_text_button.h
@@ -19,10 +19,9 @@ class VIEWS_EXPORT MdTextButton : public LabelButton {
public:
METADATA_HEADER(MdTextButton);
- static std::unique_ptr<MdTextButton> Create(
- ButtonListener* listener,
- const base::string16& text,
- int button_context = style::CONTEXT_BUTTON_MD);
+ explicit MdTextButton(ButtonListener* listener = nullptr,
+ const base::string16& text = base::string16(),
+ int button_context = style::CONTEXT_BUTTON_MD);
~MdTextButton() override;
@@ -57,13 +56,14 @@ class VIEWS_EXPORT MdTextButton : public LabelButton {
void OnFocus() override;
void OnBlur() override;
- MdTextButton(ButtonListener* listener, int button_context);
-
private:
void UpdatePadding();
- void UpdateColors();
gfx::Insets CalculateDefaultPadding() const;
+ void UpdateTextColor();
+ void UpdateBackgroundColor() override;
+ void UpdateColors();
+
// True if this button uses prominent styling (blue fill, etc.).
bool is_prominent_ = false;
diff --git a/chromium/ui/views/controls/button/md_text_button_unittest.cc b/chromium/ui/views/controls/button/md_text_button_unittest.cc
index 5fe5d9c1bf1..4013de90dab 100644
--- a/chromium/ui/views/controls/button/md_text_button_unittest.cc
+++ b/chromium/ui/views/controls/button/md_text_button_unittest.cc
@@ -4,7 +4,11 @@
#include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/background.h"
+#include "ui/views/style/platform_style.h"
+#include "ui/views/test/views_drawing_test_utils.h"
#include "ui/views/test/views_test_base.h"
+#include "ui/views/test/widget_test.h"
namespace views {
@@ -13,7 +17,7 @@ using MdTextButtonTest = ViewsTestBase;
TEST_F(MdTextButtonTest, CustomPadding) {
const base::string16 text = base::ASCIIToUTF16("abc");
std::unique_ptr<MdTextButton> button =
- MdTextButton::Create(nullptr, text, views::style::CONTEXT_BUTTON_MD);
+ std::make_unique<MdTextButton>(nullptr, text);
const gfx::Insets custom_padding(10, 20);
ASSERT_NE(button->GetInsets(), custom_padding);
@@ -22,4 +26,55 @@ TEST_F(MdTextButtonTest, CustomPadding) {
EXPECT_EQ(button->GetInsets(), custom_padding);
}
+TEST_F(MdTextButtonTest, BackgroundColorChangesWithWidgetActivation) {
+ // Test whether the button's background color changes when its containing
+ // widget's activation changes.
+ if (!PlatformStyle::kInactiveWidgetControlsAppearDisabled)
+ GTEST_SKIP() << "Button colors do not change with widget activation here.";
+
+ std::unique_ptr<Widget> widget = CreateTestWidget();
+ auto* button = widget->SetContentsView(
+ std::make_unique<MdTextButton>(nullptr, base::ASCIIToUTF16("button")));
+ button->SetProminent(true);
+ button->SetBounds(0, 0, 70, 20);
+ widget->LayoutRootViewIfNecessary();
+
+ const ui::NativeTheme* native_theme = button->GetNativeTheme();
+
+ test::WidgetTest::SimulateNativeActivate(widget.get());
+ EXPECT_TRUE(widget->IsActive());
+ SkBitmap active_bitmap = views::test::PaintViewToBitmap(button);
+
+ auto background_color = [button](const SkBitmap& bitmap) {
+ // The very edge of the bitmap contains the button's border, which we aren't
+ // interested in here. Instead, grab a pixel that is inset by the button's
+ // corner radius from the top-left point to avoid the border.
+ //
+ // It would make a bit more sense to inset by the border thickness or
+ // something, but MdTextButton doesn't expose (or even know) that value
+ // without some major abstraction violation.
+ int corner_radius = button->GetCornerRadius();
+ return bitmap.getColor(corner_radius, corner_radius);
+ };
+
+ EXPECT_EQ(background_color(active_bitmap),
+ native_theme->GetSystemColor(
+ ui::NativeTheme::kColorId_ProminentButtonColor));
+
+ // It would be neat to also check the text color here, but the label's text
+ // ends up drawn on top of the background with antialiasing, which means there
+ // aren't any pixels that are actually *exactly*
+ // kColorId_TextOnProminentButtonColor. Bummer.
+
+ // Activate another widget to cause the original widget to deactivate.
+ std::unique_ptr<Widget> other_widget = CreateTestWidget();
+ test::WidgetTest::SimulateNativeActivate(other_widget.get());
+ EXPECT_FALSE(widget->IsActive());
+ SkBitmap inactive_bitmap = views::test::PaintViewToBitmap(button);
+
+ EXPECT_EQ(background_color(inactive_bitmap),
+ native_theme->GetSystemColor(
+ ui::NativeTheme::kColorId_ProminentButtonDisabledColor));
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc
index c62097f0cad..a708a841a82 100644
--- a/chromium/ui/views/controls/button/menu_button.cc
+++ b/chromium/ui/views/controls/button/menu_button.cc
@@ -13,8 +13,8 @@
namespace views {
-MenuButton::MenuButton(const base::string16& text,
- ButtonListener* button_listener,
+MenuButton::MenuButton(ButtonListener* button_listener,
+ const base::string16& text,
int button_context)
: LabelButton(nullptr, text, button_context) {
SetHorizontalAlignment(gfx::ALIGN_LEFT);
diff --git a/chromium/ui/views/controls/button/menu_button.h b/chromium/ui/views/controls/button/menu_button.h
index 7f64f44f851..18edea530ca 100644
--- a/chromium/ui/views/controls/button/menu_button.h
+++ b/chromium/ui/views/controls/button/menu_button.h
@@ -26,9 +26,9 @@ class VIEWS_EXPORT MenuButton : public LabelButton {
METADATA_HEADER(MenuButton);
// Create a Button.
- MenuButton(const base::string16& text,
- ButtonListener* button_listener,
- int button_context = style::CONTEXT_BUTTON);
+ explicit MenuButton(ButtonListener* button_listener = nullptr,
+ const base::string16& text = base::string16(),
+ int button_context = style::CONTEXT_BUTTON);
~MenuButton() override;
MenuButtonController* button_controller() const {
diff --git a/chromium/ui/views/controls/button/menu_button_controller.cc b/chromium/ui/views/controls/button/menu_button_controller.cc
index 9c50cbfe8e1..70c3cc31f77 100644
--- a/chromium/ui/views/controls/button/menu_button_controller.cc
+++ b/chromium/ui/views/controls/button/menu_button_controller.cc
@@ -100,7 +100,7 @@ bool MenuButtonController::OnMousePressed(const ui::MouseEvent& event) {
if (button()->request_focus_on_press())
button()->RequestFocus();
- if (button()->state() != Button::STATE_DISABLED &&
+ if (button()->GetState() != Button::STATE_DISABLED &&
button()->HitTestPoint(event.location()) && IsTriggerableEvent(event)) {
return Activate(&event);
}
@@ -112,7 +112,7 @@ bool MenuButtonController::OnMousePressed(const ui::MouseEvent& event) {
}
void MenuButtonController::OnMouseReleased(const ui::MouseEvent& event) {
- if (button()->state() != Button::STATE_DISABLED &&
+ if (button()->GetState() != Button::STATE_DISABLED &&
delegate()->IsTriggerableEvent(event) &&
button()->HitTestPoint(event.location()) && !delegate()->InDrag()) {
Activate(&event);
@@ -180,35 +180,20 @@ void MenuButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen);
}
-void MenuButtonController::OnStateChanged(LabelButton::ButtonState old_state) {
- // State change occurs in IncrementPressedLocked() and
- // DecrementPressedLocked().
- if (pressed_lock_count_ != 0) {
- // The button's state was changed while it was supposed to be locked in a
- // pressed state. This shouldn't happen, but conceivably could if a caller
- // tries to switch from enabled to disabled or vice versa while the button
- // is pressed.
- if (button()->state() == Button::STATE_NORMAL)
- should_disable_after_press_ = false;
- else if (button()->state() == Button::STATE_DISABLED)
- should_disable_after_press_ = true;
- }
-}
-
bool MenuButtonController::IsTriggerableEvent(const ui::Event& event) {
return ButtonController::IsTriggerableEvent(event) &&
IsTriggerableEventType(event) && is_intentional_menu_trigger_;
}
void MenuButtonController::OnGestureEvent(ui::GestureEvent* event) {
- if (button()->state() != Button::STATE_DISABLED) {
+ if (button()->GetState() != Button::STATE_DISABLED) {
auto ref = weak_factory_.GetWeakPtr();
if (delegate()->IsTriggerableEvent(*event) && !Activate(event)) {
// When |Activate()| returns |false|, it means the click was handled by
// a button listener and has handled the gesture event. So, there is no
// need to further process the gesture event here. However, if the
// listener didn't run menu code, we should make sure to reset our state.
- if (ref && button()->state() == Button::STATE_HOVERED)
+ if (ref && button()->GetState() == Button::STATE_HOVERED)
button()->SetState(Button::STATE_NORMAL);
return;
@@ -217,7 +202,7 @@ void MenuButtonController::OnGestureEvent(ui::GestureEvent* event) {
event->SetHandled();
if (pressed_lock_count_ == 0)
button()->SetState(Button::STATE_HOVERED);
- } else if (button()->state() == Button::STATE_HOVERED &&
+ } else if (button()->GetState() == Button::STATE_HOVERED &&
(event->type() == ui::ET_GESTURE_TAP_CANCEL ||
event->type() == ui::ET_GESTURE_END) &&
pressed_lock_count_ == 0) {
@@ -308,9 +293,15 @@ void MenuButtonController::IncrementPressedLocked(
const ui::LocatedEvent* event) {
++pressed_lock_count_;
if (increment_pressed_lock_called_)
- *(increment_pressed_lock_called_) = true;
- should_disable_after_press_ = button()->state() == Button::STATE_DISABLED;
- if (button()->state() != Button::STATE_PRESSED) {
+ *increment_pressed_lock_called_ = true;
+ if (!state_changed_subscription_) {
+ state_changed_subscription_ =
+ button()->AddStateChangedCallback(base::BindRepeating(
+ &MenuButtonController::OnButtonStateChangedWhilePressedLocked,
+ base::Unretained(this)));
+ }
+ should_disable_after_press_ = button()->GetState() == Button::STATE_DISABLED;
+ if (button()->GetState() != Button::STATE_PRESSED) {
if (snap_ink_drop_to_activated)
delegate()->GetInkDrop()->SnapToActivated();
else
@@ -327,6 +318,7 @@ void MenuButtonController::DecrementPressedLocked() {
// If this was the last lock, manually reset state to the desired state.
if (pressed_lock_count_ == 0) {
menu_closed_time_ = TimeTicks::Now();
+ state_changed_subscription_.reset();
LabelButton::ButtonState desired_state = Button::STATE_NORMAL;
if (should_disable_after_press_) {
desired_state = Button::STATE_DISABLED;
@@ -340,9 +332,20 @@ void MenuButtonController::DecrementPressedLocked() {
button()->SetState(desired_state);
// The widget may be null during shutdown. If so, it doesn't make sense to
// try to add an ink drop effect.
- if (button()->GetWidget() && button()->state() != Button::STATE_PRESSED)
+ if (button()->GetWidget() && button()->GetState() != Button::STATE_PRESSED)
button()->AnimateInkDrop(InkDropState::DEACTIVATED, nullptr /* event */);
}
}
+void MenuButtonController::OnButtonStateChangedWhilePressedLocked() {
+ // The button's state was changed while it was supposed to be locked in a
+ // pressed state. This shouldn't happen, but conceivably could if a caller
+ // tries to switch from enabled to disabled or vice versa while the button is
+ // pressed.
+ if (button()->GetState() == Button::STATE_NORMAL)
+ should_disable_after_press_ = false;
+ else if (button()->GetState() == Button::STATE_DISABLED)
+ should_disable_after_press_ = true;
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/button/menu_button_controller.h b/chromium/ui/views/controls/button/menu_button_controller.h
index 35b8bf30a22..addaa2d3836 100644
--- a/chromium/ui/views/controls/button/menu_button_controller.h
+++ b/chromium/ui/views/controls/button/menu_button_controller.h
@@ -55,7 +55,6 @@ class VIEWS_EXPORT MenuButtonController : public ButtonController {
bool OnKeyReleased(const ui::KeyEvent& event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
void UpdateAccessibleNodeData(ui::AXNodeData* node_data) override;
- void OnStateChanged(Button::ButtonState old_state) override;
bool IsTriggerableEvent(const ui::Event& event) override;
// Calls TakeLock with is_sibling_menu_show as false and a nullptr to the
@@ -88,6 +87,9 @@ class VIEWS_EXPORT MenuButtonController : public ButtonController {
void DecrementPressedLocked();
+ // Called if the button state changes while pressed lock is engaged.
+ void OnButtonStateChangedWhilePressedLocked();
+
// Our listener. Not owned.
ButtonListener* const listener_;
@@ -113,6 +115,9 @@ class VIEWS_EXPORT MenuButtonController : public ButtonController {
// we programmatically show a menu on a disabled button.
bool should_disable_after_press_ = false;
+ // Subscribes to state changes on the button while pressed lock is engaged.
+ views::PropertyChangedSubscription state_changed_subscription_;
+
base::WeakPtrFactory<MenuButtonController> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MenuButtonController);
diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc
index e3e70954c85..f7c870780bb 100644
--- a/chromium/ui/views/controls/button/menu_button_unittest.cc
+++ b/chromium/ui/views/controls/button/menu_button_unittest.cc
@@ -26,6 +26,7 @@
#if defined(USE_AURA)
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_client_observer.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#endif
@@ -41,7 +42,7 @@ using test::TestInkDrop;
class TestMenuButton : public MenuButton {
public:
explicit TestMenuButton(ButtonListener* button_listener)
- : MenuButton(base::string16(ASCIIToUTF16("button")), button_listener) {}
+ : MenuButton(button_listener, base::string16(ASCIIToUTF16("button"))) {}
~TestMenuButton() override = default;
@@ -135,7 +136,7 @@ class TestButtonListener : public ButtonListener {
last_sender_ = sender;
Button* button = Button::AsButton(sender);
DCHECK(button);
- last_sender_state_ = button->state();
+ last_sender_state_ = button->GetState();
last_event_type_ = event.type();
}
@@ -146,7 +147,7 @@ class TestButtonListener : public ButtonListener {
}
Button* last_sender() { return last_sender_; }
- Button::ButtonState last_sender_state() { return last_sender_state_; }
+ Button::ButtonState last_sender_GetState() { return last_sender_state_; }
ui::EventType last_event_type() { return last_event_type_; }
private:
@@ -228,7 +229,7 @@ class TestDragDropClient : public aura::client::DragDropClient,
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
void AddObserver(aura::client::DragDropClientObserver* observer) override {}
@@ -258,7 +259,7 @@ int TestDragDropClient::StartDragAndDrop(
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
if (IsDragDropInProgress())
return ui::DragDropTypes::DRAG_NONE;
drag_in_progress_ = true;
@@ -300,7 +301,7 @@ class TestShowSiblingButtonListener : public ButtonListener {
// The MenuButton itself doesn't set the PRESSED state during Activate() or
// ButtonPressed(). That should be handled by the MenuController or,
// if no menu is shown, the listener.
- EXPECT_EQ(Button::STATE_HOVERED, source->state());
+ EXPECT_EQ(Button::STATE_HOVERED, source->GetState());
}
private:
@@ -318,7 +319,7 @@ TEST_F(MenuButtonTest, ActivateDropDownOnMouseClick) {
// Check that MenuButton has notified the listener, while it was in pressed
// state.
EXPECT_EQ(button(), button_listener.last_sender());
- EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state());
+ EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_GetState());
}
TEST_F(MenuButtonTest, ActivateOnKeyPress) {
@@ -369,7 +370,7 @@ TEST_F(MenuButtonTest, InkDropCenterSetFromClickWithPressedLock) {
MenuButtonController::PressedLock pressed_lock(button()->button_controller(),
false, &click_event);
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
EXPECT_EQ(
click_point,
InkDropHostViewTestApi(button()).GetInkDropCenterBasedOnLastEvent());
@@ -381,58 +382,58 @@ TEST_F(MenuButtonTest, ButtonStateForMenuButtonsWithPressedLocks) {
// Move the mouse over the button; the button should be in a hovered state.
generator()->MoveMouseTo(gfx::Point(10, 10));
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
// Introduce a PressedLock, which should make the button pressed.
std::unique_ptr<MenuButtonController::PressedLock> pressed_lock1(
new MenuButtonController::PressedLock(button()->button_controller()));
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
// Even if we move the mouse outside of the button, it should remain pressed.
generator()->MoveMouseTo(gfx::Point(300, 10));
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
// Creating a new lock should obviously keep the button pressed.
std::unique_ptr<MenuButtonController::PressedLock> pressed_lock2(
new MenuButtonController::PressedLock(button()->button_controller()));
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
// The button should remain pressed while any locks are active.
pressed_lock1.reset();
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
// Resetting the final lock should return the button's state to normal...
pressed_lock2.reset();
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
// ...And it should respond to mouse movement again.
generator()->MoveMouseTo(gfx::Point(10, 10));
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
// Test that the button returns to the appropriate state after the press; if
// the mouse ends over the button, the button should be hovered.
pressed_lock1 = button()->button_controller()->TakeLock();
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
pressed_lock1.reset();
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
// If the button is disabled before the pressed lock, it should be disabled
// after the pressed lock.
button()->SetState(Button::STATE_DISABLED);
pressed_lock1 = button()->button_controller()->TakeLock();
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
pressed_lock1.reset();
- EXPECT_EQ(Button::STATE_DISABLED, button()->state());
+ EXPECT_EQ(Button::STATE_DISABLED, button()->GetState());
generator()->MoveMouseTo(gfx::Point(300, 10));
// Edge case: the button is disabled, a pressed lock is added, and then the
// button is re-enabled. It should be enabled after the lock is removed.
pressed_lock1 = button()->button_controller()->TakeLock();
- EXPECT_EQ(Button::STATE_PRESSED, button()->state());
+ EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
button()->SetState(Button::STATE_NORMAL);
pressed_lock1.reset();
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
}
// Test that if a sibling menu is shown, the original menu button releases its
@@ -443,7 +444,7 @@ TEST_F(MenuButtonTest, PressedStateWithSiblingMenu) {
// Move the mouse over the button; the button should be in a hovered state.
generator()->MoveMouseTo(gfx::Point(10, 10));
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
generator()->ClickLeftButton();
// Test is continued in TestShowSiblingButtonListener::ButtonPressed().
}
@@ -461,7 +462,7 @@ TEST_F(MenuButtonTest, DraggableMenuButtonActivatesOnRelease) {
generator()->ReleaseLeftButton();
EXPECT_EQ(button(), button_listener.last_sender());
- EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state());
+ EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_GetState());
}
TEST_F(MenuButtonTest, InkDropStateForMenuButtonActivationsWithoutListener) {
@@ -570,14 +571,14 @@ TEST_F(MenuButtonTest, DraggableMenuButtonDoesNotActivateOnDrag) {
generator()->DragMouseBy(10, 0);
EXPECT_EQ(nullptr, button_listener.last_sender());
- EXPECT_EQ(Button::STATE_NORMAL, button_listener.last_sender_state());
+ EXPECT_EQ(Button::STATE_NORMAL, button_listener.last_sender_GetState());
button()->RemovePreTargetHandler(&drag_client);
}
#endif // USE_AURA
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
// Tests if the listener is notified correctly when a gesture tap happens on a
// MenuButton that has a ButtonListener.
@@ -595,10 +596,10 @@ TEST_F(MenuButtonTest, ActivateDropDownOnGestureTap) {
// Check that MenuButton has notified the listener, while it was in pressed
// state.
EXPECT_EQ(button(), button_listener.last_sender());
- EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state());
+ EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_GetState());
// The button should go back to it's normal state since the gesture ended.
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
}
// Tests that the button enters a hovered state upon a tap down, before becoming
@@ -607,10 +608,10 @@ TEST_F(MenuButtonTest, TouchFeedbackDuringTap) {
TestButtonListener button_listener;
CreateMenuButtonWithButtonListener(&button_listener);
generator()->PressTouch();
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
generator()->ReleaseTouch();
- EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_state());
+ EXPECT_EQ(Button::STATE_HOVERED, button_listener.last_sender_GetState());
}
// Tests that a move event that exits the button returns it to the normal state,
@@ -619,15 +620,15 @@ TEST_F(MenuButtonTest, TouchFeedbackDuringTapCancel) {
TestButtonListener button_listener;
CreateMenuButtonWithButtonListener(&button_listener);
generator()->PressTouch();
- EXPECT_EQ(Button::STATE_HOVERED, button()->state());
+ EXPECT_EQ(Button::STATE_HOVERED, button()->GetState());
generator()->MoveTouch(gfx::Point(10, 30));
generator()->ReleaseTouch();
- EXPECT_EQ(Button::STATE_NORMAL, button()->state());
+ EXPECT_EQ(Button::STATE_NORMAL, button()->GetState());
EXPECT_EQ(nullptr, button_listener.last_sender());
}
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
TEST_F(MenuButtonTest, InkDropHoverWhenShowingMenu) {
PressStateButtonListener button_listener(false);
@@ -674,7 +675,7 @@ TEST_F(MenuButtonTest,
class DestroyButtonInGestureListener : public ButtonListener {
public:
DestroyButtonInGestureListener() {
- menu_button_ = std::make_unique<MenuButton>(base::string16(), this);
+ menu_button_ = std::make_unique<MenuButton>();
}
~DestroyButtonInGestureListener() override = default;
diff --git a/chromium/ui/views/controls/button/radio_button.h b/chromium/ui/views/controls/button/radio_button.h
index 3bb6d4a5fe3..c3c61b5b343 100644
--- a/chromium/ui/views/controls/button/radio_button.h
+++ b/chromium/ui/views/controls/button/radio_button.h
@@ -18,7 +18,8 @@ class VIEWS_EXPORT RadioButton : public Checkbox {
public:
METADATA_HEADER(RadioButton);
- RadioButton(const base::string16& label, int group_id);
+ explicit RadioButton(const base::string16& label = base::string16(),
+ int group_id = 0);
~RadioButton() override;
// Overridden from View:
diff --git a/chromium/ui/views/controls/button/toggle_button.cc b/chromium/ui/views/controls/button/toggle_button.cc
index bf88433d005..0c840504a97 100644
--- a/chromium/ui/views/controls/button/toggle_button.cc
+++ b/chromium/ui/views/controls/button/toggle_button.cc
@@ -19,7 +19,6 @@
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_paint_util.h"
#include "ui/views/animation/ink_drop_impl.h"
-#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/animation/ink_drop_ripple.h"
#include "ui/views/border.h"
#include "ui/views/controls/highlight_path_generator.h"
@@ -330,10 +329,6 @@ std::unique_ptr<InkDrop> ToggleButton::CreateInkDrop() {
return std::move(ink_drop);
}
-std::unique_ptr<InkDropMask> ToggleButton::CreateInkDropMask() const {
- return nullptr;
-}
-
std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const {
gfx::Rect rect = thumb_view_->GetLocalBounds();
rect.Inset(-ThumbView::GetShadowOutsets());
diff --git a/chromium/ui/views/controls/button/toggle_button.h b/chromium/ui/views/controls/button/toggle_button.h
index 0106206f543..cd0cadc71e2 100644
--- a/chromium/ui/views/controls/button/toggle_button.h
+++ b/chromium/ui/views/controls/button/toggle_button.h
@@ -20,7 +20,7 @@ class VIEWS_EXPORT ToggleButton : public Button {
public:
METADATA_HEADER(ToggleButton);
- explicit ToggleButton(ButtonListener* listener);
+ explicit ToggleButton(ButtonListener* listener = nullptr);
~ToggleButton() override;
// AnimateIsOn() animates the state change to |is_on|; SetIsOn() doesn't.
@@ -72,7 +72,6 @@ class VIEWS_EXPORT ToggleButton : public Button {
void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
std::unique_ptr<InkDrop> CreateInkDrop() override;
- std::unique_ptr<InkDropMask> CreateInkDropMask() const override;
std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
SkColor GetInkDropBaseColor() const override;
diff --git a/chromium/ui/views/controls/button/toggle_button_unittest.cc b/chromium/ui/views/controls/button/toggle_button_unittest.cc
index 24e5d6a84b3..576ca185af0 100644
--- a/chromium/ui/views/controls/button/toggle_button_unittest.cc
+++ b/chromium/ui/views/controls/button/toggle_button_unittest.cc
@@ -19,8 +19,7 @@ namespace views {
class TestToggleButton : public ToggleButton {
public:
- explicit TestToggleButton(int* counter)
- : ToggleButton(nullptr), counter_(counter) {}
+ explicit TestToggleButton(int* counter) : counter_(counter) {}
~TestToggleButton() override {
// Calling SetInkDropMode() in this subclass allows this class's
// implementation of RemoveInkDropLayer() to be called. The same
diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc
index 72903d758de..0583ec98721 100644
--- a/chromium/ui/views/controls/combobox/combobox.cc
+++ b/chromium/ui/views/controls/combobox/combobox.cc
@@ -32,6 +32,7 @@
#include "ui/views/controls/button/button_controller.h"
#include "ui/views/controls/combobox/combobox_listener.h"
#include "ui/views/controls/combobox/combobox_util.h"
+#include "ui/views/controls/combobox/empty_combobox_model.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/menu/menu_config.h"
@@ -79,7 +80,7 @@ class TransparentButton : public Button {
~TransparentButton() override = default;
bool OnMousePressed(const ui::MouseEvent& mouse_event) override {
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// On Mac, comboboxes do not take focus on mouse click, but on other
// platforms they do.
parent()->RequestFocus();
@@ -111,7 +112,7 @@ class TransparentButton : public Button {
DISALLOW_COPY_AND_ASSIGN(TransparentButton);
};
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Returns the next or previous valid index (depending on |increment|'s value).
// Skips separator or disabled indices. Returns -1 if there is no valid adjacent
// index.
@@ -230,6 +231,9 @@ class Combobox::ComboboxMenuModel : public ui::MenuModel {
////////////////////////////////////////////////////////////////////////////////
// Combobox, public:
+Combobox::Combobox(int text_context, int text_style)
+ : Combobox(std::make_unique<internal::EmptyComboboxModel>()) {}
+
Combobox::Combobox(std::unique_ptr<ui::ComboboxModel> model,
int text_context,
int text_style)
@@ -238,18 +242,11 @@ Combobox::Combobox(std::unique_ptr<ui::ComboboxModel> model,
}
Combobox::Combobox(ui::ComboboxModel* model, int text_context, int text_style)
- : model_(model),
- text_context_(text_context),
+ : text_context_(text_context),
text_style_(text_style),
- listener_(nullptr),
- selected_index_(model_->GetDefaultIndex()),
- invalid_(false),
- menu_model_(new ComboboxMenuModel(this, model)),
- arrow_button_(new TransparentButton(this)),
- size_to_largest_label_(true) {
- observer_.Add(model_);
- OnComboboxModelChanged(model_);
-#if defined(OS_MACOSX)
+ arrow_button_(new TransparentButton(this)) {
+ SetModel(model);
+#if defined(OS_APPLE)
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
#else
SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -302,6 +299,28 @@ bool Combobox::SelectValue(const base::string16& value) {
return false;
}
+void Combobox::SetOwnedModel(std::unique_ptr<ui::ComboboxModel> model) {
+ // The swap keeps the outgoing model alive for SetModel().
+ owned_model_.swap(model);
+ SetModel(owned_model_.get());
+}
+
+void Combobox::SetModel(ui::ComboboxModel* model) {
+ DCHECK(model) << "After construction, the model must not be null.";
+
+ if (model_)
+ observer_.Remove(model_);
+
+ model_ = model;
+
+ if (model_) {
+ menu_model_ = std::make_unique<ComboboxMenuModel>(this, model_);
+ observer_.Add(model_);
+ selected_index_ = model_->GetDefaultIndex();
+ OnComboboxModelChanged(model_);
+ }
+}
+
void Combobox::SetTooltipText(const base::string16& tooltip_text) {
arrow_button_->SetTooltipText(tooltip_text);
if (accessible_name_.empty())
@@ -402,7 +421,7 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
bool show_menu = false;
int new_index = kNoSelection;
switch (e.key_code()) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
case ui::VKEY_DOWN:
case ui::VKEY_UP:
case ui::VKEY_SPACE:
@@ -448,7 +467,7 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
case ui::VKEY_SPACE:
show_menu = true;
break;
-#endif // OS_MACOSX
+#endif // OS_APPLE
default:
return false;
}
@@ -576,7 +595,9 @@ void Combobox::PaintIconAndText(gfx::Canvas* canvas) {
gfx::ImageSkia icon_skia =
GetImageSkiaFromImageModel(&icon, GetNativeTheme());
int icon_y = y + (contents_height - icon_skia.height()) / 2;
- canvas->DrawImageInt(icon_skia, x, icon_y);
+ gfx::Rect icon_bounds(x, icon_y, icon_skia.width(), icon_skia.height());
+ AdjustBoundsForRTLUI(&icon_bounds);
+ canvas->DrawImageInt(icon_skia, icon_bounds.x(), icon_bounds.y());
x += icon_skia.width() + LayoutProvider::Get()->GetDistanceMetric(
DISTANCE_RELATED_LABEL_HORIZONTAL);
}
@@ -622,7 +643,7 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
gfx::Rect bounds(menu_position, lb.size());
- Button::ButtonState original_state = arrow_button_->state();
+ Button::ButtonState original_state = arrow_button_->GetState();
arrow_button_->SetState(Button::STATE_PRESSED);
// Allow |menu_runner_| to be set by the testing API, but if this method is
diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h
index 91ad60c9a14..cd3a714bd0d 100644
--- a/chromium/ui/views/controls/combobox/combobox.h
+++ b/chromium/ui/views/controls/combobox/combobox.h
@@ -47,6 +47,10 @@ class VIEWS_EXPORT Combobox : public View,
static constexpr int kDefaultComboboxTextContext = style::CONTEXT_BUTTON;
static constexpr int kDefaultComboboxTextStyle = style::STYLE_PRIMARY;
+ // A combobox with an empty model.
+ explicit Combobox(int text_context = kDefaultComboboxTextContext,
+ int text_style = kDefaultComboboxTextStyle);
+
// |model| is owned by the combobox when using this constructor.
explicit Combobox(std::unique_ptr<ui::ComboboxModel> model,
int text_context = kDefaultComboboxTextContext,
@@ -70,6 +74,9 @@ class VIEWS_EXPORT Combobox : public View,
// the found index and returns true. Otherwise simply noops and returns false.
bool SelectValue(const base::string16& value);
+ void SetOwnedModel(std::unique_ptr<ui::ComboboxModel> model);
+ void SetModel(ui::ComboboxModel* model);
+
ui::ComboboxModel* model() const { return model_; }
// Set the tooltip text, and the accessible name if it is currently empty.
@@ -153,7 +160,7 @@ class VIEWS_EXPORT Combobox : public View,
std::unique_ptr<ui::ComboboxModel> owned_model_;
// Reference to our model, which may be owned or not.
- ui::ComboboxModel* model_;
+ ui::ComboboxModel* model_ = nullptr;
// Typography context for the text written in the combobox and the options
// shown in the drop-down menu.
@@ -164,13 +171,13 @@ class VIEWS_EXPORT Combobox : public View,
const int text_style_;
// Our listener. Not owned. Notified when the selected index change.
- ComboboxListener* listener_;
+ ComboboxListener* listener_ = nullptr;
// The current selected index; -1 and means no selection.
- int selected_index_;
+ int selected_index_ = -1;
// True when the selection is visually denoted as invalid.
- bool invalid_;
+ bool invalid_ = false;
// The accessible name of this combobox.
base::string16 accessible_name_;
@@ -204,7 +211,7 @@ class VIEWS_EXPORT Combobox : public View,
// When true, the size of contents is defined by the selected label.
// Otherwise, it's defined by the widest label in the menu. If this is set to
// true, the parent view must relayout in ChildPreferredSizeChanged().
- bool size_to_largest_label_;
+ bool size_to_largest_label_ = true;
// The focus ring for this Combobox.
FocusRing* focus_ring_ = nullptr;
diff --git a/chromium/ui/views/controls/combobox/combobox_unittest.cc b/chromium/ui/views/controls/combobox/combobox_unittest.cc
index b081a0b7408..1581b5f6b5f 100644
--- a/chromium/ui/views/controls/combobox/combobox_unittest.cc
+++ b/chromium/ui/views/controls/combobox/combobox_unittest.cc
@@ -11,10 +11,12 @@
#include <vector>
#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/models/combobox_model.h"
@@ -30,6 +32,7 @@
#include "ui/views/controls/combobox/combobox_listener.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/test/combobox_test_api.h"
+#include "ui/views/test/test_ax_event_observer.h"
#include "ui/views/test/view_metadata_test_utils.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/unique_widget_ptr.h"
@@ -280,7 +283,7 @@ class ComboboxTest : public ViewsTestBase {
DISALLOW_COPY_AND_ASSIGN(ComboboxTest);
};
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Tests whether the various Mac specific keyboard shortcuts invoke the dropdown
// menu or not.
TEST_F(ComboboxTest, KeyTestMac) {
@@ -354,7 +357,7 @@ TEST_F(ComboboxTest, DisabilityTest) {
// On Mac, key events can't change the currently selected index directly for a
// combobox.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Tests the behavior of various keyboard shortcuts on the currently selected
// index.
@@ -499,7 +502,7 @@ TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
PressKey(ui::VKEY_END);
EXPECT_EQ(6, combobox_->GetSelectedIndex());
}
-#endif // !OS_MACOSX
+#endif // !OS_APPLE
TEST_F(ComboboxTest, GetTextForRowTest) {
std::set<int> separators;
@@ -802,7 +805,7 @@ TEST_F(ComboboxTest, MenuModel) {
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
menu_model->GetTypeAt(kSeparatorIndex));
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Comboboxes on Mac should have checkmarks, with the selected item checked,
EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu_model->GetTypeAt(0));
EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu_model->GetTypeAt(1));
@@ -823,4 +826,122 @@ TEST_F(ComboboxTest, MenuModel) {
EXPECT_TRUE(menu_model->IsVisibleAt(0));
}
+// Verifies setting the tooltip text will call NotifyAccessibilityEvent.
+TEST_F(ComboboxTest, SetTooltipTextNotifiesAccessibilityEvent) {
+ InitCombobox(nullptr);
+ base::string16 test_tooltip_text = ASCIIToUTF16("Test Tooltip Text");
+ test::TestAXEventObserver observer;
+ EXPECT_EQ(0, observer.text_changed_event_count());
+ combobox_->SetTooltipText(test_tooltip_text);
+ EXPECT_EQ(1, observer.text_changed_event_count());
+ EXPECT_EQ(test_tooltip_text, combobox_->GetAccessibleName());
+ ui::AXNodeData data;
+ combobox_->GetAccessibleNodeData(&data);
+ const std::string& name =
+ data.GetStringAttribute(ax::mojom::StringAttribute::kName);
+ EXPECT_EQ(test_tooltip_text, ASCIIToUTF16(name));
+}
+
+namespace {
+
+using ComboboxDefaultTest = ViewsTestBase;
+
+class ConfigurableComboboxModel final : public ui::ComboboxModel {
+ public:
+ explicit ConfigurableComboboxModel(bool* destroyed = nullptr)
+ : destroyed_(destroyed) {
+ if (destroyed_)
+ *destroyed_ = false;
+ }
+ ConfigurableComboboxModel(ConfigurableComboboxModel&) = delete;
+ ConfigurableComboboxModel& operator=(const ConfigurableComboboxModel&) =
+ delete;
+ ~ConfigurableComboboxModel() override {
+ if (destroyed_)
+ *destroyed_ = true;
+ }
+
+ // ui::ComboboxModel:
+ int GetItemCount() const override { return item_count_; }
+ base::string16 GetItemAt(int index) const override {
+ DCHECK_LT(index, item_count_);
+ return base::NumberToString16(index);
+ }
+ int GetDefaultIndex() const override { return default_index_; }
+
+ void SetItemCount(int item_count) { item_count_ = item_count; }
+
+ void SetDefaultIndex(int default_index) { default_index_ = default_index; }
+
+ private:
+ bool* const destroyed_;
+ int item_count_ = 0;
+ int default_index_ = -1;
+};
+
+} // namespace
+
+TEST_F(ComboboxDefaultTest, Default) {
+ auto combobox = std::make_unique<Combobox>();
+ EXPECT_EQ(0, combobox->GetRowCount());
+ EXPECT_EQ(-1, combobox->GetSelectedRow());
+}
+
+TEST_F(ComboboxDefaultTest, SetModel) {
+ bool destroyed = false;
+ std::unique_ptr<ConfigurableComboboxModel> model =
+ std::make_unique<ConfigurableComboboxModel>(&destroyed);
+ model->SetItemCount(42);
+ model->SetDefaultIndex(27);
+ {
+ auto combobox = std::make_unique<Combobox>();
+ combobox->SetModel(model.get());
+ EXPECT_EQ(42, combobox->GetRowCount());
+ EXPECT_EQ(27, combobox->GetSelectedRow());
+ }
+ EXPECT_FALSE(destroyed);
+}
+
+TEST_F(ComboboxDefaultTest, SetOwnedModel) {
+ bool destroyed = false;
+ std::unique_ptr<ConfigurableComboboxModel> model =
+ std::make_unique<ConfigurableComboboxModel>(&destroyed);
+ model->SetItemCount(42);
+ model->SetDefaultIndex(27);
+ {
+ auto combobox = std::make_unique<Combobox>();
+ combobox->SetOwnedModel(std::move(model));
+ EXPECT_EQ(42, combobox->GetRowCount());
+ EXPECT_EQ(27, combobox->GetSelectedRow());
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(ComboboxDefaultTest, SetModelOverwriteOwned) {
+ bool destroyed = false;
+ std::unique_ptr<ConfigurableComboboxModel> model =
+ std::make_unique<ConfigurableComboboxModel>(&destroyed);
+ auto combobox = std::make_unique<Combobox>();
+ combobox->SetModel(model.get());
+ ASSERT_FALSE(destroyed);
+ combobox->SetOwnedModel(std::make_unique<ConfigurableComboboxModel>());
+ EXPECT_FALSE(destroyed);
+}
+
+TEST_F(ComboboxDefaultTest, SetOwnedModelOverwriteOwned) {
+ bool destroyed_first = false;
+ bool destroyed_second = false;
+ {
+ auto combobox = std::make_unique<Combobox>();
+ combobox->SetOwnedModel(
+ std::make_unique<ConfigurableComboboxModel>(&destroyed_first));
+ ASSERT_FALSE(destroyed_first);
+ combobox->SetOwnedModel(
+ std::make_unique<ConfigurableComboboxModel>(&destroyed_second));
+ EXPECT_TRUE(destroyed_first);
+ ASSERT_FALSE(destroyed_second);
+ }
+ EXPECT_TRUE(destroyed_second);
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/combobox/empty_combobox_model.cc b/chromium/ui/views/controls/combobox/empty_combobox_model.cc
new file mode 100644
index 00000000000..f6b9bdbbb70
--- /dev/null
+++ b/chromium/ui/views/controls/combobox/empty_combobox_model.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/combobox/empty_combobox_model.h"
+
+#include "base/notreached.h"
+#include "base/strings/string16.h"
+
+namespace views {
+namespace internal {
+
+EmptyComboboxModel::EmptyComboboxModel() = default;
+EmptyComboboxModel::~EmptyComboboxModel() = default;
+
+int EmptyComboboxModel::GetItemCount() const {
+ return 0;
+}
+
+base::string16 EmptyComboboxModel::GetItemAt(int index) const {
+ NOTREACHED();
+ return base::string16();
+}
+
+int EmptyComboboxModel::GetDefaultIndex() const {
+ return -1;
+}
+
+} // namespace internal
+} // namespace views
diff --git a/chromium/ui/views/controls/combobox/empty_combobox_model.h b/chromium/ui/views/controls/combobox/empty_combobox_model.h
new file mode 100644
index 00000000000..66793212d1d
--- /dev/null
+++ b/chromium/ui/views/controls/combobox/empty_combobox_model.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CONTROLS_COMBOBOX_EMPTY_COMBOBOX_MODEL_H_
+#define UI_VIEWS_CONTROLS_COMBOBOX_EMPTY_COMBOBOX_MODEL_H_
+
+#include "ui/base/models/combobox_model.h"
+
+namespace views {
+namespace internal {
+
+// An empty model for a combo box.
+class EmptyComboboxModel final : public ui::ComboboxModel {
+ public:
+ EmptyComboboxModel();
+ EmptyComboboxModel(EmptyComboboxModel&) = delete;
+ EmptyComboboxModel& operator=(const EmptyComboboxModel&) = delete;
+ ~EmptyComboboxModel() override;
+
+ // ui::ComboboxModel:
+ int GetItemCount() const override;
+ base::string16 GetItemAt(int index) const override;
+ int GetDefaultIndex() const override;
+};
+
+} // namespace internal
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_COMBOBOX_EMPTY_COMBOBOX_MODEL_H_
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
index b4c14a33505..6073e099014 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -5,7 +5,6 @@
#include "ui/views/controls/editable_combobox/editable_combobox.h"
#include <memory>
-#include <utility>
#include <vector>
#include "base/bind.h"
@@ -45,7 +44,7 @@
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/button_controller.h"
#include "ui/views/controls/combobox/combobox_util.h"
-#include "ui/views/controls/editable_combobox/editable_combobox_listener.h"
+#include "ui/views/controls/combobox/empty_combobox_model.h"
#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/menu_types.h"
@@ -303,6 +302,9 @@ class EditableCombobox::EditableComboboxPreTargetHandler
DISALLOW_COPY_AND_ASSIGN(EditableComboboxPreTargetHandler);
};
+EditableCombobox::EditableCombobox()
+ : EditableCombobox(std::make_unique<internal::EmptyComboboxModel>()) {}
+
EditableCombobox::EditableCombobox(
std::unique_ptr<ui::ComboboxModel> combobox_model,
const bool filter_on_edit,
@@ -312,16 +314,13 @@ EditableCombobox::EditableCombobox(
const int text_style,
const bool display_arrow)
: textfield_(new Textfield()),
- combobox_model_(std::move(combobox_model)),
- menu_model_(
- std::make_unique<EditableComboboxMenuModel>(this,
- combobox_model_.get(),
- filter_on_edit,
- show_on_empty)),
text_context_(text_context),
text_style_(text_style),
type_(type),
+ filter_on_edit_(filter_on_edit),
+ show_on_empty_(show_on_empty),
showing_password_text_(type != Type::kPassword) {
+ SetModel(std::move(combobox_model));
observer_.Add(textfield_);
textfield_->set_controller(this);
textfield_->SetFontList(GetFontList());
@@ -343,6 +342,13 @@ EditableCombobox::~EditableCombobox() {
textfield_->set_controller(nullptr);
}
+void EditableCombobox::SetModel(std::unique_ptr<ui::ComboboxModel> model) {
+ CloseMenu();
+ combobox_model_.swap(model);
+ menu_model_ = std::make_unique<EditableComboboxMenuModel>(
+ this, combobox_model_.get(), filter_on_edit_, show_on_empty_);
+}
+
const base::string16& EditableCombobox::GetText() const {
return textfield_->GetText();
}
@@ -475,15 +481,15 @@ void EditableCombobox::OnItemSelected(int index) {
void EditableCombobox::HandleNewContent(const base::string16& new_content) {
DCHECK(GetText() == new_content);
- // We notify |listener_| before updating |menu_model_|'s items shown. This
+ // We notify |callback_| before updating |menu_model_|'s items shown. This
// gives the user a chance to modify the ComboboxModel beforehand if they wish
// to do so.
// We disable UpdateItemsShown while we notify the listener in case it
// modifies the ComboboxModel, then calls OnComboboxModelChanged and thus
// UpdateItemsShown. That way UpdateItemsShown doesn't do its work twice.
- if (listener_) {
+ if (!content_changed_callback_.is_null()) {
menu_model_->DisableUpdateItemsShown();
- listener_->OnContentChanged(this);
+ content_changed_callback_.Run();
menu_model_->EnableUpdateItemsShown();
}
menu_model_->UpdateItemsShown();
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.h b/chromium/ui/views/controls/editable_combobox/editable_combobox.h
index e4a0d3c3698..fdd5882bab5 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox.h
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.h
@@ -6,7 +6,9 @@
#define UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_H_
#include <memory>
+#include <utility>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
@@ -32,7 +34,6 @@ class Event;
namespace views {
class EditableComboboxMenuModel;
-class EditableComboboxListener;
class EditableComboboxPreTargetHandler;
class MenuRunner;
class Textfield;
@@ -55,6 +56,8 @@ class VIEWS_EXPORT EditableCombobox
static constexpr int kDefaultTextContext = style::CONTEXT_BUTTON;
static constexpr int kDefaultTextStyle = style::STYLE_PRIMARY;
+ EditableCombobox();
+
// |combobox_model|: The ComboboxModel that gives us the items to show in the
// menu.
// |filter_on_edit|: Whether to only show the items that are case-insensitive
@@ -65,24 +68,25 @@ class VIEWS_EXPORT EditableCombobox
// |text_context| and |text_style|: Together these indicate the font to use.
// |display_arrow|: Whether to display an arrow in the combobox to indicate
// that there is a drop-down list.
- EditableCombobox(std::unique_ptr<ui::ComboboxModel> combobox_model,
- bool filter_on_edit,
- bool show_on_empty,
- Type type = Type::kRegular,
- int text_context = kDefaultTextContext,
- int text_style = kDefaultTextStyle,
- bool display_arrow = true);
+ explicit EditableCombobox(std::unique_ptr<ui::ComboboxModel> combobox_model,
+ bool filter_on_edit = false,
+ bool show_on_empty = true,
+ Type type = Type::kRegular,
+ int text_context = kDefaultTextContext,
+ int text_style = kDefaultTextStyle,
+ bool display_arrow = true);
~EditableCombobox() override;
+ void SetModel(std::unique_ptr<ui::ComboboxModel> model);
+
const base::string16& GetText() const;
void SetText(const base::string16& text);
const gfx::FontList& GetFontList() const;
- // Sets the listener that we will call when a selection is made.
- void set_listener(EditableComboboxListener* listener) {
- listener_ = listener;
+ void set_callback(base::RepeatingClosure callback) {
+ content_changed_callback_ = std::move(callback);
}
// Selects the specified logical text range for the textfield.
@@ -167,11 +171,16 @@ class VIEWS_EXPORT EditableCombobox
const Type type_;
+ // Whether to adapt the items shown to the textfield content.
+ bool filter_on_edit_;
+
+ // Whether to show options when the textfield is empty.
+ bool show_on_empty_;
+
// Set while the drop-down is showing.
std::unique_ptr<MenuRunner> menu_runner_;
- // Our listener. Not owned. Notified when the selected index changes.
- EditableComboboxListener* listener_ = nullptr;
+ base::RepeatingClosure content_changed_callback_;
// Whether we are currently showing the passwords for type
// Type::kPassword.
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox_listener.h b/chromium/ui/views/controls/editable_combobox/editable_combobox_listener.h
deleted file mode 100644
index e5545974a4c..00000000000
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox_listener.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_LISTENER_H_
-#define UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_LISTENER_H_
-
-#include "ui/views/views_export.h"
-
-namespace views {
-
-class EditableCombobox;
-
-// Interface used to notify consumers when something interesting happens to an
-// EditableCombobox.
-class VIEWS_EXPORT EditableComboboxListener {
- public:
- // Called when the content of the main textfield changes, either as the user
- // types or after selecting an option from the menu.
- virtual void OnContentChanged(EditableCombobox* editable_combobox) = 0;
-
- protected:
- virtual ~EditableComboboxListener() = default;
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_LISTENER_H_
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index 550aa40005b..5e56c1d8ce1 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -9,6 +9,7 @@
#include <utility>
#include <vector>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
@@ -28,7 +29,6 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/render_text.h"
#include "ui/views/context_menu_controller.h"
-#include "ui/views/controls/editable_combobox/editable_combobox_listener.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/test/menu_test_utils.h"
@@ -43,22 +43,6 @@ namespace {
using base::ASCIIToUTF16;
using views::test::WaitForMenuClosureAnimation;
-class DummyListener : public EditableComboboxListener {
- public:
- DummyListener() = default;
- ~DummyListener() override = default;
- void OnContentChanged(EditableCombobox* editable_combobox) override {
- change_count_++;
- }
-
- int change_count() const { return change_count_; }
-
- private:
- int change_count_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(DummyListener);
-};
-
// No-op test double of a ContextMenuController
class TestContextMenuController : public ContextMenuController {
public:
@@ -116,6 +100,9 @@ class EditableComboboxTest : public ViewsTestBase {
bool shift = false,
bool ctrl_cmd = false);
+ int change_count() const { return change_count_; }
+ void OnContentChanged() { ++change_count_; }
+
// The widget where the control will appear.
Widget* widget_ = nullptr;
@@ -128,8 +115,7 @@ class EditableComboboxTest : public ViewsTestBase {
// scenarios.
View* parent_of_combobox_ = nullptr;
- // Listener for our EditableCombobox.
- std::unique_ptr<DummyListener> listener_;
+ int change_count_ = 0;
std::unique_ptr<ui::test::EventGenerator> event_generator_;
@@ -168,8 +154,8 @@ void EditableComboboxTest::InitEditableCombobox(
combobox_ =
new EditableCombobox(std::make_unique<ui::SimpleComboboxModel>(items),
filter_on_edit, show_on_empty, type);
- listener_ = std::make_unique<DummyListener>();
- combobox_->set_listener(listener_.get());
+ combobox_->set_callback(base::BindRepeating(
+ &EditableComboboxTest::OnContentChanged, base::Unretained(this)));
combobox_->SetID(2);
dummy_focusable_view_ = new View();
dummy_focusable_view_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
@@ -195,7 +181,7 @@ void EditableComboboxTest::InitWidget() {
container->AddChildView(dummy_focusable_view_);
widget_->Show();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// The event loop needs to be flushed here, otherwise in various tests:
// 1. The actual showing of the native window backing the widget gets delayed
// until a spin of the event loop.
@@ -267,7 +253,7 @@ void EditableComboboxTest::SendKeyEvent(ui::KeyboardCode key_code,
const bool alt,
const bool shift,
const bool ctrl_cmd) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
bool command = ctrl_cmd;
bool control = false;
#else
@@ -404,7 +390,7 @@ TEST_F(EditableComboboxTest, EndOrHomeMovesToBeginningOrEndOfText) {
EXPECT_EQ(ASCIIToUTF16("xabcy"), combobox_->GetText());
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
TEST_F(EditableComboboxTest, AltLeftOrRightMovesToNextWords) {
InitEditableCombobox();
@@ -714,30 +700,30 @@ TEST_F(EditableComboboxTest, DontShowOnEmpty) {
ASSERT_EQ(ASCIIToUTF16("item1"), combobox_->GetItemForTest(1));
}
-TEST_F(EditableComboboxTest, NoFilteringNotifiesListener) {
+TEST_F(EditableComboboxTest, NoFilteringNotifiesCallback) {
std::vector<base::string16> items = {ASCIIToUTF16("item0"),
ASCIIToUTF16("item1")};
InitEditableCombobox(items, /*filter_on_edit=*/false, /*show_on_empty=*/true);
- ASSERT_EQ(0, listener_->change_count());
+ ASSERT_EQ(0, change_count());
combobox_->SetText(ASCIIToUTF16("a"));
- ASSERT_EQ(1, listener_->change_count());
+ ASSERT_EQ(1, change_count());
combobox_->SetText(ASCIIToUTF16("ab"));
- ASSERT_EQ(2, listener_->change_count());
+ ASSERT_EQ(2, change_count());
}
-TEST_F(EditableComboboxTest, FilteringNotifiesListener) {
+TEST_F(EditableComboboxTest, FilteringNotifiesCallback) {
std::vector<base::string16> items = {ASCIIToUTF16("item0"),
ASCIIToUTF16("item1")};
InitEditableCombobox(items, /*filter_on_edit=*/true, /*show_on_empty=*/true);
- ASSERT_EQ(0, listener_->change_count());
+ ASSERT_EQ(0, change_count());
combobox_->SetText(ASCIIToUTF16("i"));
- ASSERT_EQ(1, listener_->change_count());
+ ASSERT_EQ(1, change_count());
combobox_->SetText(ASCIIToUTF16("ix"));
- ASSERT_EQ(2, listener_->change_count());
+ ASSERT_EQ(2, change_count());
combobox_->SetText(ASCIIToUTF16("ixy"));
- ASSERT_EQ(3, listener_->change_count());
+ ASSERT_EQ(3, change_count());
}
TEST_F(EditableComboboxTest, PasswordCanBeHiddenAndRevealed) {
@@ -843,5 +829,67 @@ TEST_F(EditableComboboxTest, NoCrashWithoutWidget) {
combobox->RevealPasswords(true);
}
+using EditableComboboxDefaultTest = ViewsTestBase;
+
+class ConfigurableComboboxModel final : public ui::ComboboxModel {
+ public:
+ explicit ConfigurableComboboxModel(bool* destroyed = nullptr)
+ : destroyed_(destroyed) {
+ if (destroyed_)
+ *destroyed_ = false;
+ }
+ ConfigurableComboboxModel(ConfigurableComboboxModel&) = delete;
+ ConfigurableComboboxModel& operator=(const ConfigurableComboboxModel&) =
+ delete;
+ ~ConfigurableComboboxModel() override {
+ if (destroyed_)
+ *destroyed_ = true;
+ }
+
+ // ui::ComboboxModel:
+ int GetItemCount() const override { return item_count_; }
+ base::string16 GetItemAt(int index) const override {
+ DCHECK_LT(index, item_count_);
+ return base::NumberToString16(index);
+ }
+
+ void SetItemCount(int item_count) { item_count_ = item_count; }
+
+ private:
+ bool* const destroyed_;
+ int item_count_ = 0;
+};
+
} // namespace
+
+TEST_F(EditableComboboxDefaultTest, Default) {
+ auto combobox = std::make_unique<EditableCombobox>();
+ EXPECT_EQ(0, combobox->GetItemCountForTest());
+}
+
+TEST_F(EditableComboboxDefaultTest, SetModel) {
+ std::unique_ptr<ConfigurableComboboxModel> model =
+ std::make_unique<ConfigurableComboboxModel>();
+ model->SetItemCount(42);
+ auto combobox = std::make_unique<EditableCombobox>();
+ combobox->SetModel(std::move(model));
+ EXPECT_EQ(42, combobox->GetItemCountForTest());
+}
+
+TEST_F(EditableComboboxDefaultTest, SetModelOverwrite) {
+ bool destroyed_first = false;
+ bool destroyed_second = false;
+ {
+ auto combobox = std::make_unique<EditableCombobox>();
+ combobox->SetModel(
+ std::make_unique<ConfigurableComboboxModel>(&destroyed_first));
+ ASSERT_FALSE(destroyed_first);
+ combobox->SetModel(
+ std::make_unique<ConfigurableComboboxModel>(&destroyed_second));
+ EXPECT_TRUE(destroyed_first);
+ ASSERT_FALSE(destroyed_second);
+ }
+ EXPECT_TRUE(destroyed_second);
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/focusable_border.cc b/chromium/ui/views/controls/focusable_border.cc
index bb8f04bfdbb..d71c9e1f839 100644
--- a/chromium/ui/views/controls/focusable_border.cc
+++ b/chromium/ui/views/controls/focusable_border.cc
@@ -10,7 +10,6 @@
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_util.h"
#include "ui/native_theme/native_theme.h"
diff --git a/chromium/ui/views/controls/highlight_path_generator.cc b/chromium/ui/views/controls/highlight_path_generator.cc
index 01920dd775d..7899b1affc0 100644
--- a/chromium/ui/views/controls/highlight_path_generator.cc
+++ b/chromium/ui/views/controls/highlight_path_generator.cc
@@ -51,8 +51,11 @@ base::Optional<gfx::RRectF> HighlightPathGenerator::GetRoundRect(
base::Optional<gfx::RRectF> HighlightPathGenerator::GetRoundRect(
const View* view) {
- gfx::Rect bounds(view->GetLocalBounds());
+ gfx::Rect bounds =
+ use_contents_bounds_ ? view->GetContentsBounds() : view->GetLocalBounds();
bounds.Inset(insets_);
+ if (use_mirrored_rect_)
+ bounds = view->GetMirroredRect(bounds);
return GetRoundRect(gfx::RectF(bounds));
}
@@ -99,13 +102,11 @@ void InstallCircleHighlightPathGenerator(View* view,
view, std::make_unique<CircleHighlightPathGenerator>(insets));
}
-SkPath PillHighlightPathGenerator::GetHighlightPath(const View* view) {
- const SkRect rect = gfx::RectToSkRect(view->GetLocalBounds());
- const SkScalar corner_radius =
- SkScalarHalf(std::min(rect.width(), rect.height()));
-
- return SkPath().addRoundRect(gfx::RectToSkRect(view->GetLocalBounds()),
- corner_radius, corner_radius);
+base::Optional<gfx::RRectF> PillHighlightPathGenerator::GetRoundRect(
+ const gfx::RectF& rect) {
+ gfx::RectF bounds = rect;
+ const float corner_radius = std::min(bounds.width(), bounds.height()) / 2.f;
+ return gfx::RRectF(bounds, corner_radius);
}
void InstallPillHighlightPathGenerator(View* view) {
diff --git a/chromium/ui/views/controls/highlight_path_generator.h b/chromium/ui/views/controls/highlight_path_generator.h
index e64d0d48b1b..f5415715209 100644
--- a/chromium/ui/views/controls/highlight_path_generator.h
+++ b/chromium/ui/views/controls/highlight_path_generator.h
@@ -26,8 +26,6 @@ class View;
// effects.
class VIEWS_EXPORT HighlightPathGenerator {
public:
- // TODO(http://crbug.com/1056490): Remove this constructor in favor of the one
- // that takes |insets|.
HighlightPathGenerator();
explicit HighlightPathGenerator(const gfx::Insets& insets);
virtual ~HighlightPathGenerator();
@@ -50,8 +48,27 @@ class VIEWS_EXPORT HighlightPathGenerator {
virtual base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect);
base::Optional<gfx::RRectF> GetRoundRect(const View* view);
+ void set_use_contents_bounds(bool use_contents_bounds) {
+ use_contents_bounds_ = use_contents_bounds;
+ }
+
+ void set_use_mirrored_rect(bool use_mirrored_rect) {
+ use_mirrored_rect_ = use_mirrored_rect;
+ }
+
private:
const gfx::Insets insets_;
+
+ // When set uses the view's content bounds instead of its local bounds.
+ // TODO(http://crbug.com/1056490): Investigate removing this and seeing if all
+ // ink drops / focus rings should use the content bounds.
+ bool use_contents_bounds_ = false;
+
+ // When set uses the mirror rect in RTL. This should not be needed for focus
+ // rings paths as they handle RTL themselves.
+ // TODO(http://crbug.com/1056490): Investigate moving FocusRing RTL to this
+ // class and removing this bool.
+ bool use_mirrored_rect_ = false;
};
// Sets a highlight path that is empty. This is used for ink drops that want to
@@ -114,15 +131,11 @@ class VIEWS_EXPORT PillHighlightPathGenerator : public HighlightPathGenerator {
delete;
// HighlightPathGenerator:
- SkPath GetHighlightPath(const View* view) override;
+ base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override;
};
void VIEWS_EXPORT InstallPillHighlightPathGenerator(View* view);
-// TODO(http://crbug.com/1056490): Investigate if we can make |radius| optional
-// for FixedSizeCircleHighlightPathGenerator and
-// RoundRectHighlightPathGenerator, and combine them with
-// CircleHighlightPathGenerator and PillHighlightPathGenerator respectively.
// Sets a centered fixed-size circular highlight path.
class VIEWS_EXPORT FixedSizeCircleHighlightPathGenerator
: public HighlightPathGenerator {
diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc
index 4ca005dbdc9..840bb74c64e 100644
--- a/chromium/ui/views/controls/image_view.cc
+++ b/chromium/ui/views/controls/image_view.cc
@@ -99,6 +99,7 @@ void ImageView::SetAccessibleName(const base::string16& accessible_name) {
accessible_name_ = accessible_name;
OnPropertyChanged(&accessible_name_, kPropertyEffectsNone);
+ NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
}
const base::string16& ImageView::GetAccessibleName() const {
diff --git a/chromium/ui/views/controls/image_view_unittest.cc b/chromium/ui/views/controls/image_view_unittest.cc
index f6b9ee1c3ba..e5d94793918 100644
--- a/chromium/ui/views/controls/image_view_unittest.cc
+++ b/chromium/ui/views/controls/image_view_unittest.cc
@@ -5,18 +5,22 @@
#include "ui/views/controls/image_view.h"
#include <memory>
+#include <string>
#include <utility>
#include "base/i18n/rtl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/border.h"
#include "ui/views/layout/box_layout.h"
+#include "ui/views/test/test_ax_event_observer.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
@@ -144,6 +148,21 @@ TEST_P(ImageViewTest, ImageOriginForCustomViewBounds) {
EXPECT_EQ(image_view_bounds, image_view()->bounds());
}
+// Verifies setting the accessible name will be call NotifyAccessibilityEvent.
+TEST_P(ImageViewTest, SetAccessibleNameNotifiesAccessibilityEvent) {
+ base::string16 test_tooltip_text = base::ASCIIToUTF16("Test Tooltip Text");
+ test::TestAXEventObserver observer;
+ EXPECT_EQ(0, observer.text_changed_event_count());
+ image_view()->SetAccessibleName(test_tooltip_text);
+ EXPECT_EQ(1, observer.text_changed_event_count());
+ EXPECT_EQ(test_tooltip_text, image_view()->GetAccessibleName());
+ ui::AXNodeData data;
+ image_view()->GetAccessibleNodeData(&data);
+ const std::string& name =
+ data.GetStringAttribute(ax::mojom::StringAttribute::kName);
+ EXPECT_EQ(test_tooltip_text, base::ASCIIToUTF16(name));
+}
+
INSTANTIATE_TEST_SUITE_P(All,
ImageViewTest,
::testing::Values(Axis::kHorizontal, Axis::kVertical));
diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc
index febce15ff86..d0b00369f1a 100644
--- a/chromium/ui/views/controls/label.cc
+++ b/chromium/ui/views/controls/label.cc
@@ -70,7 +70,6 @@ Label::Label(const base::string16& text,
text_style_(text_style),
context_menu_contents_(this) {
Init(text, style::GetFont(text_context, text_style), directionality_mode);
- SetLineHeight(style::GetLineHeight(text_context, text_style));
}
Label::Label(const base::string16& text, const CustomFont& font)
@@ -89,7 +88,8 @@ const gfx::FontList& Label::GetDefaultFontList() {
void Label::SetFontList(const gfx::FontList& font_list) {
full_text_->SetFontList(font_list);
- ResetLayout();
+ ClearDisplayText();
+ PreferredSizeChanged();
}
const base::string16& Label::GetText() const {
@@ -100,7 +100,9 @@ void Label::SetText(const base::string16& new_text) {
if (new_text == GetText())
return;
full_text_->SetText(new_text);
- OnPropertyChanged(&full_text_ + kLabelText, kPropertyEffectsLayout);
+ ClearDisplayText();
+ OnPropertyChanged(&full_text_ + kLabelText,
+ kPropertyEffectsPreferredSizeChanged);
stored_selection_range_ = gfx::Range::InvalidRange();
}
@@ -117,6 +119,8 @@ void Label::SetTextStyle(int style) {
return;
text_style_ = style;
+ // TODO(pkasting): Seems like potentially |full_text_|'s font list and line
+ // height should be updated here?
UpdateColorsFromTheme();
OnPropertyChanged(&text_style_, kPropertyEffectsPreferredSizeChanged);
}
@@ -130,6 +134,7 @@ void Label::SetAutoColorReadabilityEnabled(
if (auto_color_readability_enabled_ == auto_color_readability_enabled)
return;
auto_color_readability_enabled_ = auto_color_readability_enabled;
+ RecalculateColors();
OnPropertyChanged(&auto_color_readability_enabled_, kPropertyEffectsPaint);
}
@@ -142,6 +147,7 @@ void Label::SetEnabledColor(SkColor color) {
return;
requested_enabled_color_ = color;
enabled_color_set_ = true;
+ RecalculateColors();
OnPropertyChanged(&requested_enabled_color_, kPropertyEffectsPaint);
}
@@ -154,6 +160,7 @@ void Label::SetBackgroundColor(SkColor color) {
return;
background_color_ = color;
background_color_set_ = true;
+ RecalculateColors();
OnPropertyChanged(&background_color_, kPropertyEffectsPaint);
}
@@ -166,6 +173,7 @@ void Label::SetSelectionTextColor(SkColor color) {
return;
requested_selection_text_color_ = color;
selection_text_color_set_ = true;
+ RecalculateColors();
OnPropertyChanged(&requested_selection_text_color_, kPropertyEffectsPaint);
}
@@ -178,6 +186,7 @@ void Label::SetSelectionBackgroundColor(SkColor color) {
return;
selection_background_color_ = color;
selection_background_color_set_ = true;
+ RecalculateColors();
OnPropertyChanged(&selection_background_color_, kPropertyEffectsPaint);
}
@@ -189,7 +198,9 @@ void Label::SetShadows(const gfx::ShadowValues& shadows) {
if (full_text_->shadows() == shadows)
return;
full_text_->set_shadows(shadows);
- OnPropertyChanged(&full_text_ + kLabelShadows, kPropertyEffectsLayout);
+ ClearDisplayText();
+ OnPropertyChanged(&full_text_ + kLabelShadows,
+ kPropertyEffectsPreferredSizeChanged);
}
bool Label::GetSubpixelRenderingEnabled() const {
@@ -200,6 +211,7 @@ void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) {
if (subpixel_rendering_enabled_ == subpixel_rendering_enabled)
return;
subpixel_rendering_enabled_ = subpixel_rendering_enabled;
+ ApplyTextColors();
OnPropertyChanged(&subpixel_rendering_enabled_, kPropertyEffectsPaint);
}
@@ -212,8 +224,9 @@ void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
if (GetHorizontalAlignment() == alignment)
return;
full_text_->SetHorizontalAlignment(alignment);
+ ClearDisplayText();
OnPropertyChanged(&full_text_ + kLabelHorizontalAlignment,
- kPropertyEffectsLayout);
+ kPropertyEffectsPaint);
}
gfx::VerticalAlignment Label::GetVerticalAlignment() const {
@@ -224,20 +237,27 @@ void Label::SetVerticalAlignment(gfx::VerticalAlignment alignment) {
if (GetVerticalAlignment() == alignment)
return;
full_text_->SetVerticalAlignment(alignment);
- // TODO(dfried): consider if this should be kPropertyEffectsPaint instead.
+ ClearDisplayText();
OnPropertyChanged(&full_text_ + kLabelVerticalAlignment,
- kPropertyEffectsLayout);
+ kPropertyEffectsPaint);
}
int Label::GetLineHeight() const {
- return full_text_->min_line_height();
+ // TODO(pkasting): If we can replace SetFontList() with context/style setter
+ // calls, we can eliminate the reference to font_list().GetHeight() here.
+ return line_height_.value_or(
+ std::max(style::GetLineHeight(text_context_, text_style_),
+ font_list().GetHeight()));
}
-void Label::SetLineHeight(int height) {
- if (GetLineHeight() == height)
+void Label::SetLineHeight(int line_height) {
+ if (line_height_ == line_height)
return;
- full_text_->SetMinLineHeight(height);
- OnPropertyChanged(&full_text_ + kLabelLineHeight, kPropertyEffectsLayout);
+ line_height_ = line_height;
+ full_text_->SetMinLineHeight(line_height);
+ ClearDisplayText();
+ OnPropertyChanged(&full_text_ + kLabelLineHeight,
+ kPropertyEffectsPreferredSizeChanged);
}
bool Label::GetMultiLine() const {
@@ -251,7 +271,8 @@ void Label::SetMultiLine(bool multi_line) {
return;
multi_line_ = multi_line;
full_text_->SetMultiline(multi_line);
- OnPropertyChanged(&multi_line_, kPropertyEffectsLayout);
+ ClearDisplayText();
+ OnPropertyChanged(&multi_line_, kPropertyEffectsPreferredSizeChanged);
}
int Label::GetMaxLines() const {
@@ -262,7 +283,7 @@ void Label::SetMaxLines(int max_lines) {
if (max_lines_ == max_lines)
return;
max_lines_ = max_lines;
- OnPropertyChanged(&max_lines_, kPropertyEffectsLayout);
+ OnPropertyChanged(&max_lines_, kPropertyEffectsPreferredSizeChanged);
}
bool Label::GetObscured() const {
@@ -273,9 +294,11 @@ void Label::SetObscured(bool obscured) {
if (this->GetObscured() == obscured)
return;
full_text_->SetObscured(obscured);
+ ClearDisplayText();
if (obscured)
SetSelectable(false);
- OnPropertyChanged(&full_text_ + kLabelObscured, kPropertyEffectsLayout);
+ OnPropertyChanged(&full_text_ + kLabelObscured,
+ kPropertyEffectsPreferredSizeChanged);
}
bool Label::IsDisplayTextTruncated() const {
@@ -290,8 +313,7 @@ bool Label::IsDisplayTextTruncated() const {
}
bool Label::GetAllowCharacterBreak() const {
- return full_text_->word_wrap_behavior() == gfx::WRAP_LONG_WORDS ? true
- : false;
+ return full_text_->word_wrap_behavior() == gfx::WRAP_LONG_WORDS;
}
void Label::SetAllowCharacterBreak(bool allow_character_break) {
@@ -300,8 +322,9 @@ void Label::SetAllowCharacterBreak(bool allow_character_break) {
if (full_text_->word_wrap_behavior() == behavior)
return;
full_text_->SetWordWrapBehavior(behavior);
+ ClearDisplayText();
OnPropertyChanged(&full_text_ + kLabelAllowCharacterBreak,
- kPropertyEffectsLayout);
+ kPropertyEffectsPreferredSizeChanged);
}
size_t Label::GetTextIndexOfLine(size_t line) const {
@@ -322,7 +345,8 @@ void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
if (elide_behavior_ == elide_behavior)
return;
elide_behavior_ = elide_behavior;
- OnPropertyChanged(&elide_behavior_, kPropertyEffectsLayout);
+ ClearDisplayText();
+ OnPropertyChanged(&elide_behavior_, kPropertyEffectsPreferredSizeChanged);
}
base::string16 Label::GetTooltipText() const {
@@ -365,7 +389,7 @@ void Label::SetMaximumWidth(int max_width) {
if (max_width_ == max_width)
return;
max_width_ = max_width;
- OnPropertyChanged(&max_width_, kPropertyEffectsLayout);
+ OnPropertyChanged(&max_width_, kPropertyEffectsPreferredSizeChanged);
}
bool Label::GetCollapseWhenHidden() const {
@@ -385,7 +409,6 @@ size_t Label::GetRequiredLines() const {
}
base::string16 Label::GetDisplayTextForTesting() {
- ClearDisplayText();
MaybeBuildDisplayText();
return display_text_ ? display_text_->GetDisplayText() : base::string16();
}
@@ -487,7 +510,7 @@ gfx::Size Label::GetMinimumSize() const {
return gfx::Size();
// Always reserve vertical space for at least one line.
- gfx::Size size(0, std::max(font_list().GetHeight(), GetLineHeight()));
+ gfx::Size size(0, GetLineHeight());
if (elide_behavior_ == gfx::ELIDE_HEAD ||
elide_behavior_ == gfx::ELIDE_MIDDLE ||
elide_behavior_ == gfx::ELIDE_TAIL ||
@@ -517,7 +540,7 @@ int Label::GetHeightForWidth(int w) const {
w -= GetInsets().width();
int height = 0;
- int base_line_height = std::max(GetLineHeight(), font_list().GetHeight());
+ int base_line_height = GetLineHeight();
if (!GetMultiLine() || GetText().empty() || w <= 0) {
height = base_line_height;
} else {
@@ -576,15 +599,6 @@ base::string16 Label::GetTooltipText(const gfx::Point& p) const {
return base::string16();
}
-void Label::OnHandlePropertyChangeEffects(PropertyEffects property_effects) {
- if (property_effects & kPropertyEffectsPreferredSizeChanged)
- SizeToPreferredSize();
- if (property_effects & kPropertyEffectsLayout)
- ResetLayout();
- if (property_effects & kPropertyEffectsPaint)
- RecalculateColors();
-}
-
std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const {
// Multi-line labels only support NO_ELIDE and ELIDE_TAIL for now.
// TODO(warx): Investigate more elide text support.
@@ -717,8 +731,9 @@ bool Label::OnMousePressed(const ui::MouseEvent& event) {
return selection_controller_->OnMousePressed(
event, false,
- had_focus ? SelectionController::FOCUSED
- : SelectionController::UNFOCUSED);
+ had_focus
+ ? SelectionController::InitialFocusStateOnMousePress::kFocused
+ : SelectionController::InitialFocusStateOnMousePress::kUnFocused);
}
bool Label::OnMouseDragged(const ui::MouseEvent& event) {
@@ -804,7 +819,7 @@ void Label::OnDeviceScaleFactorChanged(float old_device_scale_factor,
// When the device scale factor is changed, some font rendering parameters is
// changed (especially, hinting). The bounding box of the text has to be
// re-computed based on the new parameters. See crbug.com/441439
- ResetLayout();
+ PreferredSizeChanged();
}
void Label::VisibilityChanged(View* starting_from, bool is_visible) {
@@ -963,13 +978,14 @@ void Label::Init(const base::string16& text,
gfx::DirectionalityMode directionality_mode) {
full_text_ = gfx::RenderText::CreateRenderText();
full_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- full_text_->SetDirectionalityMode(directionality_mode);
- // NOTE: |full_text_| should not be elided at all. This is used to keep
- // some properties and to compute the size of the string.
- full_text_->SetElideBehavior(gfx::NO_ELIDE);
full_text_->SetFontList(font_list);
full_text_->SetCursorEnabled(false);
full_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS);
+ full_text_->SetMinLineHeight(GetLineHeight());
+ // NOTE: |full_text_| should not be elided at all. This is used to keep
+ // some properties and to compute the size of the string.
+ full_text_->SetElideBehavior(gfx::NO_ELIDE);
+ full_text_->SetDirectionalityMode(directionality_mode);
SetText(text);
@@ -983,12 +999,6 @@ void Label::Init(const base::string16& text,
AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
}
-void Label::ResetLayout() {
- PreferredSizeChanged();
- SchedulePaint();
- ClearDisplayText();
-}
-
void Label::MaybeBuildDisplayText() const {
if (display_text_)
return;
@@ -1007,7 +1017,7 @@ void Label::MaybeBuildDisplayText() const {
gfx::Size Label::GetTextSize() const {
gfx::Size size;
if (GetText().empty()) {
- size = gfx::Size(0, std::max(GetLineHeight(), font_list().GetHeight()));
+ size = gfx::Size(0, GetLineHeight());
} else {
// Cancel the display rect of |full_text_|. The display rect may be
// specified in GetHeightForWidth(), and specifying empty Rect cancels
@@ -1087,7 +1097,7 @@ bool Label::ShouldShowDefaultTooltip() const {
(GetMultiLine() && text_size.height() > size.height()));
}
-void Label::ClearDisplayText() const {
+void Label::ClearDisplayText() {
// The HasSelection() call below will build |display_text_| in case it is
// empty. Return early to avoid this.
if (!display_text_)
@@ -1099,6 +1109,8 @@ void Label::ClearDisplayText() const {
GetRenderTextForSelectionController()->selection();
}
display_text_ = nullptr;
+
+ SchedulePaint();
}
base::string16 Label::GetSelectedText() const {
diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h
index 5a45ef3cbf0..6cb286abd74 100644
--- a/chromium/ui/views/controls/label.h
+++ b/chromium/ui/views/controls/label.h
@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/render_text.h"
@@ -150,10 +151,9 @@ class VIEWS_EXPORT Label : public View,
void SetVerticalAlignment(gfx::VerticalAlignment alignment);
// Get or set the distance in pixels between baselines of multi-line text.
- // Default is 0, indicating the distance between lines should be the standard
- // one for the label's text, font list, and platform.
+ // Default is the height of the default font.
int GetLineHeight() const;
- void SetLineHeight(int height);
+ void SetLineHeight(int line_height);
// Get or set if the label text can wrap on multiple lines; default is false.
bool GetMultiLine() const;
@@ -186,7 +186,10 @@ class VIEWS_EXPORT Label : public View,
// returns the text position of the first character of that line.
size_t GetTextIndexOfLine(size_t line) const;
- // Set the truncate length of the rendered text.
+ // Set the truncate length of the |full_text_|.
+ // NOTE: This does not affect the |display_text_|, since right now the only
+ // consumer does not need that; if you need this function, you may need to
+ // implement this.
void SetTruncateLength(size_t truncate_length);
// Gets/Sets the eliding or fading behavior, applied as necessary. The default
@@ -261,7 +264,7 @@ class VIEWS_EXPORT Label : public View,
void SelectRange(const gfx::Range& range);
views::PropertyChangedSubscription AddTextChangedCallback(
- views::PropertyChangedCallback callback);
+ views::PropertyChangedCallback callback) WARN_UNUSED_RESULT;
// View:
int GetBaseline() const override;
@@ -273,7 +276,6 @@ class VIEWS_EXPORT Label : public View,
WordLookupClient* GetWordLookupClient() override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
base::string16 GetTooltipText(const gfx::Point& p) const override;
- void OnHandlePropertyChangeEffects(PropertyEffects property_effects) override;
protected:
// Create a single RenderText instance to actually be painted.
@@ -353,8 +355,6 @@ class VIEWS_EXPORT Label : public View,
const gfx::FontList& font_list,
gfx::DirectionalityMode directionality_mode);
- void ResetLayout();
-
// Set up |display_text_| to actually be painted.
void MaybeBuildDisplayText() const;
@@ -377,7 +377,10 @@ class VIEWS_EXPORT Label : public View,
bool ShouldShowDefaultTooltip() const;
// Clears |display_text_| and updates |stored_selection_range_|.
- void ClearDisplayText() const;
+ // TODO(crbug.com/1103804) Most uses of this function are inefficient; either
+ // replace with setting attributes on both RenderTexts or collapse them to one
+ // RenderText.
+ void ClearDisplayText();
// Returns the currently selected text.
base::string16 GetSelectedText() const;
@@ -390,6 +393,7 @@ class VIEWS_EXPORT Label : public View,
const int text_context_;
int text_style_;
+ base::Optional<int> line_height_;
// An un-elided and single-line RenderText object used for preferred sizing.
std::unique_ptr<gfx::RenderText> full_text_;
diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc
index 33d969862cc..7c361f7b9ed 100644
--- a/chromium/ui/views/controls/label_unittest.cc
+++ b/chromium/ui/views/controls/label_unittest.cc
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/i18n/rtl.h"
#include "base/strings/utf_string_conversions.h"
@@ -29,10 +30,13 @@
#include "ui/gfx/text_elider.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/border.h"
+#include "ui/views/controls/base_control_test_widget.h"
#include "ui/views/controls/link.h"
#include "ui/views/style/typography.h"
#include "ui/views/test/focus_manager_test.h"
+#include "ui/views/test/view_metadata_test_utils.h"
#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_utils.h"
@@ -45,7 +49,7 @@ namespace views {
namespace {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
const int kControlCommandModifier = ui::EF_COMMAND_DOWN;
#else
const int kControlCommandModifier = ui::EF_CONTROL_DOWN;
@@ -88,17 +92,10 @@ void SetRTL(bool rtl) {
EXPECT_EQ(rtl, base::i18n::IsRTL());
}
-// Returns true if |current| is bigger than |last|. Sets |last| to |current|.
-bool Increased(int current, int* last) {
- bool increased = current > *last;
- *last = current;
- return increased;
-}
-
base::string16 GetClipboardText(ui::ClipboardBuffer clipboard_buffer) {
base::string16 clipboard_text;
- ui::Clipboard::GetForCurrentThread()->ReadText(clipboard_buffer,
- &clipboard_text);
+ ui::Clipboard::GetForCurrentThread()->ReadText(
+ clipboard_buffer, /* data_dst = */ nullptr, &clipboard_text);
return clipboard_text;
}
@@ -116,43 +113,22 @@ base::string16 ToRTL(const char* ascii) {
} // namespace
-class LabelTest : public ViewsTestBase {
+class LabelTest : public test::BaseControlTestWidget {
public:
LabelTest() = default;
+ LabelTest(const LabelTest&) = delete;
+ LabelTest& operator=(const LabelTest&) = delete;
+ ~LabelTest() override = default;
- // ViewsTestBase:
- void SetUp() override {
- ViewsTestBase::SetUp();
-
- Widget::InitParams params =
- CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.bounds = gfx::Rect(200, 200);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- widget_.Init(std::move(params));
- View* container = new View();
- widget_.SetContentsView(container);
-
- label_ = new Label();
- container->AddChildView(label_);
-
- widget_.Show();
- }
-
- void TearDown() override {
- widget_.Close();
- ViewsTestBase::TearDown();
+ protected:
+ void CreateWidgetContent(View* container) override {
+ label_ = container->AddChildView(std::make_unique<Label>());
}
- protected:
Label* label() { return label_; }
- Widget* widget() { return &widget_; }
-
private:
Label* label_ = nullptr;
- Widget widget_;
-
- DISALLOW_COPY_AND_ASSIGN(LabelTest);
};
// Test fixture for text selection related tests.
@@ -215,31 +191,31 @@ class LabelSelectionTest : public LabelTest {
SimulatePaint();
gfx::RenderText* render_text =
label()->GetRenderTextForSelectionController();
- const gfx::Range range(index, index + 1);
- const std::vector<gfx::Rect> bounds =
- render_text->GetSubstringBounds(range);
- DCHECK_EQ(1u, bounds.size());
- const int mid_y = bounds[0].y() + bounds[0].height() / 2;
// For single-line text, use the glyph bounds since it gives a better
// representation of the midpoint between glyphs when considering selection.
- // TODO(tapted): When GetCursorSpan() supports returning a vertical range
- // as well as a horizontal range, just use that here.
+ // TODO(crbug.com/248597): Add multiline support to GetCursorBounds(...).
if (!render_text->multiline()) {
- return gfx::Point(render_text->GetCursorSpan(range).Round().start(),
- mid_y);
+ return render_text
+ ->GetCursorBounds(gfx::SelectionModel(index, gfx::CURSOR_FORWARD),
+ true)
+ .left_center();
}
- // Otherwise, GetCursorSpan() will give incorrect results. Multiline
+ // Otherwise, GetCursorBounds() will give incorrect results. Multiline
// editing is not supported (http://crbug.com/248597) so there hasn't been
// a need to draw a cursor. Instead, derive a point from the selection
// bounds, which always rounds up to an integer after the end of a glyph.
// This rounding differs to the glyph bounds, which rounds to nearest
// integer. See http://crbug.com/735346.
+ auto bounds = render_text->GetSubstringBounds({index, index + 1});
+ DCHECK_EQ(1u, bounds.size());
+
const bool rtl =
render_text->GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT;
// Return Point corresponding to the leading edge of the character.
- return gfx::Point(rtl ? bounds[0].right() - 1 : bounds[0].x() + 1, mid_y);
+ return rtl ? bounds[0].right_center() + gfx::Vector2d(-1, 0)
+ : bounds[0].left_center() + gfx::Vector2d(1, 0);
}
size_t GetLineCount() {
@@ -261,8 +237,14 @@ class LabelSelectionTest : public LabelTest {
DISALLOW_COPY_AND_ASSIGN(LabelSelectionTest);
};
+TEST_F(LabelTest, Metadata) {
+ // Calling SetMultiLine() will DCHECK unless the label is in multi-line mode.
+ label()->SetMultiLine(true);
+ test::TestViewMetadata(label());
+}
+
TEST_F(LabelTest, FontPropertySymbol) {
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// On linux, the fonts are mocked with a custom FontConfig. The "Courier New"
// family name is mapped to Cousine-Regular.ttf (see: $build/test_fonts/*).
std::string font_name("Courier New");
@@ -738,8 +720,8 @@ TEST_F(LabelTest, MultiLineSizing) {
required_size.width() + border.width());
}
-#if !defined(OS_MACOSX)
-// TODO(warx): Remove !defined(OS_MACOSX) once SetMaxLines() is applied to MAC
+#if !defined(OS_APPLE)
+// TODO(warx): Remove !defined(OS_APPLE) once SetMaxLines() is applied to MAC
// (crbug.com/758720).
TEST_F(LabelTest, MultiLineSetMaxLines) {
// Ensure SetMaxLines clamps the line count of a string with returns.
@@ -920,11 +902,15 @@ TEST_F(LabelTest, MultilineSupportedRenderText) {
// Ensures SchedulePaint() calls are not made in OnPaint().
TEST_F(LabelTest, NoSchedulePaintInOnPaint) {
TestLabel label;
+ int count = 0;
+ const auto expect_paint_count_increased = [&]() {
+ EXPECT_GT(label.schedule_paint_count(), count);
+ count = label.schedule_paint_count();
+ };
// Initialization should schedule at least one paint, but the precise number
// doesn't really matter.
- int count = label.schedule_paint_count();
- EXPECT_LT(0, count);
+ expect_paint_count_increased();
// Painting should never schedule another paint.
label.SimulatePaint();
@@ -932,16 +918,16 @@ TEST_F(LabelTest, NoSchedulePaintInOnPaint) {
// Test a few things that should schedule paints. Multiple times is OK.
label.SetEnabled(false);
- EXPECT_TRUE(Increased(label.schedule_paint_count(), &count));
+ expect_paint_count_increased();
label.SetText(label.GetText() + ASCIIToUTF16("Changed"));
- EXPECT_TRUE(Increased(label.schedule_paint_count(), &count));
+ expect_paint_count_increased();
label.SizeToPreferredSize();
- EXPECT_TRUE(Increased(label.schedule_paint_count(), &count));
+ expect_paint_count_increased();
label.SetEnabledColor(SK_ColorBLUE);
- EXPECT_TRUE(Increased(label.schedule_paint_count(), &count));
+ expect_paint_count_increased();
label.SimulatePaint();
EXPECT_EQ(count, label.schedule_paint_count()); // Unchanged.
@@ -954,7 +940,7 @@ TEST_F(LabelTest, EmptyLabel) {
EXPECT_TRUE(label()->size().IsEmpty());
// With no text, neither links nor labels have a size in any dimension.
- Link concrete_link((base::string16()));
+ Link concrete_link;
EXPECT_TRUE(concrete_link.GetPreferredSize().IsEmpty());
}
diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc
index 29348fd8a9d..7ce8a18c0e1 100644
--- a/chromium/ui/views/controls/link.cc
+++ b/chromium/ui/views/controls/link.cc
@@ -210,7 +210,7 @@ void Link::ConfigureFocus() {
if (GetText().empty()) {
SetFocusBehavior(FocusBehavior::NEVER);
} else {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
#else
SetFocusBehavior(FocusBehavior::ALWAYS);
diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h
index 51b5a2341a5..427498c7153 100644
--- a/chromium/ui/views/controls/link.h
+++ b/chromium/ui/views/controls/link.h
@@ -35,7 +35,7 @@ class VIEWS_EXPORT Link : public Label {
using ClickedCallback =
base::RepeatingCallback<void(Link* source, int event_flags)>;
- explicit Link(const base::string16& title,
+ explicit Link(const base::string16& title = base::string16(),
int text_context = style::CONTEXT_LABEL,
int text_style = style::STYLE_LINK);
~Link() override;
diff --git a/chromium/ui/views/controls/link_unittest.cc b/chromium/ui/views/controls/link_unittest.cc
new file mode 100644
index 00000000000..aaa0782049a
--- /dev/null
+++ b/chromium/ui/views/controls/link_unittest.cc
@@ -0,0 +1,82 @@
+// 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 "ui/views/controls/link.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/base_control_test_widget.h"
+#include "ui/views/test/view_metadata_test_utils.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+namespace {
+
+class LinkTest : public test::BaseControlTestWidget {
+ public:
+ LinkTest() = default;
+ LinkTest(const LinkTest&) = delete;
+ LinkTest& operator=(const LinkTest&) = delete;
+ ~LinkTest() override = default;
+
+ protected:
+ void CreateWidgetContent(View* container) override {
+ link_ = container->AddChildView(
+ std::make_unique<Link>(base::ASCIIToUTF16("TestLink")));
+ }
+
+ Link* link() { return link_; }
+
+ public:
+ Link* link_ = nullptr;
+};
+
+} // namespace
+
+TEST_F(LinkTest, Metadata) {
+ link()->SetMultiLine(true);
+ test::TestViewMetadata(link());
+}
+
+TEST_F(LinkTest, TestLinkClick) {
+ bool link_clicked = false;
+ link()->set_callback(
+ base::BindRepeating([](bool* link_clicked, Link* link,
+ int event_flags) { *link_clicked = true; },
+ &link_clicked));
+ link()->SizeToPreferredSize();
+ gfx::Point point = link()->bounds().CenterPoint();
+ ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON);
+ link()->OnMouseReleased(release);
+ EXPECT_TRUE(link_clicked);
+}
+
+TEST_F(LinkTest, TestLinkTap) {
+ bool link_clicked = false;
+ link()->set_callback(
+ base::BindRepeating([](bool* link_clicked, Link* link,
+ int event_flags) { *link_clicked = true; },
+ &link_clicked));
+ link()->SizeToPreferredSize();
+ gfx::Point point = link()->bounds().CenterPoint();
+ ui::GestureEvent tap_event(point.x(), point.y(), 0, ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+ link()->OnGestureEvent(&tap_event);
+ EXPECT_TRUE(link_clicked);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h
index c84d1377c94..e64b39342ea 100644
--- a/chromium/ui/views/controls/menu/menu_config.h
+++ b/chromium/ui/views/controls/menu/menu_config.h
@@ -208,6 +208,30 @@ struct VIEWS_EXPORT MenuConfig {
// Margins for footnotes (HIGHLIGHTED item at the end of a menu).
int footnote_vertical_margin = 11;
+ // New Badge -----------------------------------------------------------------
+ // Note that there are a few differences between Views and Mac constants here
+ // that are due to the fact that the rendering is different and therefore
+ // tweaks to the spacing need to be made to achieve the same visual result.
+
+ // Difference in the font size (in pixels) between menu label font and "new"
+ // badge font size.
+ static constexpr int kNewBadgeFontSizeAdjustment = -1;
+
+ // Space between primary text and "new" badge.
+ static constexpr int kNewBadgeHorizontalMargin = 8;
+
+ // Highlight padding around "new" text.
+ static constexpr int kNewBadgeInternalPadding = 4;
+ static constexpr int kNewBadgeInternalPaddingTopMac = 1;
+
+ // The baseline offset of the "new" badge image to the menu text baseline.
+ static constexpr int kNewBadgeBaslineOffsetMac = -4;
+
+ // The corner radius of the rounded rect for the "new" badge.
+ static constexpr int kNewBadgeCornerRadius = 3;
+ static_assert(kNewBadgeCornerRadius <= kNewBadgeInternalPadding,
+ "New badge corner radius should not exceed padding.");
+
private:
// Configures a MenuConfig as appropriate for the current platform.
void Init();
diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm
index 1de2eb9c7ec..757cafaf612 100644
--- a/chromium/ui/views/controls/menu/menu_config_mac.mm
+++ b/chromium/ui/views/controls/menu/menu_config_mac.mm
@@ -7,6 +7,7 @@
#import <AppKit/AppKit.h>
#include "base/mac/mac_util.h"
+#include "ui/gfx/platform_font_mac.h"
namespace {
@@ -42,7 +43,8 @@ void InitMaterialMenuConfig(views::MenuConfig* config) {
namespace views {
void MenuConfig::Init() {
- font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0]));
+ font_list = gfx::FontList(gfx::Font(
+ new gfx::PlatformFontMac(gfx::PlatformFontMac::SystemFontType::kMenu)));
check_selected_combobox_item = true;
arrow_key_selection_wraps = false;
use_mnemonics = false;
diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc
index 87f9291b67d..bad1730e19a 100644
--- a/chromium/ui/views/controls/menu/menu_controller.cc
+++ b/chromium/ui/views/controls/menu/menu_controller.cc
@@ -17,6 +17,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
@@ -62,6 +63,7 @@
#endif
#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
@@ -72,7 +74,7 @@ namespace views {
namespace {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
bool AcceleratorShouldCancelMenu(const ui::Accelerator& accelerator) {
// Since AcceleratorShouldCancelMenu() is called quite early in key
// event handling, it is actually invoked for modifier keys themselves
@@ -108,12 +110,12 @@ bool ShouldIgnoreScreenBoundsForMenus() {
#if defined(USE_OZONE)
// Wayland requires placing menus is screen coordinates. See comment in
// ozone_platform_wayland.cc.
- return ui::OzonePlatform::GetInstance()
- ->GetPlatformProperties()
- .ignore_screen_bounds_for_menus;
-#else
- return false;
+ if (features::IsUsingOzonePlatform())
+ return ui::OzonePlatform::GetInstance()
+ ->GetPlatformProperties()
+ .ignore_screen_bounds_for_menus;
#endif
+ return false;
}
// The amount of time the mouse should be down before a mouse release is
@@ -514,7 +516,7 @@ void MenuController::Run(Widget* parent,
menu_pre_target_handler_ = MenuPreTargetHandler::Create(this, owner_);
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
menu_cocoa_watcher_ = std::make_unique<MenuCocoaWatcherMac>(base::BindOnce(
&MenuController::Cancel, this->AsWeakPtr(), ExitType::kAll));
#endif
@@ -546,7 +548,7 @@ void MenuController::Run(Widget* parent,
}
void MenuController::Cancel(ExitType type) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
menu_closure_animation_.reset();
#endif
@@ -871,12 +873,12 @@ bool MenuController::OnMouseWheel(SubmenuView* source,
void MenuController::OnGestureEvent(SubmenuView* source,
ui::GestureEvent* event) {
if (owner_ && send_gesture_events_to_owner()) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
NOTIMPLEMENTED();
-#else // !defined(OS_MACOSX)
+#else // !defined(OS_APPLE)
event->ConvertLocationToTarget(source->GetWidget()->GetNativeWindow(),
owner()->GetNativeWindow());
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
owner()->OnGestureEvent(event);
// Reset |send_gesture_events_to_owner_| when the first gesture ends.
if (event->type() == ui::ET_GESTURE_END)
@@ -1181,22 +1183,22 @@ ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent(
base::WeakPtr<MenuController> this_ref = AsWeakPtr();
if (event->type() == ui::ET_KEY_PRESSED) {
bool key_handled = false;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Special handling for Option-Up and Option-Down, which should behave like
// Home and End respectively in menus.
if ((event->flags() & ui::EF_ALT_DOWN)) {
+ ui::KeyEvent rewritten_event(*event);
if (event->key_code() == ui::VKEY_UP) {
- key_handled = OnKeyPressed(ui::VKEY_HOME);
+ rewritten_event.set_key_code(ui::VKEY_HOME);
} else if (event->key_code() == ui::VKEY_DOWN) {
- key_handled = OnKeyPressed(ui::VKEY_END);
- } else {
- key_handled = OnKeyPressed(event->key_code());
+ rewritten_event.set_key_code(ui::VKEY_END);
}
+ key_handled = OnKeyPressed(rewritten_event);
} else {
- key_handled = OnKeyPressed(event->key_code());
+ key_handled = OnKeyPressed(*event);
}
#else
- key_handled = OnKeyPressed(event->key_code());
+ key_handled = OnKeyPressed(*event);
#endif
if (key_handled)
@@ -1228,7 +1230,7 @@ ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent(
ui::Accelerator accelerator(*event);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (AcceleratorShouldCancelMenu(accelerator)) {
Cancel(ExitType::kAll);
return ui::POST_DISPATCH_PERFORM_DEFAULT;
@@ -1294,7 +1296,7 @@ void MenuController::TurnOffMenuSelectionHoldForTest() {
}
void MenuController::OnMenuItemDestroying(MenuItemView* menu_item) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (menu_closure_animation_ && menu_closure_animation_->item() == menu_item)
menu_closure_animation_.reset();
#endif
@@ -1379,7 +1381,7 @@ void MenuController::SetSelection(MenuItemView* menu_item,
StartShowTimer();
// Notify an accessibility focus event on all menu items except for the root.
- if (menu_item &&
+ if (menu_item && pending_item_changed &&
(MenuDepth(menu_item) != 1 ||
menu_item->GetType() != MenuItemView::Type::kSubMenu ||
(menu_item->GetType() == MenuItemView::Type::kActionableSubMenu &&
@@ -1475,23 +1477,25 @@ void MenuController::StartDrag(SubmenuView* source,
int drag_ops = item->GetDelegate()->GetDragOperations(item);
did_initiate_drag_ = true;
base::WeakPtr<MenuController> this_ref = AsWeakPtr();
- // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
+ // TODO(varunjain): Properly determine and send DragEventSource below.
item->GetWidget()->RunShellDrag(nullptr, std::move(data), widget_loc,
- drag_ops,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+ drag_ops, ui::mojom::DragEventSource::kMouse);
// MenuController may have been deleted so check before accessing member
// variables.
if (this_ref)
did_initiate_drag_ = false;
}
-bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
- // Do not process while performing drag-and-drop
+bool MenuController::OnKeyPressed(const ui::KeyEvent& event) {
+ DCHECK_EQ(event.type(), ui::ET_KEY_PRESSED);
+
+ // Do not process while performing drag-and-drop.
if (for_drop_)
return false;
bool handled_key_code = false;
+ const ui::KeyboardCode key_code = event.key_code();
switch (key_code) {
case ui::VKEY_HOME:
if (IsEditableCombobox())
@@ -1506,10 +1510,12 @@ bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
break;
case ui::VKEY_UP:
+ case ui::VKEY_PRIOR:
IncrementSelection(INCREMENT_SELECTION_UP);
break;
case ui::VKEY_DOWN:
+ case ui::VKEY_NEXT:
IncrementSelection(INCREMENT_SELECTION_DOWN);
break;
@@ -1534,7 +1540,7 @@ bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
break;
// On Mac, treat space the same as return.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
case ui::VKEY_SPACE:
SendAcceleratorToHotTrackedView();
break;
@@ -1546,7 +1552,7 @@ bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
// Fallthrough to accept or dismiss combobox menus on F4, like windows.
FALLTHROUGH;
case ui::VKEY_RETURN:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
case ui::VKEY_SPACE:
#endif
// An odd special case: if a prefix selection is in flight, space should
@@ -1570,7 +1576,7 @@ bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
handled_key_code = true;
if (!SendAcceleratorToHotTrackedView() &&
pending_state_.item->GetEnabled()) {
- Accept(pending_state_.item, 0);
+ Accept(pending_state_.item, event.flags());
}
}
}
@@ -1590,7 +1596,7 @@ bool MenuController::OnKeyPressed(ui::KeyboardCode key_code) {
CloseSubmenu();
break;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
case ui::VKEY_APPS: {
Button* hot_view = GetFirstHotTrackedView(pending_state_.item);
if (hot_view) {
@@ -1698,7 +1704,7 @@ void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
}
void MenuController::Accept(MenuItemView* item, int event_flags) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
menu_closure_animation_ = std::make_unique<MenuClosureAnimationMac>(
item, item->GetParentMenuItem()->GetSubmenu(),
base::BindOnce(&MenuController::ReallyAccept, base::Unretained(this),
@@ -1712,7 +1718,7 @@ void MenuController::Accept(MenuItemView* item, int event_flags) {
void MenuController::ReallyAccept(MenuItemView* item, int event_flags) {
DCHECK(!for_drop_);
result_ = item;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Reset the closure animation since it's now finished - this also unblocks
// input events for the menu.
menu_closure_animation_.reset();
@@ -2827,7 +2833,7 @@ void MenuController::RepostEventAndCancel(SubmenuView* source,
if (last_part.type != MenuPart::NONE)
exit_type = ExitType::kOutermost;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// When doing a menu closure animation, target the deepest submenu - that way
// MenuClosureAnimationMac will fade out all the menus in sync, rather than
// the shallowest menu only.
@@ -3114,16 +3120,21 @@ void MenuController::SetNextHotTrackedView(
SetInitialHotTrackedView(to_select, direction);
}
-void MenuController::SetHotTrackedButton(Button* hot_button) {
+void MenuController::SetHotTrackedButton(Button* new_hot_button) {
+ // Set hot tracked state and fire a11y events for the hot tracked button.
+ // This must be done whether or not it was the previous hot tracked button.
+ // For example, when a zoom button is pressed, the menu remains open and the
+ // same zoom button should have its hot tracked state set again.
+
// If we're providing a new hot-tracked button, first remove the existing one.
- if (hot_button_ && hot_button_ != hot_button) {
+ if (hot_button_ && hot_button_ != new_hot_button) {
hot_button_->SetHotTracked(false);
hot_button_->GetViewAccessibility().EndPopupFocusOverride();
}
// Then set the new one.
- hot_button_ = hot_button;
- if (hot_button_ && !hot_button_->IsHotTracked()) {
+ hot_button_ = new_hot_button;
+ if (hot_button_) {
hot_button_->GetViewAccessibility().SetPopupFocusOverride();
hot_button_->SetHotTracked(true);
hot_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
@@ -3155,7 +3166,7 @@ void MenuController::UnregisterAlertedItem(MenuItemView* item) {
}
bool MenuController::CanProcessInputEvents() const {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return !menu_closure_animation_;
#else
return true;
diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h
index 2b3625c5b5a..9da3b74e527 100644
--- a/chromium/ui/views/controls/menu/menu_controller.h
+++ b/chromium/ui/views/controls/menu/menu_controller.h
@@ -27,7 +27,7 @@
#include "ui/views/controls/menu/menu_delegate.h"
#include "ui/views/widget/widget_observer.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/views/controls/menu/menu_closure_animation_mac.h"
#include "ui/views/controls/menu/menu_cocoa_watcher_mac.h"
#endif
@@ -353,9 +353,8 @@ class VIEWS_EXPORT MenuController
const ui::LocatedEvent* event);
void StartDrag(SubmenuView* source, const gfx::Point& location);
- // Handles |key_code| as a keypress. Returns true if OnKeyPressed handled the
- // key code.
- bool OnKeyPressed(ui::KeyboardCode key_code);
+ // Returns true if OnKeyPressed handled the key |event|.
+ bool OnKeyPressed(const ui::KeyEvent& event);
// Creates a MenuController. See |for_drop_| member for details on |for_drop|.
MenuController(bool for_drop, internal::MenuControllerDelegate* delegate);
@@ -612,7 +611,7 @@ class VIEWS_EXPORT MenuController
SelectionIncrementDirectionType direction);
// Updates the current |hot_button_| and its hot tracked state.
- void SetHotTrackedButton(Button* hot_button);
+ void SetHotTrackedButton(Button* new_hot_button);
// Returns whether typing a new character will continue the existing prefix
// selection. If this returns false, typing a new character will start a new
@@ -768,7 +767,7 @@ class VIEWS_EXPORT MenuController
// A mask of the EventFlags for the mouse buttons currently pressed.
int current_mouse_pressed_state_ = 0;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
std::unique_ptr<MenuClosureAnimationMac> menu_closure_animation_;
std::unique_ptr<MenuCocoaWatcherMac> menu_cocoa_watcher_;
#endif
diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
index 67168197f0a..a473b624f83 100644
--- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
@@ -4,13 +4,16 @@
#include "ui/views/controls/menu/menu_controller.h"
+#include <vector>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
+#include "base/test/bind_test_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_action_data.h"
@@ -45,6 +48,7 @@
#include "ui/aura/null_window_targeter.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/views/controls/menu/menu_pre_target_handler.h"
#endif
@@ -53,11 +57,8 @@
#include "ui/gfx/x/x11.h" // nogncheck
#endif
-#if defined(OS_CHROMEOS)
-#include "ui/base/ui_base_features.h"
-#endif
-
#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
@@ -68,14 +69,15 @@ namespace {
bool ShouldIgnoreScreenBoundsForMenus() {
#if defined(USE_OZONE)
- // Wayland requires placing menus is screen coordinates. See comment in
- // ozone_platform_wayland.cc.
- return ui::OzonePlatform::GetInstance()
- ->GetPlatformProperties()
- .ignore_screen_bounds_for_menus;
-#else
- return false;
+ if (features::IsUsingOzonePlatform()) {
+ // Wayland requires placing menus is screen coordinates. See comment in
+ // ozone_platform_wayland.cc.
+ return ui::OzonePlatform::GetInstance()
+ ->GetPlatformProperties()
+ .ignore_screen_bounds_for_menus;
+ }
#endif
+ return false;
}
// Test implementation of MenuControllerDelegate that only reports the values
@@ -203,7 +205,7 @@ class TestDragDropClient : public aura::client::DragDropClient {
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
@@ -224,7 +226,7 @@ int TestDragDropClient::StartDragAndDrop(
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
drag_in_progress_ = true;
start_drag_and_drop_callback_.Run();
return 0;
@@ -346,7 +348,7 @@ class MenuControllerTest : public ViewsTestBase,
set_views_delegate(std::move(test_views_delegate));
ViewsTestBase::SetUp();
Init();
- ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
+ ASSERT_TRUE(base::CurrentUIThread::IsSet());
}
void TearDown() override {
@@ -875,7 +877,7 @@ class MenuControllerTest : public ViewsTestBase,
INSTANTIATE_TEST_SUITE_P(All, MenuControllerTest, testing::Bool());
-#if defined(USE_X11)
+#if defined(USE_AURA)
// Tests that an event targeter which blocks events will be honored by the menu
// event dispatcher.
TEST_F(MenuControllerTest, EventTargeter) {
@@ -892,13 +894,14 @@ TEST_F(MenuControllerTest, EventTargeter) {
TestAsyncEscapeKey();
EXPECT_EQ(MenuController::ExitType::kAll, menu_exit_type());
}
-
-#endif // defined(USE_X11)
+#endif // defined(USE_AURA)
#if defined(USE_X11)
// Tests that touch event ids are released correctly. See crbug.com/439051 for
// details. When the ids aren't managed correctly, we get stuck down touches.
TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) {
+ if (features::IsUsingOzonePlatform())
+ return;
TestEventHandler test_event_handler;
GetRootWindow(owner())->AddPreTargetHandler(&test_event_handler);
@@ -1062,67 +1065,115 @@ TEST_F(MenuControllerTest, LastSelectedItem) {
ResetSelection();
}
-// Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled
-// items.
-TEST_F(MenuControllerTest, NextSelectedItem) {
- // Disabling the item "Three" gets it skipped when using keyboard to navigate.
- menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
+// MenuController tests which set expectations about how menu item selection
+// behaves should verify test cases work as intended for all supported selection
+// mechanisms.
+class MenuControllerSelectionTest : public MenuControllerTest {
+ public:
+ MenuControllerSelectionTest() = default;
+ ~MenuControllerSelectionTest() override = default;
- // Fake initial hot selection.
- SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
- EXPECT_EQ(1, pending_state_item()->GetCommand());
+ protected:
+ // Models a mechanism by which menu item selection can be incremented and/or
+ // decremented.
+ struct SelectionMechanism {
+ base::RepeatingClosure IncrementSelection;
+ base::RepeatingClosure DecrementSelection;
+ };
- // Move down in the menu.
- // Select next item.
- IncrementSelection();
- EXPECT_EQ(2, pending_state_item()->GetCommand());
+ // Returns all mechanisms by which menu item selection can be incremented
+ // and/or decremented.
+ const std::vector<SelectionMechanism>& selection_mechanisms() {
+ return selection_mechanisms_;
+ }
- // Skip disabled item.
- IncrementSelection();
- EXPECT_EQ(4, pending_state_item()->GetCommand());
+ private:
+ const std::vector<SelectionMechanism> selection_mechanisms_ = {
+ // Updates selection via IncrementSelection()/DecrementSelection().
+ SelectionMechanism{
+ base::BindLambdaForTesting([this]() { IncrementSelection(); }),
+ base::BindLambdaForTesting([this]() { DecrementSelection(); })},
+ // Updates selection via down/up arrow keys.
+ SelectionMechanism{
+ base::BindLambdaForTesting([this]() { DispatchKey(ui::VKEY_DOWN); }),
+ base::BindLambdaForTesting([this]() { DispatchKey(ui::VKEY_UP); })},
+ // Updates selection via next/prior keys.
+ SelectionMechanism{
+ base::BindLambdaForTesting([this]() { DispatchKey(ui::VKEY_NEXT); }),
+ base::BindLambdaForTesting(
+ [this]() { DispatchKey(ui::VKEY_PRIOR); })}};
+};
- if (SelectionWraps()) {
- // Wrap around.
- IncrementSelection();
+// Tests that opening menu and exercising various mechanisms to update selection
+// iterates over enabled items.
+TEST_F(MenuControllerSelectionTest, NextSelectedItem) {
+ for (const auto& selection_mechanism : selection_mechanisms()) {
+ // Disabling the item "Three" gets it skipped when using keyboard to
+ // navigate.
+ menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
+
+ // Fake initial hot selection.
+ SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
EXPECT_EQ(1, pending_state_item()->GetCommand());
- // Move up in the menu.
- // Wrap around.
- DecrementSelection();
- EXPECT_EQ(4, pending_state_item()->GetCommand());
- } else {
- // Don't wrap.
- IncrementSelection();
+ // Move down in the menu.
+ // Select next item.
+ selection_mechanism.IncrementSelection.Run();
+ EXPECT_EQ(2, pending_state_item()->GetCommand());
+
+ // Skip disabled item.
+ selection_mechanism.IncrementSelection.Run();
EXPECT_EQ(4, pending_state_item()->GetCommand());
- }
- // Skip disabled item.
- DecrementSelection();
- EXPECT_EQ(2, pending_state_item()->GetCommand());
+ if (SelectionWraps()) {
+ // Wrap around.
+ selection_mechanism.IncrementSelection.Run();
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+
+ // Move up in the menu.
+ // Wrap around.
+ selection_mechanism.DecrementSelection.Run();
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+ } else {
+ // Don't wrap.
+ selection_mechanism.IncrementSelection.Run();
+ EXPECT_EQ(4, pending_state_item()->GetCommand());
+ }
- // Select previous item.
- DecrementSelection();
- EXPECT_EQ(1, pending_state_item()->GetCommand());
+ // Skip disabled item.
+ selection_mechanism.DecrementSelection.Run();
+ EXPECT_EQ(2, pending_state_item()->GetCommand());
- // Clear references in menu controller to the menu item that is going away.
- ResetSelection();
+ // Select previous item.
+ selection_mechanism.DecrementSelection.Run();
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+
+ // Clear references in menu controller to the menu item that is going
+ // away.
+ ResetSelection();
+ }
}
-// Tests that opening menu and pressing 'Up' selects the last enabled menu item.
-TEST_F(MenuControllerTest, PreviousSelectedItem) {
- // Disabling the item "Four" gets it skipped when using keyboard to navigate.
- menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false);
+// Tests that opening menu and exercising various mechanisms to decrement
+// selection selects the last enabled menu item.
+TEST_F(MenuControllerSelectionTest, PreviousSelectedItem) {
+ for (const auto& selection_mechanism : selection_mechanisms()) {
+ // Disabling the item "Four" gets it skipped when using keyboard to
+ // navigate.
+ menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false);
- // Fake initial root item selection and submenu showing.
- SetPendingStateItem(menu_item());
- EXPECT_EQ(0, pending_state_item()->GetCommand());
+ // Fake initial root item selection and submenu showing.
+ SetPendingStateItem(menu_item());
+ EXPECT_EQ(0, pending_state_item()->GetCommand());
- // Move up and select a previous (in our case the last enabled) item.
- DecrementSelection();
- EXPECT_EQ(3, pending_state_item()->GetCommand());
+ // Move up and select a previous (in our case the last enabled) item.
+ selection_mechanism.DecrementSelection.Run();
+ EXPECT_EQ(3, pending_state_item()->GetCommand());
- // Clear references in menu controller to the menu item that is going away.
- ResetSelection();
+ // Clear references in menu controller to the menu item that is going
+ // away.
+ ResetSelection();
+ }
}
// Tests that the APIs related to the current selected item work correctly.
@@ -1318,6 +1369,13 @@ TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) {
EXPECT_TRUE(button1->IsHotTracked());
EXPECT_EQ(button1, GetHotButton());
+ // Setting the hot tracked state twice on the same button via the
+ // menu controller should still set the hot tracked state on the button again.
+ button1->SetHotTracked(false);
+ SetHotTrackedButton(button1);
+ EXPECT_TRUE(button1->IsHotTracked());
+ EXPECT_EQ(button1, GetHotButton());
+
ExitMenuRun();
EXPECT_FALSE(button1->IsHotTracked());
EXPECT_TRUE(button2->IsHotTracked());
@@ -1713,36 +1771,6 @@ TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) {
EXPECT_EQ(1, nested_delegate->on_menu_closed_called());
}
-TEST_F(MenuControllerTest, ArrowKeysAtEnds) {
- menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
-
- SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
- EXPECT_EQ(1, pending_state_item()->GetCommand());
-
- if (SelectionWraps()) {
- DispatchKey(ui::VKEY_UP);
- EXPECT_EQ(4, pending_state_item()->GetCommand());
-
- DispatchKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, pending_state_item()->GetCommand());
- } else {
- DispatchKey(ui::VKEY_UP);
- EXPECT_EQ(1, pending_state_item()->GetCommand());
- }
-
- DispatchKey(ui::VKEY_DOWN);
- EXPECT_EQ(2, pending_state_item()->GetCommand());
-
- DispatchKey(ui::VKEY_DOWN);
- EXPECT_EQ(4, pending_state_item()->GetCommand());
-
- DispatchKey(ui::VKEY_DOWN);
- if (SelectionWraps())
- EXPECT_EQ(1, pending_state_item()->GetCommand());
- else
- EXPECT_EQ(4, pending_state_item()->GetCommand());
-}
-
// Test that the menu is properly placed where it best fits.
TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) {
MenuBoundsOptions options;
@@ -2476,14 +2504,14 @@ TEST_F(MenuControllerTest, SetSelectionIndices_NestedButtons) {
container_view->AddChildView(new Label());
// Add two focusable buttons (buttons in menus are always focusable).
- Button* const button1 = new LabelButton(nullptr, base::string16());
+ Button* const button1 =
+ container_view->AddChildView(std::make_unique<LabelButton>());
button1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
button1->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem);
- container_view->AddChildView(button1);
- Button* const button2 = new LabelButton(nullptr, base::string16());
+ Button* const button2 =
+ container_view->AddChildView(std::make_unique<LabelButton>());
button2->GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem);
button2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
- container_view->AddChildView(button2);
OpenMenu(menu_item());
@@ -2551,7 +2579,7 @@ TEST_F(MenuControllerTest, AccessibilityEmitsSelectChildrenChanged) {
EXPECT_EQ(observer.saw_selected_children_changed_, true);
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// This test exercises a Mac-specific behavior, by which hotkeys using modifiers
// cause menus to close and the hotkeys to be handled by the browser window.
// This specific test case tries using cmd-ctrl-f, which normally means
diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc
index fc94bfc7f17..9b3cc9c5f21 100644
--- a/chromium/ui/views/controls/menu/menu_host.cc
+++ b/chromium/ui/views/controls/menu/menu_host.cc
@@ -23,7 +23,7 @@
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h"
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
#include "ui/aura/window.h"
#endif
@@ -31,7 +31,7 @@ namespace views {
namespace internal {
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// This class adds itself as the pre target handler for the |window|
// passed in. It currently handles touch events and forwards them to the
// controller. Reason for this approach is views does not get raw touch
@@ -78,16 +78,16 @@ class PreMenuEventDispatchHandler : public ui::EventHandler,
DISALLOW_COPY_AND_ASSIGN(PreMenuEventDispatchHandler);
};
-#endif // OS_MACOSX
+#endif // OS_APPLE
void TransferGesture(Widget* source, Widget* target) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
NOTIMPLEMENTED();
-#else // !defined(OS_MACOSX)
+#else // !defined(OS_APPLE)
source->GetGestureRecognizer()->TransferEventsTo(
source->GetNativeView(), target->GetNativeView(),
ui::TransferTouchesBehavior::kDontCancel);
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
}
} // namespace internal
@@ -138,7 +138,7 @@ void MenuHost::InitMenuHost(Widget* parent,
#endif
Init(std::move(params));
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
pre_dispatch_handler_ =
std::make_unique<internal::PreMenuEventDispatchHandler>(
menu_controller, submenu_, GetNativeView());
@@ -173,11 +173,6 @@ void MenuHost::ShowMenuHost(bool do_capture) {
} else {
GetGestureRecognizer()->CancelActiveTouchesExcept(nullptr);
}
-#if defined(MACOSX)
- // Cancel existing touches, so we don't miss some touch release/cancel
- // events due to the menu taking capture.
- GetGestureRecognizer()->CancelActiveTouchesExcept(nullptr);
-#endif // defined (OS_MACOSX)
// If MenuHost has no parent widget, it needs to call Show to get focus,
// so that it will get keyboard events.
if (owner_ == nullptr)
@@ -203,7 +198,7 @@ void MenuHost::DestroyMenuHost() {
HideMenuHost();
destroying_ = true;
static_cast<MenuHostRootView*>(GetRootView())->ClearSubmenu();
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
pre_dispatch_handler_.reset();
#endif
Close();
diff --git a/chromium/ui/views/controls/menu/menu_host.h b/chromium/ui/views/controls/menu/menu_host.h
index 9735ff987bf..30f097e745c 100644
--- a/chromium/ui/views/controls/menu/menu_host.h
+++ b/chromium/ui/views/controls/menu/menu_host.h
@@ -95,7 +95,7 @@ class MenuHost : public Widget, public WidgetObserver {
// If true and capture is lost we don't notify the delegate.
bool ignore_capture_lost_;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Handles raw touch events at the moment.
std::unique_ptr<internal::PreMenuEventDispatchHandler> pre_dispatch_handler_;
#endif
diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc
index 262ea70539a..f14eb806914 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view.cc
@@ -53,29 +53,26 @@ namespace views {
namespace {
-// Difference in the font size (in pixels) between menu label font and "new"
-// badge font size.
-constexpr int kNewBadgeFontSizeAdjustment = -1;
-
-// Space between primary text and "new" badge.
-constexpr int kNewBadgeHorizontalMargin = 8;
-
-// Highlight size around "new" badge.
-constexpr gfx::Insets kNewBadgeInternalPadding{4};
-
-// The corner radius of the rounded rect for the "new" badge.
-constexpr int kNewBadgeCornerRadius = 3;
-static_assert(kNewBadgeCornerRadius <= kNewBadgeInternalPadding.left(),
- "New badge corner radius should not exceed padding.");
+// Returns the appropriate font to use for the "new" badge based on the font
+// currently being used to render the title of the menu item.
+gfx::FontList DeriveNewBadgeFont(const gfx::FontList& primary_font) {
+ // Preferred font is slightly smaller and slightly more bold than the title
+ // font. The size change is required to make it look correct in the badge; we
+ // add a small degree of bold to prevent color smearing/blurring due to font
+ // smoothing. This ensures readability on all platforms and in both light and
+ // dark modes.
+ return primary_font.Derive(MenuConfig::kNewBadgeFontSizeAdjustment,
+ gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM);
+}
// Returns the horizontal space required for the "new" badge.
int GetNewBadgeRequiredWidth(const gfx::FontList& primary_font) {
const base::string16 new_text =
l10n_util::GetStringUTF16(IDS_MENU_ITEM_NEW_BADGE);
- gfx::FontList badge_font =
- primary_font.DeriveWithSizeDelta(kNewBadgeFontSizeAdjustment);
+ gfx::FontList badge_font = DeriveNewBadgeFont(primary_font);
return gfx::GetStringWidth(new_text, badge_font) +
- kNewBadgeInternalPadding.width() + 2 * kNewBadgeHorizontalMargin;
+ 2 * MenuConfig::kNewBadgeInternalPadding +
+ 2 * MenuConfig::kNewBadgeHorizontalMargin;
}
// Returns the highlight rect for the "new" badge given the font and text rect
@@ -83,8 +80,8 @@ int GetNewBadgeRequiredWidth(const gfx::FontList& primary_font) {
gfx::Rect GetNewBadgeRectOutsetAroundText(const gfx::FontList& badge_font,
const gfx::Rect& badge_text_rect) {
gfx::Rect badge_rect = badge_text_rect;
- badge_rect.Inset(
- -gfx::AdjustVisualBorderForFont(badge_font, kNewBadgeInternalPadding));
+ badge_rect.Inset(-gfx::AdjustVisualBorderForFont(
+ badge_font, gfx::Insets(MenuConfig::kNewBadgeInternalPadding)));
return badge_rect;
}
@@ -174,7 +171,9 @@ base::string16 MenuItemView::GetTooltipText(const gfx::Point& p) const {
}
const MenuDelegate* delegate = GetDelegate();
- CHECK(delegate);
+ if (!delegate)
+ return base::string16();
+
gfx::Point location(p);
ConvertPointToScreen(this, &location);
return delegate->GetTooltipText(command_, location);
@@ -206,7 +205,8 @@ void MenuItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
} else {
item_text = title_;
}
- node_data->SetName(GetAccessibleNameForMenuItem(item_text, GetMinorText()));
+ node_data->SetName(GetAccessibleNameForMenuItem(item_text, GetMinorText(),
+ ShouldShowNewBadge()));
switch (type_) {
case Type::kSubMenu:
@@ -215,7 +215,8 @@ void MenuItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
break;
case Type::kCheckbox:
case Type::kRadio: {
- const bool is_checked = GetDelegate()->IsItemChecked(GetCommand());
+ const bool is_checked =
+ GetDelegate() && GetDelegate()->IsItemChecked(GetCommand());
node_data->SetCheckedState(is_checked ? ax::mojom::CheckedState::kTrue
: ax::mojom::CheckedState::kFalse);
} break;
@@ -262,7 +263,8 @@ bool MenuItemView::IsBubble(MenuAnchorPosition anchor) {
// static
base::string16 MenuItemView::GetAccessibleNameForMenuItem(
const base::string16& item_text,
- const base::string16& minor_text) {
+ const base::string16& minor_text,
+ bool is_new_feature) {
base::string16 accessible_name = item_text;
// Filter out the "&" for accessibility clients.
@@ -284,6 +286,12 @@ base::string16 MenuItemView::GetAccessibleNameForMenuItem(
accessible_name.append(minor_text);
}
+ if (is_new_feature) {
+ accessible_name.push_back(' ');
+ accessible_name.append(l10n_util::GetStringUTF16(
+ IDS_MENU_ITEM_NEW_BADGE_SCREEN_READER_MESSAGE));
+ }
+
return accessible_name;
}
@@ -748,6 +756,12 @@ void MenuItemView::SetAlerted() {
SchedulePaint();
}
+bool MenuItemView::ShouldShowNewBadge() const {
+ static const bool feature_enabled =
+ base::FeatureList::IsEnabled(features::kEnableNewBadgeOnMenuItems);
+ return feature_enabled && is_new_;
+}
+
MenuItemView::MenuItemView(MenuItemView* parent,
int command,
MenuItemView::Type type) {
@@ -816,7 +830,7 @@ void MenuItemView::Init(MenuItemView* parent,
if (type_ == Type::kCheckbox || type_ == Type::kRadio) {
radio_check_image_view_ = AddChildView(std::make_unique<ImageView>());
bool show_check_radio_icon =
- type_ == Type::kRadio || (type_ == Type::kCheckbox &&
+ type_ == Type::kRadio || (type_ == Type::kCheckbox && GetDelegate() &&
GetDelegate()->IsItemChecked(GetCommand()));
radio_check_image_view_->SetVisible(show_check_radio_icon);
radio_check_image_view_->set_can_process_events_within_subtree(false);
@@ -943,7 +957,6 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
if (forced_visual_selection_.has_value())
render_selection = *forced_visual_selection_;
- MenuDelegate* delegate = GetDelegate();
// Render the background. As MenuScrollViewContainer draws the background, we
// only need the background when we want it to look different, as when we're
// selected.
@@ -966,10 +979,12 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
top_margin += (available_height - total_text_height) / 2;
// Render the check.
- if (type_ == Type::kCheckbox && delegate->IsItemChecked(GetCommand())) {
+ MenuDelegate* delegate = GetDelegate();
+ if (type_ == Type::kCheckbox && delegate &&
+ delegate->IsItemChecked(GetCommand())) {
radio_check_image_view_->SetImage(GetMenuCheckImage(icon_color));
} else if (type_ == Type::kRadio) {
- const bool toggled = delegate->IsItemChecked(GetCommand());
+ const bool toggled = delegate && delegate->IsItemChecked(GetCommand());
const gfx::VectorIcon& radio_icon =
toggled ? kMenuRadioSelectedIcon : kMenuRadioEmptyIcon;
const SkColor radio_icon_color = GetNativeTheme()->GetSystemColor(
@@ -1009,7 +1024,7 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
DrawNewBadge(
canvas,
gfx::Point(label_start + gfx::GetStringWidth(title(), style.font_list) +
- kNewBadgeHorizontalMargin,
+ MenuConfig::kNewBadgeHorizontalMargin,
top_margin),
style.font_list, flags);
}
@@ -1333,8 +1348,7 @@ void MenuItemView::DrawNewBadge(gfx::Canvas* canvas,
const gfx::Point& unmirrored_badge_start,
const gfx::FontList& primary_font,
int text_render_flags) {
- gfx::FontList badge_font =
- primary_font.DeriveWithSizeDelta(kNewBadgeFontSizeAdjustment);
+ gfx::FontList badge_font = DeriveNewBadgeFont(primary_font);
const base::string16 new_text =
l10n_util::GetStringUTF16(IDS_MENU_ITEM_NEW_BADGE);
@@ -1342,7 +1356,7 @@ void MenuItemView::DrawNewBadge(gfx::Canvas* canvas,
gfx::Rect badge_text_bounds(unmirrored_badge_start,
gfx::GetStringSize(new_text, badge_font));
badge_text_bounds.Offset(
- kNewBadgeInternalPadding.left(),
+ MenuConfig::kNewBadgeInternalPadding,
gfx::GetFontCapHeightCenterOffset(primary_font, badge_font));
if (base::i18n::IsRTL())
badge_text_bounds.set_x(GetMirroredXForRect(badge_text_bounds));
@@ -1354,7 +1368,7 @@ void MenuItemView::DrawNewBadge(gfx::Canvas* canvas,
new_flags.setColor(background_color);
canvas->DrawRoundRect(
GetNewBadgeRectOutsetAroundText(badge_font, badge_text_bounds),
- kNewBadgeCornerRadius, new_flags);
+ MenuConfig::kNewBadgeCornerRadius, new_flags);
// Render the badge text.
const SkColor foreground_color = GetNativeTheme()->GetSystemColor(
@@ -1440,12 +1454,6 @@ bool MenuItemView::HasChecksOrRadioButtons() const {
[](const auto* item) { return item->HasChecksOrRadioButtons(); });
}
-bool MenuItemView::ShouldShowNewBadge() const {
- static const bool feature_enabled =
- base::FeatureList::IsEnabled(features::kEnableNewBadgeOnMenuItems);
- return feature_enabled && is_new_;
-}
-
BEGIN_METADATA(MenuItemView)
METADATA_PARENT_CLASS(View)
END_METADATA()
diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h
index 01b797549ac..3309df7eeee 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.h
+++ b/chromium/ui/views/controls/menu/menu_item_view.h
@@ -117,7 +117,7 @@ class VIEWS_EXPORT MenuItemView : public View {
// Constructor for use with the top level menu item. This menu is never
// shown to the user, rather its use as the parent for all menu items.
- explicit MenuItemView(MenuDelegate* delegate);
+ explicit MenuItemView(MenuDelegate* delegate = nullptr);
// Overridden from View:
base::string16 GetTooltipText(const gfx::Point& p) const override;
@@ -138,7 +138,8 @@ class VIEWS_EXPORT MenuItemView : public View {
// removed and the menu item accelerator text is appended.
static base::string16 GetAccessibleNameForMenuItem(
const base::string16& item_text,
- const base::string16& accelerator_text);
+ const base::string16& accelerator_text,
+ bool is_new_feature);
// Hides and cancels the menu. This does nothing if the menu is not open.
void Cancel();
@@ -353,6 +354,10 @@ class VIEWS_EXPORT MenuItemView : public View {
void SetAlerted();
bool is_alerted() const { return is_alerted_; }
+ // Returns whether or not a "new" badge should be shown on this menu item.
+ // Takes into account whether the badging feature is enabled.
+ bool ShouldShowNewBadge() const;
+
protected:
// Creates a MenuItemView. This is used by the various AddXXX methods.
MenuItemView(MenuItemView* parent, int command, Type type);
@@ -491,10 +496,6 @@ class VIEWS_EXPORT MenuItemView : public View {
// Returns true if the menu has items with a checkbox or a radio button.
bool HasChecksOrRadioButtons() const;
- // Returns whether or not a "new" badge should be shown on this menu item.
- // Takes into account whether the badging feature is enabled.
- bool ShouldShowNewBadge() const;
-
void invalidate_dimensions() { dimensions_.height = 0; }
bool is_dimensions_valid() const { return dimensions_.height > 0; }
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc
index d5db143769b..a350498702e 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl.cc
+++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc
@@ -27,6 +27,12 @@
#include "ui/events/x/events_x_utils.h" // nogncheck
#endif
+#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
+#include "ui/events/event_constants.h"
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
namespace views {
namespace {
@@ -44,11 +50,27 @@ void FireFocusAfterMenuClose(base::WeakPtr<Widget> widget) {
}
}
+#if defined(USE_X11) || defined(USE_OZONE)
+bool IsAltPressed() {
+#if defined(USE_OZONE)
+ if (features::IsUsingOzonePlatform()) {
+ return (ui::OzonePlatform::GetInstance()->GetKeyModifiers() &
+ ui::EF_ALT_DOWN) != 0;
+ }
+#endif
+#if defined(USE_X11)
+ return ui::IsAltPressed();
+#else
+ return false;
+#endif
+}
+#endif // defined(USE_X11) || degined(USE_OZONE)
+
} // namespace
namespace internal {
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
MenuRunnerImplInterface* MenuRunnerImplInterface::Create(
ui::MenuModel* menu_model,
int32_t run_types,
@@ -241,9 +263,9 @@ bool MenuRunnerImpl::ShouldShowMnemonics(int32_t run_types) {
// Show mnemonics if the button has focus or alt is pressed.
#if defined(OS_WIN)
show_mnemonics |= ui::win::IsAltPressed();
-#elif defined(USE_X11)
- show_mnemonics |= ui::IsAltPressed();
-#elif defined(OS_MACOSX)
+#elif defined(USE_X11) || defined(USE_OZONE)
+ show_mnemonics |= IsAltPressed();
+#elif defined(OS_APPLE)
show_mnemonics = false;
#endif
return show_mnemonics;
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
index d24e6915480..ce54769f786 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.h
@@ -14,6 +14,7 @@
#include "ui/views/controls/menu/menu_runner_impl_interface.h"
@class MenuControllerCocoa;
+@class MenuControllerDelegate;
namespace views {
namespace test {
@@ -45,6 +46,9 @@ class VIEWS_EXPORT MenuRunnerImplCocoa : public MenuRunnerImplInterface {
// The Cocoa menu controller that this instance is bridging.
base::scoped_nsobject<MenuControllerCocoa> menu_controller_;
+ // The delegate for the |menu_controller_|.
+ base::scoped_nsobject<MenuControllerDelegate> menu_delegate_;
+
// Are we in run waiting for it to return?
bool running_;
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
index 29c6ad4d742..c51447a72ac 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
@@ -4,17 +4,178 @@
#import "ui/views/controls/menu/menu_runner_impl_cocoa.h"
+#include "base/mac/mac_util.h"
#import "base/message_loop/message_pump_mac.h"
+#import "skia/ext/skia_utils_mac.h"
#import "ui/base/cocoa/cocoa_base_utils.h"
#import "ui/base/cocoa/menu_controller.h"
+#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/models/menu_model.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/mac/coordinate_conversion.h"
+#include "ui/gfx/platform_font_mac.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_runner_impl_adapter.h"
+#include "ui/views/views_features.h"
#include "ui/views/widget/widget.h"
+namespace {
+
+NSImage* NewTagImage() {
+ static NSImage* new_tag = []() {
+ // 1. Make the attributed string.
+
+ NSString* badge_text = l10n_util::GetNSString(IDS_MENU_ITEM_NEW_BADGE);
+
+ // The preferred font is slightly smaller and slightly more bold than the
+ // menu font. The size change is required to make it look correct in the
+ // badge; we add a small degree of bold to prevent color smearing/blurring
+ // due to font smoothing. This ensures readability on all platforms and in
+ // both light and dark modes.
+ gfx::Font badge_font = gfx::Font(
+ new gfx::PlatformFontMac(gfx::PlatformFontMac::SystemFontType::kMenu));
+ badge_font =
+ badge_font.Derive(views::MenuConfig::kNewBadgeFontSizeAdjustment,
+ gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM);
+
+ NSColor* badge_text_color = skia::SkColorToSRGBNSColor(
+ ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+ ui::NativeTheme::kColorId_TextOnProminentButtonColor));
+
+ NSDictionary* badge_attrs = @{
+ NSFontAttributeName : badge_font.GetNativeFont(),
+ NSForegroundColorAttributeName : badge_text_color,
+ };
+
+ NSMutableAttributedString* badge_attr_string =
+ [[NSMutableAttributedString alloc] initWithString:badge_text
+ attributes:badge_attrs];
+
+ if (base::mac::IsOS10_10()) {
+ // The system font for 10.10 is Helvetica Neue, and when used for this
+ // "new tag" the letters look cramped. Track it out so that there's some
+ // breathing room. There is no tracking attribute, so instead add kerning
+ // to all but the last character.
+ [badge_attr_string
+ addAttribute:NSKernAttributeName
+ value:@0.4
+ range:NSMakeRange(0, [badge_attr_string length] - 1)];
+ }
+
+ // 2. Calculate the size required.
+
+ NSSize badge_size = [badge_attr_string size];
+ badge_size.width = trunc(badge_size.width);
+ badge_size.height = trunc(badge_size.height);
+
+ badge_size.width += 2 * views::MenuConfig::kNewBadgeInternalPadding +
+ 2 * views::MenuConfig::kNewBadgeHorizontalMargin;
+ badge_size.height += views::MenuConfig::kNewBadgeInternalPaddingTopMac;
+
+ // 3. Craft the image.
+
+ return [[NSImage
+ imageWithSize:badge_size
+ flipped:NO
+ drawingHandler:^(NSRect dest_rect) {
+ NSRect badge_frame = NSInsetRect(
+ dest_rect, views::MenuConfig::kNewBadgeHorizontalMargin, 0);
+ NSBezierPath* rounded_badge_rect = [NSBezierPath
+ bezierPathWithRoundedRect:badge_frame
+ xRadius:views::MenuConfig::kNewBadgeCornerRadius
+ yRadius:views::MenuConfig::
+ kNewBadgeCornerRadius];
+ NSColor* badge_color = skia::SkColorToSRGBNSColor(
+ ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+ ui::NativeTheme::kColorId_ProminentButtonColor));
+ [badge_color set];
+ [rounded_badge_rect fill];
+
+ NSPoint badge_text_location = NSMakePoint(
+ NSMinX(badge_frame) + views::MenuConfig::kNewBadgeInternalPadding,
+ NSMinY(badge_frame) +
+ views::MenuConfig::kNewBadgeInternalPaddingTopMac);
+ [badge_attr_string drawAtPoint:badge_text_location];
+
+ return YES;
+ }] retain];
+ }();
+
+ return new_tag;
+}
+
+} // namespace
+
+@interface NewTagAttachmentCell : NSTextAttachmentCell
+@end
+
+@implementation NewTagAttachmentCell
+
+- (instancetype)init {
+ if (self = [super init]) {
+ self.image = NewTagImage();
+ }
+ return self;
+}
+
+- (NSPoint)cellBaselineOffset {
+ return NSMakePoint(0, views::MenuConfig::kNewBadgeBaslineOffsetMac);
+}
+
+- (NSSize)cellSize {
+ return [self.image size];
+}
+
+@end
+
+@interface MenuControllerDelegate : NSObject <MenuControllerCocoaDelegate>
+@end
+
+@implementation MenuControllerDelegate
+
+- (void)controllerWillAddItem:(NSMenuItem*)menuItem
+ fromModel:(ui::MenuModel*)model
+ atIndex:(NSInteger)index {
+ static const bool feature_enabled =
+ base::FeatureList::IsEnabled(views::features::kEnableNewBadgeOnMenuItems);
+ if (!feature_enabled || !model->IsNewFeatureAt(index))
+ return;
+
+ // TODO(avi): When moving to 10.11 as the minimum macOS, switch to using
+ // NSTextAttachment's |image| and |bounds| properties and avoid the whole
+ // NSTextAttachmentCell subclassing mishegas.
+ base::scoped_nsobject<NSTextAttachment> attachment(
+ [[NSTextAttachment alloc] init]);
+ attachment.get().attachmentCell =
+ [[[NewTagAttachmentCell alloc] init] autorelease];
+
+ // Starting in 10.13, if an attributed string is set as a menu item title, and
+ // NSFontAttributeName is not specified for it, it is automatically rendered
+ // in a font matching other menu items. Prior to then, a menu item with no
+ // specified font is rendered in Helvetica. In addition, while the
+ // documentation says that -[NSFont menuFontOfSize:0] gives the standard menu
+ // font, that doesn't actually match up. Therefore, specify a font that
+ // visually matches.
+ NSDictionary* attrs = nil;
+ if (base::mac::IsAtMostOS10_12())
+ attrs = @{NSFontAttributeName : [NSFont menuFontOfSize:14]};
+
+ base::scoped_nsobject<NSMutableAttributedString> attrTitle(
+ [[NSMutableAttributedString alloc] initWithString:menuItem.title
+ attributes:attrs]);
+ [attrTitle
+ appendAttributedString:[NSAttributedString
+ attributedStringWithAttachment:attachment]];
+
+ menuItem.attributedTitle = attrTitle;
+}
+
+@end
+
namespace views {
namespace internal {
namespace {
@@ -124,7 +285,7 @@ MenuRunnerImplInterface* MenuRunnerImplInterface::Create(
int32_t run_types,
base::RepeatingClosure on_menu_closed_callback) {
if ((run_types & MenuRunner::CONTEXT_MENU) &&
- !(run_types & MenuRunner::IS_NESTED)) {
+ !(run_types & (MenuRunner::IS_NESTED))) {
return new MenuRunnerImplCocoa(menu_model,
std::move(on_menu_closed_callback));
}
@@ -139,8 +300,11 @@ MenuRunnerImplCocoa::MenuRunnerImplCocoa(
delete_after_run_(false),
closing_event_time_(base::TimeTicks()),
on_menu_closed_callback_(std::move(on_menu_closed_callback)) {
- menu_controller_.reset([[MenuControllerCocoa alloc] initWithModel:menu
- useWithPopUpButtonCell:NO]);
+ menu_delegate_.reset([[MenuControllerDelegate alloc] init]);
+ menu_controller_.reset([[MenuControllerCocoa alloc]
+ initWithModel:menu
+ delegate:menu_delegate_.get()
+ useWithPopUpButtonCell:NO]);
}
bool MenuRunnerImplCocoa::IsRunning() const {
diff --git a/chromium/ui/views/controls/menu/menu_runner_unittest.cc b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
index 193bc0af4ce..9c8e6348fcc 100644
--- a/chromium/ui/views/controls/menu/menu_runner_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
@@ -257,7 +257,7 @@ TEST_F(MenuRunnerTest, PrefixSelect) {
// This test is Mac-specific: Mac is the only platform where VKEY_SPACE
// activates menu items.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
TEST_F(MenuRunnerTest, SpaceActivatesItem) {
if (!MenuConfig::instance().all_menus_use_prefix_selection)
return;
@@ -282,7 +282,7 @@ TEST_F(MenuRunnerTest, SpaceActivatesItem) {
EXPECT_EQ(1, delegate->on_menu_closed_called());
EXPECT_NE(nullptr, delegate->on_menu_closed_menu());
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
// Tests that attempting to nest a menu within a drag-and-drop menu does not
// cause a crash. Instead the drag and drop action should be canceled, and the
@@ -595,11 +595,12 @@ TEST_F(MenuRunnerImplTest, FocusOnMenuClose) {
new internal::MenuRunnerImpl(menu_item_view());
// Create test button that has focus.
+ auto button_managed = std::make_unique<LabelButton>();
+ button_managed->SetID(1);
+ button_managed->SetSize(gfx::Size(20, 20));
LabelButton* button =
- new LabelButton(nullptr, base::string16(), style::CONTEXT_BUTTON);
- button->SetID(1);
- button->SetSize(gfx::Size(20, 20));
- owner()->GetRootView()->AddChildView(button);
+ owner()->GetRootView()->AddChildView(std::move(button_managed));
+
button->SetFocusBehavior(View::FocusBehavior::ALWAYS);
button->GetWidget()->widget_delegate()->SetCanActivate(true);
button->GetWidget()->Activate();
diff --git a/chromium/ui/views/controls/menu/menu_separator.cc b/chromium/ui/views/controls/menu/menu_separator.cc
index b48d80919ce..712460c1593 100644
--- a/chromium/ui/views/controls/menu/menu_separator.cc
+++ b/chromium/ui/views/controls/menu/menu_separator.cc
@@ -88,8 +88,21 @@ gfx::Size MenuSeparator::CalculatePreferredSize() const {
height);
}
+ui::MenuSeparatorType MenuSeparator::GetType() const {
+ return type_;
+}
+
+void MenuSeparator::SetType(ui::MenuSeparatorType type) {
+ if (type_ == type)
+ return;
+
+ type_ = type;
+ OnPropertyChanged(&type_, kPropertyEffectsPreferredSizeChanged);
+}
+
BEGIN_METADATA(MenuSeparator)
METADATA_PARENT_CLASS(View)
+ADD_PROPERTY_METADATA(MenuSeparator, ui::MenuSeparatorType, Type)
END_METADATA()
} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_separator.h b/chromium/ui/views/controls/menu/menu_separator.h
index a331914d44a..18566efae79 100644
--- a/chromium/ui/views/controls/menu/menu_separator.h
+++ b/chromium/ui/views/controls/menu/menu_separator.h
@@ -8,6 +8,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "ui/base/models/menu_separator_types.h"
+#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
#include "ui/views/views_export.h"
@@ -17,15 +18,20 @@ class VIEWS_EXPORT MenuSeparator : public View {
public:
METADATA_HEADER(MenuSeparator);
- explicit MenuSeparator(ui::MenuSeparatorType type) : type_(type) {}
+ explicit MenuSeparator(
+ ui::MenuSeparatorType type = ui::MenuSeparatorType::NORMAL_SEPARATOR)
+ : type_(type) {}
// View overrides.
void OnPaint(gfx::Canvas* canvas) override;
gfx::Size CalculatePreferredSize() const override;
+ ui::MenuSeparatorType GetType() const;
+ void SetType(ui::MenuSeparatorType type);
+
private:
// The type of the separator.
- const ui::MenuSeparatorType type_;
+ ui::MenuSeparatorType type_;
DISALLOW_COPY_AND_ASSIGN(MenuSeparator);
};
diff --git a/chromium/ui/views/controls/menu/menu_separator_unittest.cc b/chromium/ui/views/controls/menu/menu_separator_unittest.cc
new file mode 100644
index 00000000000..a046f8f16ef
--- /dev/null
+++ b/chromium/ui/views/controls/menu/menu_separator_unittest.cc
@@ -0,0 +1,35 @@
+// 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 "ui/views/controls/menu/menu_separator.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/models/menu_separator_types.h"
+#include "ui/views/controls/menu/menu_config.h"
+#include "ui/views/test/view_metadata_test_utils.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace views {
+
+using MenuSeparatorTest = ViewsTestBase;
+
+TEST_F(MenuSeparatorTest, Metadata) {
+ auto separator = std::make_unique<MenuSeparator>();
+ test::TestViewMetadata(separator.get());
+}
+
+TEST_F(MenuSeparatorTest, TypeChangeEffect) {
+ auto separator = std::make_unique<MenuSeparator>();
+ separator->SizeToPreferredSize();
+ const MenuConfig& config = MenuConfig::instance();
+ EXPECT_EQ(config.separator_height, separator->height());
+
+ separator->SetType(ui::MenuSeparatorType::DOUBLE_SEPARATOR);
+ separator->SizeToPreferredSize();
+ EXPECT_EQ(config.double_separator_height, separator->height());
+}
+
+} // namespace views
diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc
index 28a9c908988..554f1d2baec 100644
--- a/chromium/ui/views/controls/menu/submenu_view.cc
+++ b/chromium/ui/views/controls/menu/submenu_view.cc
@@ -9,13 +9,13 @@
#include <set>
#include "base/compiler_specific.h"
+#include "base/numerics/safe_conversions.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/ime/input_method.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller.h"
@@ -373,8 +373,9 @@ void SubmenuView::SetSelectedRow(int row) {
}
base::string16 SubmenuView::GetTextForRow(int row) {
- return MenuItemView::GetAccessibleNameForMenuItem(GetMenuItemAt(row)->title(),
- base::string16());
+ return MenuItemView::GetAccessibleNameForMenuItem(
+ GetMenuItemAt(row)->title(), base::string16(),
+ GetMenuItemAt(row)->ShouldShowNewBadge());
}
bool SubmenuView::IsShowing() const {
@@ -540,7 +541,7 @@ bool SubmenuView::OnScroll(float dx, float dy) {
const gfx::Rect& full_bounds = bounds();
int x = vis_bounds.x();
float y_f = vis_bounds.y() - dy - roundoff_error_;
- int y = gfx::ToRoundedInt(y_f);
+ int y = base::ClampRound(y_f);
roundoff_error_ = y - y_f;
// clamp y to [0, full_height - vis_height)
y = std::min(y, full_bounds.height() - vis_bounds.height() - 1);
diff --git a/chromium/ui/views/controls/menu/submenu_view_unittest.cc b/chromium/ui/views/controls/menu/submenu_view_unittest.cc
index 2816426be56..f277cd1092d 100644
--- a/chromium/ui/views/controls/menu/submenu_view_unittest.cc
+++ b/chromium/ui/views/controls/menu/submenu_view_unittest.cc
@@ -11,7 +11,7 @@
namespace views {
TEST(SubmenuViewTest, GetLastItem) {
- MenuItemView* parent = new MenuItemView(nullptr);
+ MenuItemView* parent = new MenuItemView();
MenuRunner menu_runner(parent, 0);
SubmenuView* submenu = parent->CreateSubmenu();
@@ -20,14 +20,14 @@ TEST(SubmenuViewTest, GetLastItem) {
submenu->AddChildView(new View());
EXPECT_EQ(nullptr, submenu->GetLastItem());
- MenuItemView* first = new MenuItemView(nullptr);
+ MenuItemView* first = new MenuItemView();
submenu->AddChildView(first);
EXPECT_EQ(first, submenu->GetLastItem());
submenu->AddChildView(new View());
EXPECT_EQ(first, submenu->GetLastItem());
- MenuItemView* second = new MenuItemView(nullptr);
+ MenuItemView* second = new MenuItemView();
submenu->AddChildView(second);
EXPECT_EQ(second, submenu->GetLastItem());
}
diff --git a/chromium/ui/views/controls/menu/test_menu_item_view.cc b/chromium/ui/views/controls/menu/test_menu_item_view.cc
index 76cb42ca116..7329b162b87 100644
--- a/chromium/ui/views/controls/menu/test_menu_item_view.cc
+++ b/chromium/ui/views/controls/menu/test_menu_item_view.cc
@@ -6,7 +6,7 @@
namespace views {
-TestMenuItemView::TestMenuItemView() : MenuItemView(nullptr) {}
+TestMenuItemView::TestMenuItemView() = default;
TestMenuItemView::TestMenuItemView(MenuDelegate* delegate)
: MenuItemView(delegate) {}
diff --git a/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm b/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm
index 9126b956b94..0d125253db1 100644
--- a/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm
+++ b/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm
@@ -8,6 +8,7 @@
#include <memory>
+#include "base/mac/mac_util.h"
#import "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#import "testing/gtest_mac.h"
@@ -161,13 +162,17 @@ TEST_F(NativeViewHostMacTest, ContentViewPositionAndSize) {
CreateHost();
toplevel()->SetBounds(gfx::Rect(0, 0, 100, 100));
- // TODO(amp): Update expect rect after Mac native size is implemented.
- // For now the native size is ignored on mac.
+ // The new visual style on macOS 11 (and presumably later) has slightly taller
+ // titlebars, which means the window rect has to leave a bit of extra space
+ // for the titlebar.
+ int titlebar_extra = base::mac::IsAtLeastOS11() ? 6 : 0;
+
native_host()->ShowWidget(5, 10, 100, 100, 200, 200);
- EXPECT_NSEQ(NSMakeRect(5, -32, 100, 100), [native_view_ frame]);
+ EXPECT_NSEQ(NSMakeRect(5, -32 - titlebar_extra, 100, 100),
+ [native_view_ frame]);
native_host()->ShowWidget(10, 25, 50, 50, 50, 50);
- EXPECT_NSEQ(NSMakeRect(10, 3, 50, 50), [native_view_ frame]);
+ EXPECT_NSEQ(NSMakeRect(10, 3 - titlebar_extra, 50, 50), [native_view_ frame]);
DestroyHost();
}
diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc
index aa0d2fe8423..c0cc92ea180 100644
--- a/chromium/ui/views/controls/prefix_selector.cc
+++ b/chromium/ui/views/controls/prefix_selector.cc
@@ -42,7 +42,9 @@ bool PrefixSelector::ShouldContinueSelection() const {
void PrefixSelector::SetCompositionText(
const ui::CompositionText& composition) {}
-void PrefixSelector::ConfirmCompositionText(bool keep_selection) {}
+uint32_t PrefixSelector::ConfirmCompositionText(bool keep_selection) {
+ return UINT32_MAX;
+}
void PrefixSelector::ClearCompositionText() {}
@@ -172,12 +174,26 @@ bool PrefixSelector::SetCompositionFromExistingText(
#endif
#if defined(OS_CHROMEOS)
+gfx::Range PrefixSelector::GetAutocorrectRange() const {
+ NOTIMPLEMENTED_LOG_ONCE();
+ return gfx::Range();
+}
+
+gfx::Rect PrefixSelector::GetAutocorrectCharacterBounds() const {
+ NOTIMPLEMENTED_LOG_ONCE();
+ return gfx::Rect();
+}
+
bool PrefixSelector::SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) {
// TODO(crbug.com/1091088) Implement setAutocorrectRange.
NOTIMPLEMENTED_LOG_ONCE();
return false;
}
+
+void PrefixSelector::ClearAutocorrectRange() {
+ // TODO(crbug.com/1091088) Implement ClearAutocorrectRange.
+}
#endif
#if defined(OS_WIN)
diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h
index 12dffcc111b..9df6de750fb 100644
--- a/chromium/ui/views/controls/prefix_selector.h
+++ b/chromium/ui/views/controls/prefix_selector.h
@@ -44,7 +44,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
// ui::TextInputClient:
void SetCompositionText(const ui::CompositionText& composition) override;
- void ConfirmCompositionText(bool keep_selection) override;
+ uint32_t ConfirmCompositionText(bool keep_selection) override;
void ClearCompositionText() override;
void InsertText(const base::string16& text) override;
void InsertChar(const ui::KeyEvent& event) override;
@@ -83,8 +83,11 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
#endif
#if defined(OS_CHROMEOS)
+ gfx::Range GetAutocorrectRange() const override;
+ gfx::Rect GetAutocorrectCharacterBounds() const override;
bool SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) override;
+ void ClearAutocorrectRange() override;
#endif
#if defined(OS_WIN)
diff --git a/chromium/ui/views/controls/resize_area_unittest.cc b/chromium/ui/views/controls/resize_area_unittest.cc
index 25725fdaf73..aaf484683d4 100644
--- a/chromium/ui/views/controls/resize_area_unittest.cc
+++ b/chromium/ui/views/controls/resize_area_unittest.cc
@@ -17,7 +17,7 @@
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_utils.h"
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
#include "ui/aura/window.h"
#endif
@@ -148,7 +148,7 @@ void ResizeAreaTest::TearDown() {
}
// TODO(tdanderson): Enable these tests on OSX. See crbug.com/710475.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Verifies the correct calls have been made to
// TestResizeAreaDelegate::OnResize() for a sequence of mouse events
// corresponding to a successful resize operation.
@@ -202,6 +202,6 @@ TEST_F(ResizeAreaTest, NoDragOnGestureTap) {
EXPECT_EQ(0, resize_amount());
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
} // namespace views
diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc
index 8f1244d79c2..c161189c9fb 100644
--- a/chromium/ui/views/controls/scroll_view.cc
+++ b/chromium/ui/views/controls/scroll_view.cc
@@ -12,6 +12,9 @@
#include "base/macros.h"
#include "base/numerics/ranges.h"
#include "build/build_config.h"
+#include "ui/accessibility/ax_action_data.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/base/ui_base_features.h"
#include "ui/compositor/overscroll/scroll_input_handler.h"
#include "ui/events/event.h"
@@ -377,7 +380,7 @@ void ScrollView::Layout() {
// When horizontal scrollbar is disabled, it should not matter
// if its OverlapsContent matches vertical bar's.
if (!hide_horizontal_scrollbar_) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, scrollbars may update their style one at a time, so they may
// temporarily be of different types. Refuse to lay out at this point.
if (horiz_sb_->OverlapsContent() != vert_sb_->OverlapsContent())
@@ -641,6 +644,70 @@ void ScrollView::OnThemeChanged() {
UpdateBackground();
}
+void ScrollView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+ View::GetAccessibleNodeData(node_data);
+ if (!contents_)
+ return;
+
+ ScrollBar* horizontal = horizontal_scroll_bar();
+ if (horizontal) {
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollX,
+ CurrentOffset().x());
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin,
+ horizontal->GetMinPosition());
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollXMax,
+ horizontal->GetMaxPosition());
+ }
+ ScrollBar* vertical = vertical_scroll_bar();
+ if (vertical) {
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollY,
+ CurrentOffset().y());
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollYMin,
+ vertical->GetMinPosition());
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollYMax,
+ vertical->GetMaxPosition());
+ }
+ if (horizontal || vertical)
+ node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kScrollable, true);
+}
+
+bool ScrollView::HandleAccessibleAction(const ui::AXActionData& action_data) {
+ if (!contents_)
+ return View::HandleAccessibleAction(action_data);
+
+ ScrollBar* horizontal = horizontal_scroll_bar();
+ ScrollBar* vertical = vertical_scroll_bar();
+ switch (action_data.action) {
+ case ax::mojom::Action::kScrollLeft:
+ if (horizontal)
+ return horizontal->ScrollByAmount(ScrollBar::ScrollAmount::kPrevPage);
+ else
+ return false;
+ case ax::mojom::Action::kScrollRight:
+ if (horizontal)
+ return horizontal->ScrollByAmount(ScrollBar::ScrollAmount::kNextPage);
+ else
+ return false;
+ case ax::mojom::Action::kScrollUp:
+ if (vertical)
+ return vertical->ScrollByAmount(ScrollBar::ScrollAmount::kPrevPage);
+ else
+ return false;
+ case ax::mojom::Action::kScrollDown:
+ if (vertical)
+ return vertical->ScrollByAmount(ScrollBar::ScrollAmount::kNextPage);
+ else
+ return false;
+ case ax::mojom::Action::kSetScrollOffset:
+ ScrollToOffset(gfx::ScrollOffset(action_data.target_point.x(),
+ action_data.target_point.y()));
+ return true;
+ default:
+ return View::HandleAccessibleAction(action_data);
+ break;
+ }
+}
+
void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
if (!contents_)
return;
diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h
index 54b1e11e5ce..25664deff7d 100644
--- a/chromium/ui/views/controls/scroll_view.h
+++ b/chromium/ui/views/controls/scroll_view.h
@@ -143,6 +143,8 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
void OnScrollEvent(ui::ScrollEvent* event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
void OnThemeChanged() override;
+ void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+ bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
// ScrollBarController overrides:
void ScrollToPosition(ScrollBar* source, int position) override;
diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc
index 0359bc89ed3..6213feb7355 100644
--- a/chromium/ui/views/controls/scroll_view_unittest.cc
+++ b/chromium/ui/views/controls/scroll_view_unittest.cc
@@ -31,7 +31,7 @@
#include "ui/views/test/widget_test.h"
#include "ui/views/view_test_api.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/test/scoped_preferred_scroller_style_mac.h"
#endif
@@ -237,7 +237,7 @@ class ScrollViewTest : public ViewsTestBase {
}
protected:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
void SetOverlayScrollersEnabled(bool enabled) {
// Ensure the old scroller override is destroyed before creating a new one.
// Otherwise, the swizzlers are interleaved and restore incorrect methods.
@@ -285,7 +285,7 @@ class WidgetScrollViewTest : public test::WidgetTest,
// Adds a ScrollView with the given |contents_view| and does layout.
ScrollView* AddScrollViewWithContents(std::unique_ptr<View> contents,
bool commit_layers = true) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
scroller_style_ = std::make_unique<ui::test::ScopedPreferredScrollerStyle>(
use_overlay_scrollers_);
#endif
@@ -363,7 +363,7 @@ class WidgetScrollViewTest : public test::WidgetTest,
base::RepeatingClosure quit_closure_;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
std::unique_ptr<ui::test::ScopedPreferredScrollerStyle> scroller_style_;
#endif
@@ -1027,7 +1027,7 @@ TEST_F(ScrollViewTest, DontCreateLayerOnViewportIfLayerOnScrollViewCreated) {
EXPECT_FALSE(test_api.contents_viewport()->layer());
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Tests the overlay scrollbars on Mac. Ensure that they show up properly and
// do not overlap each other.
TEST_F(ScrollViewTest, CocoaOverlayScrollBars) {
@@ -1193,7 +1193,7 @@ TEST_F(WidgetScrollViewTest, ScrollersOnRest) {
EXPECT_EQ(gfx::ScrollOffset(x_offset, y_offset), test_api.CurrentOffset());
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
// Test that increasing the size of the viewport "below" scrolled content causes
// the content to scroll up so that it still fills the viewport.
diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc
index 122d1c13caa..e16c7b3f6d6 100644
--- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc
@@ -11,10 +11,12 @@
namespace views {
-BaseScrollBarButton::BaseScrollBarButton(ButtonListener* listener)
+BaseScrollBarButton::BaseScrollBarButton(ButtonListener* listener,
+ const base::TickClock* tick_clock)
: Button(listener),
repeater_(base::BindRepeating(&BaseScrollBarButton::RepeaterNotifyClick,
- base::Unretained(this))) {}
+ base::Unretained(this)),
+ tick_clock) {}
BaseScrollBarButton::~BaseScrollBarButton() = default;
diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h
index 052f5f1f62f..b055ffd8130 100644
--- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h
@@ -11,6 +11,10 @@
#include "build/build_config.h"
#include "ui/views/repeat_controller.h"
+namespace base {
+class TickClock;
+}
+
namespace views {
///////////////////////////////////////////////////////////////////////////////
@@ -26,7 +30,8 @@ class VIEWS_EXPORT BaseScrollBarButton : public Button {
public:
METADATA_HEADER(BaseScrollBarButton);
- explicit BaseScrollBarButton(ButtonListener* listener);
+ explicit BaseScrollBarButton(ButtonListener* listener,
+ const base::TickClock* tick_clock = nullptr);
~BaseScrollBarButton() override;
protected:
diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button_unittest.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button_unittest.cc
new file mode 100644
index 00000000000..9c5f679c17a
--- /dev/null
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button_unittest.cc
@@ -0,0 +1,134 @@
+// 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 "ui/views/controls/scrollbar/base_scroll_bar_button.h"
+
+#include <memory>
+
+#include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/test/scoped_screen_override.h"
+#include "ui/display/test/test_screen.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/views/repeat_controller.h"
+#include "ui/views/test/view_metadata_test_utils.h"
+
+namespace views {
+
+namespace {
+
+using testing::_;
+using testing::AtLeast;
+using testing::AtMost;
+
+class MockButtonListener : public ButtonListener {
+ public:
+ MockButtonListener() = default;
+ MockButtonListener(const MockButtonListener&) = delete;
+ MockButtonListener& operator=(const MockButtonListener&) = delete;
+ ~MockButtonListener() override = default;
+
+ // ButtonListener:
+ MOCK_METHOD(void,
+ ButtonPressed,
+ (Button * sender, const ui::Event& event),
+ (override));
+};
+
+class BaseScrollBarButtonTest : public testing::Test {
+ public:
+ BaseScrollBarButtonTest()
+ : button_(std::make_unique<BaseScrollBarButton>(
+ &listener_,
+ task_environment_.GetMockTickClock())) {}
+
+ ~BaseScrollBarButtonTest() override = default;
+
+ protected:
+ testing::StrictMock<MockButtonListener>& listener() { return listener_; }
+ Button* button() { return button_.get(); }
+
+ void AdvanceTime(base::TimeDelta time_delta) {
+ task_environment_.FastForwardBy(time_delta);
+ }
+
+ private:
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ display::test::TestScreen test_screen_;
+ display::test::ScopedScreenOverride screen_override{&test_screen_};
+
+ testing::StrictMock<MockButtonListener> listener_;
+ const std::unique_ptr<Button> button_;
+};
+
+} // namespace
+
+TEST_F(BaseScrollBarButtonTest, Metadata) {
+ test::TestViewMetadata(button());
+}
+
+TEST_F(BaseScrollBarButtonTest, CallbackFiresOnMouseDown) {
+ EXPECT_CALL(listener(), ButtonPressed(_, _));
+
+ // By default the button should notify its listener on mouse release.
+ button()->OnMousePressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+}
+
+TEST_F(BaseScrollBarButtonTest, CallbackFilesMultipleTimesMouseHeldDown) {
+ EXPECT_CALL(listener(), ButtonPressed(_, _)).Times(AtLeast(2));
+
+ // By default the button should notify its listener on mouse release.
+ button()->OnMousePressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() * 10);
+}
+
+TEST_F(BaseScrollBarButtonTest, CallbackStopsFiringAfterMouseReleased) {
+ EXPECT_CALL(listener(), ButtonPressed(_, _)).Times(AtLeast(2));
+
+ // By default the button should notify its listener on mouse release.
+ button()->OnMousePressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() * 10);
+
+ testing::Mock::VerifyAndClearExpectations(&listener());
+
+ button()->OnMouseReleased(ui::MouseEvent(
+ ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() * 10);
+
+ EXPECT_CALL(listener(), ButtonPressed(_, _)).Times(AtMost(0));
+}
+
+TEST_F(BaseScrollBarButtonTest, CallbackStopsFiringAfterMouseCaptureReleased) {
+ EXPECT_CALL(listener(), ButtonPressed(_, _)).Times(AtLeast(2));
+
+ // By default the button should notify its listener on mouse release.
+ button()->OnMousePressed(ui::MouseEvent(
+ ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() * 10);
+
+ testing::Mock::VerifyAndClearExpectations(&listener());
+
+ button()->OnMouseCaptureLost();
+
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() * 10);
+
+ EXPECT_CALL(listener(), ButtonPressed(_, _)).Times(AtMost(0));
+}
+
+} // namespace views
diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.cc b/chromium/ui/views/controls/scrollbar/scroll_bar.cc
index 4283f6783ee..1655bf7252c 100644
--- a/chromium/ui/views/controls/scrollbar/scroll_bar.cc
+++ b/chromium/ui/views/controls/scrollbar/scroll_bar.cc
@@ -14,6 +14,7 @@
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "base/numerics/ranges.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -23,7 +24,6 @@
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
@@ -158,11 +158,11 @@ void ScrollBar::OnGestureEvent(ui::GestureEvent* event) {
int scroll_amount;
if (IsHorizontal()) {
scroll_amount_f = event->details().scroll_x() - roundoff_error_.x();
- scroll_amount = gfx::ToRoundedInt(scroll_amount_f);
+ scroll_amount = base::ClampRound(scroll_amount_f);
roundoff_error_.set_x(scroll_amount - scroll_amount_f);
} else {
scroll_amount_f = event->details().scroll_y() - roundoff_error_.y();
- scroll_amount = gfx::ToRoundedInt(scroll_amount_f);
+ scroll_amount = base::ClampRound(scroll_amount_f);
roundoff_error_.set_y(scroll_amount - scroll_amount_f);
}
if (ScrollByContentsOffset(scroll_amount))
@@ -310,7 +310,7 @@ void ScrollBar::Update(int viewport_size,
// content size multiplied by the height of the thumb track.
float ratio =
std::min<float>(1.0, static_cast<float>(viewport_size) / contents_size_);
- thumb_->SetLength(gfx::ToRoundedInt(ratio * GetTrackSize()));
+ thumb_->SetLength(base::ClampRound(ratio * GetTrackSize()));
int thumb_position = CalculateThumbPosition(contents_scroll_offset);
thumb_->SetPosition(thumb_position);
@@ -343,7 +343,7 @@ ScrollBar::ScrollBar(bool is_horiz)
///////////////////////////////////////////////////////////////////////////////
// ScrollBar, private:
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// static
base::RetainingOneShotTimer* ScrollBar::GetHideTimerForTesting(
ScrollBar* scroll_bar) {
@@ -409,7 +409,7 @@ int ScrollBar::CalculateContentsOffset(float thumb_position,
thumb_position = thumb_position - (thumb_size / 2);
float result = (thumb_position * (contents_size_ - viewport_size_)) /
(track_size - thumb_size);
- return gfx::ToRoundedInt(result);
+ return base::ClampRound(result);
}
void ScrollBar::SetContentsScrollOffset(int contents_scroll_offset) {
diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.h b/chromium/ui/views/controls/scrollbar/scroll_bar.h
index cc3ec682be6..264db19019e 100644
--- a/chromium/ui/views/controls/scrollbar/scroll_bar.h
+++ b/chromium/ui/views/controls/scrollbar/scroll_bar.h
@@ -186,6 +186,9 @@ class VIEWS_EXPORT ScrollBar : public View,
friend class test::ScrollViewTestApi;
FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, ScrollBarFitsToBottom);
FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, ThumbFullLengthOfTrack);
+ FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, DragThumbScrollsContent);
+ FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, RightClickOpensMenu);
+ FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, TestPageScrollingByPress);
static base::RetainingOneShotTimer* GetHideTimerForTesting(
ScrollBar* scroll_bar);
diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc
index 5da0afbd601..7d85eb0ac04 100644
--- a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc
+++ b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc
@@ -95,7 +95,7 @@ void ScrollBarButton::PaintButtonContents(gfx::Canvas* canvas) {
ui::NativeTheme::ExtraParams ScrollBarButton::GetNativeThemeParams() const {
ui::NativeTheme::ExtraParams params;
- switch (state()) {
+ switch (GetState()) {
case Button::STATE_HOVERED:
params.scrollbar_arrow.is_hovering = true;
break;
@@ -124,7 +124,7 @@ ui::NativeTheme::Part ScrollBarButton::GetNativeThemePart() const {
}
ui::NativeTheme::State ScrollBarButton::GetNativeThemeState() const {
- switch (state()) {
+ switch (GetState()) {
case Button::STATE_HOVERED:
return ui::NativeTheme::kHovered;
case Button::STATE_PRESSED:
diff --git a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc
index 32933a471ea..12e55b83968 100644
--- a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc
+++ b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc
@@ -4,11 +4,20 @@
#include <memory>
+#include "base/time/time.h"
#include "build/build_config.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"
#include "ui/views/controls/scrollbar/scroll_bar_views.h"
+#include "ui/views/test/view_metadata_test_utils.h"
#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_utils.h"
namespace {
@@ -43,6 +52,20 @@ class TestScrollBarController : public views::ScrollBarController {
int last_position;
};
+// This container is used to forward gesture events to the scrollbar for
+// testing fling and other gestures.
+class TestScrollbarContainer : public views::View {
+ public:
+ TestScrollbarContainer() = default;
+ ~TestScrollbarContainer() override = default;
+ TestScrollbarContainer(const TestScrollbarContainer&) = delete;
+ TestScrollbarContainer& operator=(const TestScrollbarContainer&) = delete;
+
+ void OnGestureEvent(ui::GestureEvent* event) override {
+ children()[0]->OnGestureEvent(event);
+ }
+};
+
} // namespace
namespace views {
@@ -55,29 +78,30 @@ class ScrollBarViewsTest : public ViewsTestBase {
ViewsTestBase::SetUp();
controller_ = std::make_unique<TestScrollBarController>();
- widget_ = new Widget;
+ widget_ = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.bounds = gfx::Rect(0, 0, 100, 100);
+ params.bounds = gfx::Rect(0, 0, 100, 300);
widget_->Init(std::move(params));
- View* container = new View();
- widget_->SetContentsView(container);
+ widget_->Show();
+ auto* container =
+ widget_->SetContentsView(std::make_unique<TestScrollbarContainer>());
- scrollbar_ = new ScrollBarViews(true);
+ scrollbar_ =
+ container->AddChildView(std::make_unique<ScrollBarViews>(true));
scrollbar_->SetBounds(0, 0, 100, 100);
scrollbar_->Update(100, 1000, 0);
scrollbar_->set_controller(controller_.get());
- container->AddChildView(scrollbar_);
track_size_ = scrollbar_->GetTrackBounds().width();
}
void TearDown() override {
- widget_->Close();
+ widget_.reset();
ViewsTestBase::TearDown();
}
protected:
- Widget* widget_ = nullptr;
+ UniqueWidgetPtr widget_;
// This is the Views scrollbar.
ScrollBar* scrollbar_ = nullptr;
@@ -89,6 +113,11 @@ class ScrollBarViewsTest : public ViewsTestBase {
std::unique_ptr<TestScrollBarController> controller_;
};
+// Verify properties are accessible via metadata.
+TEST_F(ScrollBarViewsTest, MetaDataTest) {
+ test::TestViewMetadata(scrollbar_);
+}
+
TEST_F(ScrollBarViewsTest, Scrolling) {
EXPECT_EQ(0, scrollbar_->GetPosition());
EXPECT_EQ(900, scrollbar_->GetMaxPosition());
@@ -182,4 +211,53 @@ TEST_F(ScrollBarViewsTest, ThumbFullLengthOfTrack) {
EXPECT_EQ(0, scrollbar_->GetPosition());
}
+TEST_F(ScrollBarViewsTest, RightClickOpensMenu) {
+ EXPECT_EQ(nullptr, scrollbar_->menu_model_);
+ EXPECT_EQ(nullptr, scrollbar_->menu_runner_);
+ scrollbar_->set_context_menu_controller(scrollbar_);
+ scrollbar_->ShowContextMenu(scrollbar_->GetBoundsInScreen().CenterPoint(),
+ ui::MENU_SOURCE_MOUSE);
+ EXPECT_NE(nullptr, scrollbar_->menu_model_);
+ EXPECT_NE(nullptr, scrollbar_->menu_runner_);
+}
+
+#if !defined(OS_APPLE)
+TEST_F(ScrollBarViewsTest, TestPageScrollingByPress) {
+ ui::test::EventGenerator generator(GetRootWindow(widget_.get()));
+ EXPECT_EQ(0, scrollbar_->GetPosition());
+ generator.MoveMouseTo(
+ scrollbar_->GetThumb()->GetBoundsInScreen().right_center() +
+ gfx::Vector2d(4, 0));
+ generator.ClickLeftButton();
+ generator.ClickLeftButton();
+ EXPECT_GT(scrollbar_->GetPosition(), 0);
+}
+
+TEST_F(ScrollBarViewsTest, DragThumbScrollsContent) {
+ ui::test::EventGenerator generator(GetRootWindow(widget_.get()));
+ EXPECT_EQ(0, scrollbar_->GetPosition());
+ generator.MoveMouseTo(
+ scrollbar_->GetThumb()->GetBoundsInScreen().CenterPoint());
+ generator.DragMouseBy(15, 0);
+ EXPECT_GE(scrollbar_->GetPosition(), 10);
+}
+
+TEST_F(ScrollBarViewsTest, FlingGestureScrollsView) {
+ constexpr int kNumScrollSteps = 100;
+ constexpr int kScrollVelocity = 10;
+ ui::test::EventGenerator generator(GetRootWindow(widget_.get()));
+ EXPECT_EQ(0, scrollbar_->GetPosition());
+ const gfx::Point start_pos =
+ widget_->GetContentsView()->GetBoundsInScreen().CenterPoint();
+ const gfx::Point end_pos = start_pos + gfx::Vector2d(-100, 0);
+ generator.GestureScrollSequence(
+ start_pos, end_pos,
+ generator.CalculateScrollDurationForFlingVelocity(
+ start_pos, end_pos, kScrollVelocity, kNumScrollSteps),
+ kNumScrollSteps);
+ // Just make sure the view scrolled
+ EXPECT_GT(scrollbar_->GetPosition(), 0);
+}
+#endif
+
} // namespace views
diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc
index 0f71ce43d4a..87f35789cc2 100644
--- a/chromium/ui/views/controls/slider.cc
+++ b/chromium/ui/views/controls/slider.cc
@@ -8,9 +8,9 @@
#include <memory>
#include "base/check_op.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "build/build_config.h"
#include "cc/paint/paint_flags.h"
#include "third_party/skia/include/core/SkCanvas.h"
@@ -53,7 +53,7 @@ Slider::Slider(SliderListener* listener)
pending_accessibility_value_change_(false) {
highlight_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
EnableCanvasFlippingForRTLUI(true);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
#else
SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -134,7 +134,7 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) {
if (listener_)
listener_->SliderValueChanged(this, value_, old_value, reason);
- if (old_value_valid && base::MessageLoopCurrent::Get()) {
+ if (old_value_valid && base::CurrentThread::Get()) {
// Do not animate when setting the value of the slider for the first time.
// There is no message-loop when running tests. So we cannot animate then.
if (!move_animation_) {
diff --git a/chromium/ui/views/controls/slider.h b/chromium/ui/views/controls/slider.h
index e1a78cb18dd..239cca3b254 100644
--- a/chromium/ui/views/controls/slider.h
+++ b/chromium/ui/views/controls/slider.h
@@ -47,7 +47,7 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate {
public:
METADATA_HEADER(Slider);
- explicit Slider(SliderListener* listener);
+ explicit Slider(SliderListener* listener = nullptr);
~Slider() override;
float GetValue() const;
diff --git a/chromium/ui/views/controls/slider_unittest.cc b/chromium/ui/views/controls/slider_unittest.cc
index 895907e21ed..03b3b4fd329 100644
--- a/chromium/ui/views/controls/slider_unittest.cc
+++ b/chromium/ui/views/controls/slider_unittest.cc
@@ -25,6 +25,7 @@
#include "ui/views/test/slider_test_api.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/view.h"
+#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/widget/widget_utils.h"
@@ -169,7 +170,7 @@ class SliderTest : public views::ViewsTestBase {
// The maximum y value within the bounds of the slider.
int max_y_ = 0;
// The widget container for the slider being tested.
- views::Widget* widget_ = nullptr;
+ views::UniqueWidgetPtr widget_;
// An event generator.
std::unique_ptr<ui::test::EventGenerator> event_generator_;
@@ -179,10 +180,9 @@ class SliderTest : public views::ViewsTestBase {
void SliderTest::SetUp() {
views::ViewsTestBase::SetUp();
- slider_ = new Slider(nullptr);
- View* view = slider_;
- gfx::Size size = view->GetPreferredSize();
- view->SetSize(size);
+ auto slider = std::make_unique<Slider>();
+ gfx::Size size = slider->GetPreferredSize();
+ slider->SetSize(size);
max_x_ = size.width() - 1;
max_y_ = size.height() - 1;
default_locale_ = base::i18n::GetConfiguredLocale();
@@ -191,19 +191,17 @@ void SliderTest::SetUp() {
CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS));
init_params.bounds = gfx::Rect(size);
- widget_ = new views::Widget();
+ widget_ = std::make_unique<Widget>();
widget_->Init(std::move(init_params));
- widget_->SetContentsView(slider_);
+ slider_ = widget_->SetContentsView(std::move(slider));
widget_->Show();
event_generator_ =
- std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget_));
+ std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget_.get()));
}
void SliderTest::TearDown() {
- if (widget_ && !widget_->IsClosed())
- widget_->Close();
-
+ widget_.reset();
base::i18n::SetICUDefaultLocale(default_locale_);
views::ViewsTestBase::TearDown();
@@ -234,7 +232,7 @@ TEST_F(SliderTest, UpdateFromClickRTLHorizontal) {
}
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
// Test the slider location after a tap gesture.
TEST_F(SliderTest, SliderValueForTapGesture) {
@@ -394,6 +392,6 @@ TEST_F(SliderTest, SliderRaisesA11yEvents) {
EXPECT_TRUE(observer.value_changed());
}
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
} // namespace views
diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc
index 5c915f9d452..b9e036c5d7d 100644
--- a/chromium/ui/views/controls/styled_label.cc
+++ b/chromium/ui/views/controls/styled_label.cc
@@ -100,8 +100,9 @@ void StyledLabel::SetText(const base::string16& text) {
OnPropertyChanged(&text_, kPropertyEffectsPreferredSizeChanged);
}
-gfx::FontList StyledLabel::GetDefaultFontList() const {
- return style::GetFont(text_context_, default_text_style_);
+gfx::FontList StyledLabel::GetFontList(const RangeStyleInfo& style_info) const {
+ return style_info.custom_font.value_or(style::GetFont(
+ text_context_, style_info.text_style.value_or(default_text_style_)));
}
void StyledLabel::AddStyleRange(const gfx::Range& range,
@@ -149,16 +150,16 @@ void StyledLabel::SetDefaultTextStyle(int text_style) {
}
int StyledLabel::GetLineHeight() const {
- return specified_line_height_;
+ return line_height_.value_or(
+ style::GetLineHeight(text_context_, default_text_style_));
}
void StyledLabel::SetLineHeight(int line_height) {
- if (specified_line_height_ == line_height)
+ if (line_height_ == line_height)
return;
- specified_line_height_ = line_height;
- OnPropertyChanged(&specified_line_height_,
- kPropertyEffectsPreferredSizeChanged);
+ line_height_ = line_height;
+ OnPropertyChanged(&line_height_, kPropertyEffectsPreferredSizeChanged);
}
base::Optional<SkColor> StyledLabel::GetDisplayedOnBackgroundColor() const {
@@ -338,26 +339,6 @@ int StyledLabel::StartX(int excess_space) const {
: excess_space);
}
-int StyledLabel::GetDefaultLineHeight() const {
- return specified_line_height_ > 0
- ? specified_line_height_
- : std::max(
- style::GetLineHeight(text_context_, default_text_style_),
- GetDefaultFontList().GetHeight());
-}
-
-gfx::FontList StyledLabel::GetFontListForRange(
- const StyleRanges::const_iterator& range) const {
- if (range == style_ranges_.end())
- return GetDefaultFontList();
-
- return range->style_info.custom_font
- ? range->style_info.custom_font.value()
- : style::GetFont(
- text_context_,
- range->style_info.text_style.value_or(default_text_style_));
-}
-
void StyledLabel::CalculateLayout(int width) const {
const gfx::Insets insets = GetInsets();
width = std::max(width, insets.width());
@@ -369,7 +350,7 @@ void StyledLabel::CalculateLayout(int width) const {
layout_views_ = std::make_unique<LayoutViews>();
const int content_width = width - insets.width();
- const int default_line_height = GetDefaultLineHeight();
+ const int line_height = GetLineHeight();
RangeStyleInfo default_style;
default_style.text_style = default_text_style_;
int max_width = 0, total_height = 0;
@@ -379,7 +360,7 @@ void StyledLabel::CalculateLayout(int width) const {
StyleRanges::const_iterator current_range = style_ranges_.begin();
for (base::string16 remaining_string = text_;
content_width > 0 && !remaining_string.empty();) {
- layout_size_info_.line_sizes.emplace_back(0, default_line_height);
+ layout_size_info_.line_sizes.emplace_back(0, line_height);
auto& line_size = layout_size_info_.line_sizes.back();
layout_views_->views_per_line.emplace_back();
auto& views = layout_views_->views_per_line.back();
@@ -412,13 +393,13 @@ void StyledLabel::CalculateLayout(int width) const {
!current_range->style_info.custom_view)) {
const gfx::Rect chunk_bounds(line_size.width(), 0,
content_width - line_size.width(),
- default_line_height);
+ line_height);
// If the start of the remaining text is inside a styled range, the font
// style may differ from the base font. The font specified by the range
// should be used when eliding text.
- gfx::FontList text_font_list = position >= range.start()
- ? GetFontListForRange(current_range)
- : GetDefaultFontList();
+ gfx::FontList text_font_list =
+ GetFontList((position >= range.start()) ? current_range->style_info
+ : RangeStyleInfo());
int elide_result = gfx::ElideRectangleText(
remaining_string, text_font_list, chunk_bounds.width(),
chunk_bounds.height(), gfx::WRAP_LONG_WORDS, &substrings);
diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h
index 31b24580ecb..b12461bad21 100644
--- a/chromium/ui/views/controls/styled_label.h
+++ b/chromium/ui/views/controls/styled_label.h
@@ -118,9 +118,10 @@ class VIEWS_EXPORT StyledLabel : public View {
const base::string16& GetText() const;
void SetText(const base::string16& text);
- // Returns the font list that results from the default text context and style
- // for ranges. This can be used as the basis for a range |custom_font|.
- gfx::FontList GetDefaultFontList() const;
+ // Returns the FontList that should be used. |style_info| is an optional
+ // argument that takes precedence over the default values.
+ gfx::FontList GetFontList(
+ const RangeStyleInfo& style_info = RangeStyleInfo()) const;
// Marks the given range within |text_| with style defined by |style_info|.
// |range| must be contained in |text_|.
@@ -206,13 +207,6 @@ class VIEWS_EXPORT StyledLabel : public View {
// space available on that line.
int StartX(int excess_space) const;
- // Returns the default line height, based on the default style.
- int GetDefaultLineHeight() const;
-
- // Returns the FontList that should be used for |range|.
- gfx::FontList GetFontListForRange(
- const StyleRanges::const_iterator& range) const;
-
// Sets |layout_size_info_| and |layout_views_| for the given |width|. No-op
// if current_width <= width <= max_width, where:
// current_width = layout_size_info_.total_size.width()
@@ -239,8 +233,7 @@ class VIEWS_EXPORT StyledLabel : public View {
int text_context_ = style::CONTEXT_LABEL;
int default_text_style_ = style::STYLE_PRIMARY;
- // Line height. If zero, style::GetLineHeight() is used.
- int specified_line_height_ = 0;
+ base::Optional<int> line_height_;
// The listener that will be informed of link clicks.
StyledLabelListener* listener_;
diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc
index 2a1a5db6ab1..c42c1452e19 100644
--- a/chromium/ui/views/controls/styled_label_unittest.cc
+++ b/chromium/ui/views/controls/styled_label_unittest.cc
@@ -285,7 +285,7 @@ TEST_F(StyledLabelTest, StyledRangeCustomFontUnderlined) {
StyledLabel::RangeStyleInfo style_info;
style_info.tooltip = ASCIIToUTF16("tooltip");
style_info.custom_font =
- styled()->GetDefaultFontList().DeriveWithStyle(gfx::Font::UNDERLINE);
+ styled()->GetFontList().DeriveWithStyle(gfx::Font::UNDERLINE);
styled()->AddStyleRange(
gfx::Range(text.size(), text.size() + underlined_text.size()),
style_info);
@@ -307,7 +307,7 @@ TEST_F(StyledLabelTest, StyledRangeTextStyleBold) {
// Pretend disabled text becomes bold for testing.
bold_provider.SetFont(
style::CONTEXT_LABEL, style::STYLE_DISABLED,
- styled()->GetDefaultFontList().DeriveWithWeight(gfx::Font::Weight::BOLD));
+ styled()->GetFontList().DeriveWithWeight(gfx::Font::Weight::BOLD));
StyledLabel::RangeStyleInfo style_info;
style_info.text_style = style::STYLE_DISABLED;
@@ -373,11 +373,8 @@ TEST_F(StyledLabelTest, Color) {
styled()->SetBounds(0, 0, 1000, 1000);
styled()->Layout();
- Widget* widget = new Widget();
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- widget->Init(std::move(params));
- View* container = new View();
- widget->SetContentsView(container);
+ std::unique_ptr<Widget> widget = CreateTestWidget();
+ View* container = widget->SetContentsView(std::make_unique<View>());
// The code below is not prepared to deal with dark mode.
widget->GetNativeTheme()->set_use_dark_colors(false);
@@ -394,6 +391,7 @@ TEST_F(StyledLabelTest, Color) {
container->AddChildView(std::make_unique<Link>(ASCIIToUTF16(text_link)));
const SkColor kDefaultLinkColor = link->GetEnabledColor();
+ ASSERT_EQ(3u, styled()->children().size());
EXPECT_EQ(SK_ColorBLUE, LabelAt(0)->GetEnabledColor());
EXPECT_EQ(kDefaultLinkColor,
LabelAt(1, Link::kViewClassName)->GetEnabledColor());
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 50ecb7c042b..cd1093b7a5d 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -76,7 +76,7 @@ void Tab::SetSelected(bool selected) {
contents_->SetVisible(selected);
contents_->parent()->InvalidateLayout();
SetState(selected ? State::kActive : State::kInactive);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
SetFocusBehavior(selected ? FocusBehavior::ACCESSIBLE_ONLY
: FocusBehavior::NEVER);
#else
@@ -471,10 +471,8 @@ void TabStrip::OnPaintBorder(gfx::Canvas* canvas) {
max_main_axis - min_main_axis, kSelectedBorderThickness);
if (!is_horizontal)
rect.Transpose();
- canvas->FillRect(
- rect, SkColorSetA(GetNativeTheme()->GetSystemColor(
- ui::NativeTheme::kColorId_FocusedBorderColor),
- SK_AlphaOPAQUE));
+ canvas->FillRect(rect, GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_TabSelectedBorderColor));
}
DEFINE_ENUM_CONVERTERS(TabbedPane::Orientation,
diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc
index fa791c4700c..5e9f53653aa 100644
--- a/chromium/ui/views/controls/table/table_view.cc
+++ b/chromium/ui/views/controls/table/table_view.cc
@@ -85,7 +85,7 @@ ui::NativeTheme::ColorId selected_text_color_id(bool has_focus) {
// Whether the platform "command" key is down.
bool IsCmdOrCtrl(const ui::Event& event) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return event.IsCommandDown();
#else
return event.IsControlDown();
@@ -462,7 +462,7 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
return true;
case ui::VKEY_UP:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (event.IsAltDown()) {
if (GetRowCount())
SelectByViewIndex(0);
@@ -475,7 +475,7 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
return true;
case ui::VKEY_DOWN:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (event.IsAltDown()) {
if (GetRowCount())
SelectByViewIndex(GetRowCount() - 1);
@@ -1234,7 +1234,7 @@ void TableView::UpdateVirtualAccessibilityChildren() {
cell_data.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnSpan,
1);
if (base::i18n::IsRTL())
- cell_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ cell_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
auto sort_direction = ax::mojom::SortDirection::kUnsorted;
if (column.sortable && primary_sorted_column_id.has_value() &&
@@ -1323,7 +1323,7 @@ void TableView::UpdateVirtualAccessibilityChildren() {
cell_data.SetName(model_->GetText(model_index, column.id));
if (base::i18n::IsRTL())
- cell_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ cell_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
auto sort_direction = ax::mojom::SortDirection::kUnsorted;
if (column.sortable && primary_sorted_column_id.has_value() &&
diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc
index df538cc5d3c..acf951de599 100644
--- a/chromium/ui/views/controls/table/table_view_unittest.cc
+++ b/chromium/ui/views/controls/table/table_view_unittest.cc
@@ -122,7 +122,7 @@ class TableViewTestHelper {
namespace {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
constexpr int kCtrlOrCmdMask = ui::EF_COMMAND_DOWN;
#else
constexpr int kCtrlOrCmdMask = ui::EF_CONTROL_DOWN;
@@ -1179,7 +1179,7 @@ TEST_F(TableViewTest, SelectionNoSelectOnRemove) {
}
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Verifies selection works by way of a gesture.
TEST_F(TableViewTest, SelectOnTap) {
// Initially no selection.
diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc
index d0c5e12a9d1..926f65e3623 100644
--- a/chromium/ui/views/controls/textfield/textfield.cc
+++ b/chromium/ui/views/controls/textfield/textfield.cc
@@ -15,6 +15,7 @@
#endif
#include "base/command_line.h"
+#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
@@ -70,7 +71,7 @@
#endif
#if defined(USE_X11)
-#include "ui/base/x/x11_util_internal.h" // nogncheck
+#include "ui/base/x/x11_util.h" // nogncheck
#endif
#if defined(OS_CHROMEOS)
@@ -78,11 +79,17 @@
#include "ui/wm/core/ime_util_chromeos.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/cocoa/defaults_utils.h"
#include "ui/base/cocoa/secure_password_input.h"
#endif
+#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_gl_egl_utility.h"
+#endif
+
namespace views {
namespace {
@@ -100,7 +107,7 @@ enum TextfieldPropertyKey {
kTextfieldSelectedRange,
};
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
constexpr gfx::SelectionBehavior kLineSelectionBehavior = gfx::SELECTION_EXTEND;
constexpr gfx::SelectionBehavior kWordSelectionBehavior = gfx::SELECTION_CARET;
constexpr gfx::SelectionBehavior kMoveParagraphSelectionBehavior =
@@ -185,14 +192,14 @@ ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) {
#endif
return ui::TextEditCommand::DELETE_BACKWARD;
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Only erase by line break on Linux and ChromeOS.
if (shift)
return ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE;
#endif
return ui::TextEditCommand::DELETE_WORD_BACKWARD;
case ui::VKEY_DELETE:
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Only erase by line break on Linux and ChromeOS.
if (shift && control)
return ui::TextEditCommand::DELETE_TO_END_OF_LINE;
@@ -260,7 +267,7 @@ bool IsControlKeyModifier(int flags) {
// Control-modified key combination, but we cannot extend it to other platforms
// as Control has different meanings and behaviors.
// https://crrev.com/2580483002/#msg46
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
return flags & ui::EF_CONTROL_DOWN;
#else
return false;
@@ -272,6 +279,24 @@ bool IsValidCharToInsert(const base::char16& ch) {
return (ch >= 0x20 && ch < 0x7F) || ch > 0x9F;
}
+bool CanUseTransparentBackgroundForDragImage() {
+#if defined(USE_OZONE)
+ if (::features::IsUsingOzonePlatform()) {
+ const auto* const egl_utility =
+ ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
+ return egl_utility ? egl_utility->IsTransparentBackgroundSupported()
+ : false;
+ }
+#endif
+#if defined(USE_X11)
+ // Fallback on the background color if the system doesn't support compositing.
+ return ui::XVisualManager::GetInstance()->ArgbVisualAvailable();
+#else
+ // Other platforms allow this.
+ return true;
+#endif
+}
+
} // namespace
// static
@@ -283,7 +308,7 @@ base::TimeDelta Textfield::GetCaretBlinkInterval() {
? base::TimeDelta()
: base::TimeDelta::FromMilliseconds(system_value);
}
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
base::TimeDelta system_value;
if (ui::TextInsertionCaretBlinkPeriod(&system_value))
return system_value;
@@ -315,7 +340,7 @@ Textfield::Textfield()
if (use_focus_ring_)
focus_ring_ = FocusRing::Install(this);
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Do not map accelerators on Mac. E.g. They might not reflect custom
// keybindings that a user has set. But also on Mac, these commands dispatch
// via the "responder chain" when the OS searches through menu items in the
@@ -593,6 +618,26 @@ size_t Textfield::GetCursorPosition() const {
return model_->GetCursorPosition();
}
+void Textfield::SetTextAndScrollAndSelectRange(
+ const base::string16& text,
+ const size_t cursor_position,
+ const std::vector<size_t>& positions,
+ const gfx::Range range) {
+ // Pass cursor_position to SetText to ensure edit history works as expected.
+ model_->SetText(text, cursor_position);
+ NotifyAccessibilityEvent(ax::mojom::Event::kValueChanged, true);
+ for (auto position : positions) {
+ model_->MoveCursorTo(position);
+ GetRenderText()->GetUpdatedCursorBounds();
+ }
+ model_->SelectRange(range);
+ OnPropertyChanged(&model_ + kTextfieldSelectedRange, kPropertyEffectsPaint);
+ // We don't set the |text_changed| param to true because that would notify
+ // the TextfieldController::ContentsChanged(), which should only occur when
+ // the user changes the text.
+ UpdateAfterChange(false, true);
+}
+
void Textfield::SetColor(SkColor value) {
GetRenderText()->SetColor(value);
cursor_view_->layer()->SetColor(value);
@@ -644,6 +689,7 @@ void Textfield::SetAccessibleName(const base::string16& name) {
accessible_name_ = name;
OnPropertyChanged(&accessible_name_, kPropertyEffectsNone);
+ NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
}
void Textfield::SetObscuredGlyphSpacing(int spacing) {
@@ -655,6 +701,24 @@ void Textfield::SetExtraInsets(const gfx::Insets& insets) {
UpdateBorder();
}
+void Textfield::FitToLocalBounds() {
+ // Textfield insets include a reasonable amount of whitespace on all sides of
+ // the default font list. Fallback fonts with larger heights may paint over
+ // the vertical whitespace as needed. Alternate solutions involve undesirable
+ // behavior like changing the default font size, shrinking some fallback fonts
+ // beyond their legibility, or enlarging controls dynamically with content.
+ gfx::Rect bounds = GetLocalBounds();
+ const gfx::Insets insets = GetInsets();
+ // The text will draw with the correct vertical alignment if we don't apply
+ // the vertical insets.
+ bounds.Inset(insets.left(), 0, insets.right(), 0);
+ bounds.set_x(GetMirroredXForRect(bounds));
+ GetRenderText()->SetDisplayRect(bounds);
+ OnCaretBoundsChanged();
+ UpdateCursorViewPosition();
+ UpdateCursorVisibility();
+}
+
////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:
@@ -724,8 +788,9 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
return selection_controller_.OnMousePressed(
event, handled,
- had_focus ? SelectionController::FOCUSED
- : SelectionController::UNFOCUSED);
+ had_focus
+ ? SelectionController::InitialFocusStateOnMousePress::kFocused
+ : SelectionController::InitialFocusStateOnMousePress::kUnFocused);
}
bool Textfield::OnMouseDragged(const ui::MouseEvent& event) {
@@ -1090,21 +1155,7 @@ bool Textfield::HandleAccessibleAction(const ui::AXActionData& action_data) {
}
void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- // Textfield insets include a reasonable amount of whitespace on all sides of
- // the default font list. Fallback fonts with larger heights may paint over
- // the vertical whitespace as needed. Alternate solutions involve undesirable
- // behavior like changing the default font size, shrinking some fallback fonts
- // beyond their legibility, or enlarging controls dynamically with content.
- gfx::Rect bounds = GetLocalBounds();
- const gfx::Insets insets = GetInsets();
- // The text will draw with the correct vertical alignment if we don't apply
- // the vertical insets.
- bounds.Inset(insets.left(), 0, insets.right(), 0);
- bounds.set_x(GetMirroredXForRect(bounds));
- GetRenderText()->SetDisplayRect(bounds);
- OnCaretBoundsChanged();
- UpdateCursorViewPosition();
- UpdateCursorVisibility();
+ FitToLocalBounds();
}
bool Textfield::GetNeedsNotificationWhenVisibleBoundsChange() const {
@@ -1127,11 +1178,11 @@ void Textfield::OnFocus() {
if (focus_reason_ == ui::TextInputClient::FOCUS_REASON_NONE)
focus_reason_ = ui::TextInputClient::FOCUS_REASON_OTHER;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
password_input_enabler_ =
std::make_unique<ui::ScopedPasswordInputEnabler>();
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
GetRenderText()->set_focused(true);
if (ShouldShowCursor()) {
@@ -1172,9 +1223,9 @@ void Textfield::OnBlur() {
SchedulePaint();
View::OnBlur();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
password_input_enabler_.reset();
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
}
gfx::Point Textfield::GetKeyboardContextMenuLocation() {
@@ -1237,12 +1288,9 @@ void Textfield::WriteDragDataForView(View* sender,
SkBitmap bitmap;
float raster_scale = ScaleFactorForDragFromWidget(GetWidget());
- SkColor color = SK_ColorTRANSPARENT;
-#if defined(USE_X11)
- // Fallback on the background color if the system doesn't support compositing.
- if (!ui::XVisualManager::GetInstance()->ArgbVisualAvailable())
- color = GetBackgroundColor();
-#endif
+ SkColor color = CanUseTransparentBackgroundForDragImage()
+ ? SK_ColorTRANSPARENT
+ : GetBackgroundColor();
label.Paint(PaintInfo::CreateRootPaintInfo(
ui::CanvasPainter(&bitmap, label.size(), raster_scale, color,
GetWidget()->GetCompositor()->is_pixel_canvas())
@@ -1459,21 +1507,21 @@ void Textfield::SetCompositionText(const ui::CompositionText& composition) {
OnAfterUserAction();
}
-void Textfield::ConfirmCompositionText(bool keep_selection) {
+uint32_t Textfield::ConfirmCompositionText(bool keep_selection) {
// TODO(b/134473433) Modify this function so that when keep_selection is
// true, the selection is not changed when text committed
if (keep_selection) {
NOTIMPLEMENTED_LOG_ONCE();
}
if (!model_->HasCompositionText())
- return;
-
+ return 0;
OnBeforeUserAction();
skip_input_method_cancel_composition_ = true;
- model_->ConfirmCompositionText();
+ const uint32_t confirmed_text_length = model_->ConfirmCompositionText();
skip_input_method_cancel_composition_ = false;
UpdateAfterChange(true, true);
OnAfterUserAction();
+ return confirmed_text_length;
}
void Textfield::ClearCompositionText() {
@@ -1749,7 +1797,7 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const {
return readable && HasSelection();
case ui::TextEditCommand::PASTE:
ui::Clipboard::GetForCurrentThread()->ReadText(
- ui::ClipboardBuffer::kCopyPaste, &result);
+ ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, &result);
return editable && !result.empty();
case ui::TextEditCommand::SELECT_ALL:
return !GetText().empty() &&
@@ -1768,7 +1816,7 @@ bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const {
case ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION:
// On Mac, the textfield should respond to Up/Down arrows keys and
// PageUp/PageDown.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return true;
#else
return false;
@@ -1819,10 +1867,33 @@ bool Textfield::SetCompositionFromExistingText(
#endif
#if defined(OS_CHROMEOS)
+gfx::Range Textfield::GetAutocorrectRange() const {
+ return model_->autocorrect_range();
+}
+
+gfx::Rect Textfield::GetAutocorrectCharacterBounds() const {
+ gfx::Range autocorrect_range = model_->autocorrect_range();
+ if (autocorrect_range.is_empty())
+ return gfx::Rect();
+
+ gfx::RenderText* render_text = GetRenderText();
+ const gfx::SelectionModel caret(autocorrect_range, gfx::CURSOR_BACKWARD);
+ gfx::Rect rect;
+ rect = render_text->GetCursorBounds(caret, false);
+
+ ConvertRectToScreen(this, &rect);
+ return rect;
+}
+
bool Textfield::SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) {
- // TODO(crbug.com/1091088) Implement autocorrect range textfield handling.
- return false;
+ base::UmaHistogramEnumeration("InputMethod.Assistive.Autocorrect.Count",
+ TextInputClient::SubClass::kTextField);
+ return model_->SetAutocorrectRange(autocorrect_text, range);
+}
+
+void Textfield::ClearAutocorrectRange() {
+ model_->SetAutocorrectRange(base::string16(), gfx::Range());
}
#endif
@@ -1863,7 +1934,8 @@ gfx::Point Textfield::GetLastClickRootLocation() const {
base::string16 Textfield::GetSelectionClipboardText() const {
base::string16 selection_clipboard_text;
ui::Clipboard::GetForCurrentThread()->ReadText(
- ui::ClipboardBuffer::kSelection, &selection_clipboard_text);
+ ui::ClipboardBuffer::kSelection, /* data_dst = */ nullptr,
+ &selection_clipboard_text);
return selection_clipboard_text;
}
diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h
index 775ce4f9683..0763cc99053 100644
--- a/chromium/ui/views/controls/textfield/textfield.h
+++ b/chromium/ui/views/controls/textfield/textfield.h
@@ -51,11 +51,11 @@ namespace base {
class TimeDelta;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
namespace ui {
class ScopedPasswordInputEnabler;
}
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
namespace views {
@@ -122,6 +122,23 @@ class VIEWS_EXPORT Textfield : public View,
void SetText(const base::string16& new_text);
void SetText(const base::string16& new_text, size_t cursor_position);
+ // Similar to calling SetText followed by SetSelectedRange calls, but will
+ // dedupe some calls. Notably, this prevents OnCaretBoundsChanged from being
+ // called multiple times for a single change which can cause issues for
+ // accessibility tools.
+ // - |text| and |cursor_position| see SetText() comment above.
+ // - |scroll_positions| a vector of positions to scroll into view. This can
+ // contain multiple positions which can be useful to e.g. ensure multiple
+ // positions are in view (if possible). Scrolls are applied in the order of
+ // |scroll_positions|; i.e., later positions will have priority over earlier
+ // positions.
+ // - |range| is used to invoke SetSelectedRange and will also be scrolled to.
+ void SetTextAndScrollAndSelectRange(
+ const base::string16& text,
+ const size_t cursor_position,
+ const std::vector<size_t>& scroll_positions,
+ const gfx::Range range);
+
// Appends the given string to the previously-existing text in the field.
void AppendText(const base::string16& new_text);
@@ -261,6 +278,10 @@ class VIEWS_EXPORT Textfield : public View,
void SetExtraInsets(const gfx::Insets& insets);
+ // Fits the textfield to the local bounds, applying internal padding and
+ // updating the cursor position and visibility.
+ void FitToLocalBounds();
+
// View overrides:
int GetBaseline() const override;
gfx::Size CalculatePreferredSize() const override;
@@ -346,7 +367,7 @@ class VIEWS_EXPORT Textfield : public View,
// ui::TextInputClient overrides:
void SetCompositionText(const ui::CompositionText& composition) override;
- void ConfirmCompositionText(bool keep_selection) override;
+ uint32_t ConfirmCompositionText(bool keep_selection) override;
void ClearCompositionText() override;
void InsertText(const base::string16& text) override;
void InsertChar(const ui::KeyEvent& event) override;
@@ -387,8 +408,11 @@ class VIEWS_EXPORT Textfield : public View,
#endif
#if defined(OS_CHROMEOS)
+ gfx::Range GetAutocorrectRange() const override;
+ gfx::Rect GetAutocorrectCharacterBounds() const override;
bool SetAutocorrectRange(const base::string16& autocorrect_text,
const gfx::Range& range) override;
+ void ClearAutocorrectRange() override;
#endif
#if defined(OS_WIN)
@@ -402,7 +426,7 @@ class VIEWS_EXPORT Textfield : public View,
#endif
views::PropertyChangedSubscription AddTextChangedCallback(
- views::PropertyChangedCallback callback);
+ views::PropertyChangedCallback callback) WARN_UNUSED_RESULT;
protected:
// Inserts or appends a character in response to an IME operation.
@@ -670,10 +694,10 @@ class VIEWS_EXPORT Textfield : public View,
// View containing the text cursor.
View* cursor_view_ = nullptr;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Used to track active password input sessions.
std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
// How this textfield was focused.
ui::TextInputClient::FocusReason focus_reason_ =
diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc
index d3df8334a98..24514cb8e02 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model.cc
@@ -9,10 +9,10 @@
#include "base/check_op.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/task/current_thread.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/gfx/range/range.h"
@@ -345,7 +345,7 @@ gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) {
// the default kill ring size of 1 (i.e. a single buffer) is assumed.
base::string16* GetKillBuffer() {
static base::NoDestructor<base::string16> kill_buffer;
- DCHECK(base::MessageLoopCurrentForUI::IsSet());
+ DCHECK(base::CurrentUIThread::IsSet());
return kill_buffer.get();
}
@@ -621,7 +621,7 @@ bool TextfieldModel::Copy() {
bool TextfieldModel::Paste() {
base::string16 text;
ui::Clipboard::GetForCurrentThread()->ReadText(
- ui::ClipboardBuffer::kCopyPaste, &text);
+ ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, &text);
if (text.empty())
return false;
@@ -753,6 +753,45 @@ void TextfieldModel::SetCompositionText(
}
}
+#if defined(OS_CHROMEOS)
+bool TextfieldModel::SetAutocorrectRange(const base::string16& autocorrect_text,
+ const gfx::Range& autocorrect_range) {
+ // Clears autocorrect range if text is empty.
+ if (autocorrect_text.empty() || autocorrect_range == gfx::Range()) {
+ autocorrect_range_ = gfx::Range();
+ original_text_ = base::EmptyString16();
+ } else {
+ // TODO(crbug.com/1108170): Use original text to create the Undo window.
+ base::string16 current_text =
+ render_text_->GetTextFromRange(autocorrect_range);
+ // current text should always be valid.
+ if (current_text.empty())
+ return false;
+
+ original_text_ = std::move(current_text);
+ uint32_t autocorrect_range_start = autocorrect_range.start();
+
+ // TODO(crbug.com/1108170): Update the autocorrect range when the
+ // composition changes for ChromeOS. The current autocorrect_range_ does not
+ // get updated when composition changes or more text is committed.
+ autocorrect_range_ =
+ gfx::Range(autocorrect_range_start,
+ autocorrect_text.length() + autocorrect_range_start);
+
+ base::string16 before_text = render_text_->GetTextFromRange(
+ gfx::Range(0, autocorrect_range.start()));
+ base::string16 after_text = render_text_->GetTextFromRange(gfx::Range(
+ autocorrect_range.end(),
+ std::max(autocorrect_range.end(),
+ static_cast<uint32_t>(render_text_->text().length()))));
+ base::string16 new_text =
+ before_text.append(autocorrect_text).append(after_text);
+ SetRenderTextText(new_text);
+ }
+ return true;
+}
+#endif
+
void TextfieldModel::SetCompositionFromExistingText(const gfx::Range& range) {
if (range.is_empty() || !gfx::Range(0, text().length()).Contains(range)) {
ClearComposition();
@@ -763,10 +802,11 @@ void TextfieldModel::SetCompositionFromExistingText(const gfx::Range& range) {
render_text_->SetCompositionRange(range);
}
-void TextfieldModel::ConfirmCompositionText() {
+uint32_t TextfieldModel::ConfirmCompositionText() {
DCHECK(HasCompositionText());
base::string16 composition =
text().substr(composition_range_.start(), composition_range_.length());
+ uint32_t composition_length = composition_range_.length();
// TODO(oshima): current behavior on ChromeOS is a bit weird and not
// sure exactly how this should work. Find out and fix if necessary.
AddOrMergeEditHistory(std::make_unique<internal::InsertEdit>(
@@ -775,6 +815,7 @@ void TextfieldModel::ConfirmCompositionText() {
ClearComposition();
if (delegate_)
delegate_->OnCompositionTextConfirmedOrCleared();
+ return composition_length;
}
void TextfieldModel::CancelCompositionText() {
diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h
index 9873bc160e9..2d581a86104 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.h
+++ b/chromium/ui/views/controls/textfield/textfield_model.h
@@ -233,14 +233,26 @@ class VIEWS_EXPORT TextfieldModel {
// composition text.
void SetCompositionText(const ui::CompositionText& composition);
+#if defined(OS_CHROMEOS)
+ // Return the text range corresponding to the autocorrected text.
+ const gfx::Range& autocorrect_range() const { return autocorrect_range_; }
+
+ // Replace the text in the specified range with the autocorrect text and
+ // store necessary metadata (The size of the new text + the original text)
+ // to be able to undo this change if needed.
+ bool SetAutocorrectRange(const base::string16& autocorrect_text,
+ const gfx::Range& range);
+#endif
+
// Puts the text in the specified range into composition mode.
// This method should not be called with composition text or an invalid range.
// The provided range is checked against the string's length, if |range| is
// out of bounds, the composition will be cleared.
void SetCompositionFromExistingText(const gfx::Range& range);
- // Converts current composition text into final content.
- void ConfirmCompositionText();
+ // Converts current composition text into final content and returns the
+ // length of the text committed.
+ uint32_t ConfirmCompositionText();
// Removes current composition text.
void CancelCompositionText();
@@ -321,9 +333,16 @@ class VIEWS_EXPORT TextfieldModel {
// The stylized text, cursor, selection, and the visual layout model.
std::unique_ptr<gfx::RenderText> render_text_;
- // The composition range.
gfx::Range composition_range_;
+#if defined(OS_CHROMEOS)
+ gfx::Range autocorrect_range_;
+ // Original text is the text that was replaced by the autocorrect feature.
+ // This should be restored if the Undo button corresponding to the Autocorrect
+ // window is pressed.
+ base::string16 original_text_;
+#endif
+
// The list of Edits. The oldest Edits are at the front of the list, and the
// newest ones are at the back of the list.
using EditHistory = std::list<std::unique_ptr<internal::Edit>>;
diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
index d9b29c3f0fb..f3919eece4a 100644
--- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <memory>
+#include <string>
#include <vector>
#include "base/auto_reset.h"
@@ -185,7 +186,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) {
// TODO(xji): temporarily disable in platform Win since the complex script
// characters turned into empty square due to font regression. So, not able
// to test 2 characters belong to the same grapheme.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EXPECT_EQ(
base::WideToUTF16(L"\x0915\x093f\x0061\x0062\x0915\x094d\x092e\x094d"),
model.text());
@@ -197,7 +198,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) {
// TODO(xji): temporarily disable in platform Win since the complex script
// characters turned into empty square due to font regression. So, not able
// to test 2 characters belong to the same grapheme.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EXPECT_TRUE(model.Delete());
EXPECT_EQ(base::WideToUTF16(L"\x0061\x0062\x0915\x094d\x092e\x094d"),
model.text());
@@ -249,7 +250,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) {
model.SetText(base::WideToUTF16(L"ABC\xFF80\xFF9E"), 0);
model.MoveCursorTo(model.text().length());
model.Backspace();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, the entire cluster should be deleted to match
// NSTextField behavior.
EXPECT_EQ(base::WideToUTF16(L"ABC"), model.text());
@@ -264,7 +265,7 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) {
model.SetText(base::WideToUTF16(L"\U0001F466\U0001F3FE"), 0);
model.MoveCursorTo(model.text().length());
model.Backspace();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, the entire emoji should be deleted to match NSTextField
// behavior.
EXPECT_EQ(base::WideToUTF16(L""), model.text());
@@ -370,7 +371,7 @@ TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) {
// TODO(xji): temporarily disable in platform Win since the complex script
// characters turned into empty square due to font regression. So, not able
// to test 2 characters belong to the same grapheme.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
model.Append(
base::WideToUTF16(L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"
L"def"));
@@ -705,14 +706,16 @@ TEST_F(TextfieldModelTest, Clipboard) {
// Cut with an empty selection should do nothing.
model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE);
EXPECT_FALSE(model.Cut());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_EQ(initial_clipboard_text, clipboard_text);
EXPECT_STR_EQ("HELLO WORLD", model.text());
EXPECT_EQ(11U, model.GetCursorPosition());
// Copy with an empty selection should do nothing.
EXPECT_FALSE(model.Copy());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_EQ(initial_clipboard_text, clipboard_text);
EXPECT_STR_EQ("HELLO WORLD", model.text());
EXPECT_EQ(11U, model.GetCursorPosition());
@@ -721,7 +724,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
model.render_text()->SetObscured(true);
model.SelectAll(false);
EXPECT_FALSE(model.Cut());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_EQ(initial_clipboard_text, clipboard_text);
EXPECT_STR_EQ("HELLO WORLD", model.text());
EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText());
@@ -729,7 +733,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
// Copy on obscured (password) text should do nothing.
model.SelectAll(false);
EXPECT_FALSE(model.Copy());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_EQ(initial_clipboard_text, clipboard_text);
EXPECT_STR_EQ("HELLO WORLD", model.text());
EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText());
@@ -739,7 +744,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE);
model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, gfx::SELECTION_RETAIN);
EXPECT_TRUE(model.Cut());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("WORLD", clipboard_text);
EXPECT_STR_EQ("HELLO ", model.text());
EXPECT_EQ(6U, model.GetCursorPosition());
@@ -747,7 +753,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
// Copy with non-empty selection.
model.SelectAll(false);
EXPECT_TRUE(model.Copy());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("HELLO ", clipboard_text);
EXPECT_STR_EQ("HELLO ", model.text());
EXPECT_EQ(6U, model.GetCursorPosition());
@@ -768,7 +775,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
model.SetText(base::ASCIIToUTF16("It's time to say goodbye."), 0);
model.SelectRange({17, 24});
EXPECT_TRUE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("HELLO ", clipboard_text);
EXPECT_STR_EQ("It's time to say HELLO.", model.text());
EXPECT_EQ(22U, model.GetCursorPosition());
@@ -779,7 +787,8 @@ TEST_F(TextfieldModelTest, Clipboard) {
ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
model.SelectRange({5, 8});
EXPECT_FALSE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_TRUE(clipboard_text.empty());
EXPECT_STR_EQ("It's time to say HELLO.", model.text());
EXPECT_EQ(8U, model.GetCursorPosition());
@@ -802,7 +811,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
model.SelectRange({0, 5});
model.SelectRange({13, 17}, false);
EXPECT_TRUE(model.Cut());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("It's ", clipboard_text);
EXPECT_STR_EQ("time to HELLO.", model.text());
EXPECT_EQ(0U, model.GetCursorPosition());
@@ -814,7 +824,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
model.SelectRange({13, 8});
model.SelectRange({0, 4}, false);
EXPECT_TRUE(model.Copy());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("HELLO", clipboard_text);
EXPECT_STR_EQ("time to HELLO.", model.text());
EXPECT_EQ(8U, model.GetCursorPosition());
@@ -827,7 +838,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
model.SelectRange({5, 8}, false);
model.SelectRange({14, 14}, false);
EXPECT_TRUE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("HELLO", clipboard_text);
EXPECT_STR_EQ("HELLOime HELLO.", model.text());
EXPECT_EQ(5U, model.GetCursorPosition());
@@ -840,7 +852,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
model.SelectRange({1, 2});
model.SelectRange({4, 5}, false);
EXPECT_FALSE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_TRUE(clipboard_text.empty());
EXPECT_STR_EQ("HELLOime HELLO.", model.text());
EXPECT_EQ(2U, model.GetCursorPosition());
@@ -853,7 +866,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
model.SelectRange({2, 2});
model.SelectRange({4, 5}, false);
EXPECT_FALSE(model.Cut());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("initial text", clipboard_text);
EXPECT_STR_EQ("HELLOime HELLO.", model.text());
EXPECT_EQ(2U, model.GetCursorPosition());
@@ -862,7 +876,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
// Copy with an empty primary selection and nonempty secondary selections
// should not replace the clipboard.
EXPECT_FALSE(model.Copy());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("initial text", clipboard_text);
EXPECT_STR_EQ("HELLOime HELLO.", model.text());
EXPECT_EQ(2U, model.GetCursorPosition());
@@ -872,7 +887,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
// empty clipboard should change neither the text nor the selections.
ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
EXPECT_FALSE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_TRUE(clipboard_text.empty());
EXPECT_STR_EQ("HELLOime HELLO.", model.text());
EXPECT_EQ(2U, model.GetCursorPosition());
@@ -883,7 +899,8 @@ TEST_F(TextfieldModelTest, Clipboard_WithSecondarySelections) {
ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
.WriteText(initial_clipboard_text);
EXPECT_TRUE(model.Paste());
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &clipboard_text);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &clipboard_text);
EXPECT_STR_EQ("initial text", clipboard_text);
EXPECT_STR_EQ("HEinitial textLLime HELLO.", model.text());
EXPECT_EQ(14U, model.GetCursorPosition());
@@ -938,7 +955,7 @@ TEST_F(TextfieldModelTest, SelectWordTest) {
// TODO(xji): temporarily disable in platform Win since the complex script
// characters and Chinese characters are turned into empty square due to font
// regression.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
TEST_F(TextfieldModelTest, SelectWordTest_MixScripts) {
TextfieldModel model(nullptr);
std::vector<WordAndCursor> word_and_cursor;
@@ -1944,7 +1961,8 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) {
EXPECT_STR_EQ("ABCDEabc", model.text());
// Confirm the composition.
- model.ConfirmCompositionText();
+ uint32_t composition_text_length = model.ConfirmCompositionText();
+ EXPECT_EQ(composition_text_length, static_cast<uint32_t>(3));
EXPECT_STR_EQ("ABCDEabc", model.text());
EXPECT_TRUE(model.Undo());
EXPECT_STR_EQ("ABCDE", model.text());
diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.cc b/chromium/ui/views/controls/textfield/textfield_test_api.cc
index 40176fa3a73..e939a3121a1 100644
--- a/chromium/ui/views/controls/textfield/textfield_test_api.cc
+++ b/chromium/ui/views/controls/textfield/textfield_test_api.cc
@@ -37,4 +37,12 @@ bool TextfieldTestApi::ShouldShowCursor() const {
return textfield_->ShouldShowCursor();
}
+int TextfieldTestApi::GetDisplayOffsetX() const {
+ return GetRenderText()->GetUpdatedDisplayOffset().x();
+}
+
+void TextfieldTestApi::SetDisplayOffsetX(int x) const {
+ return GetRenderText()->SetDisplayOffset(x);
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.h b/chromium/ui/views/controls/textfield/textfield_test_api.h
index 78436bcb5a4..9afc9ba332e 100644
--- a/chromium/ui/views/controls/textfield/textfield_test_api.h
+++ b/chromium/ui/views/controls/textfield/textfield_test_api.h
@@ -56,6 +56,9 @@ class TextfieldTestApi {
bool ShouldShowCursor() const;
+ int GetDisplayOffsetX() const;
+ void SetDisplayOffsetX(int x) const;
+
private:
Textfield* textfield_;
};
diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc
index 5a25faebf58..c33b2ae9706 100644
--- a/chromium/ui/views/controls/textfield/textfield_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc
@@ -19,7 +19,6 @@
#include "base/pickle.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
-#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_enums.mojom.h"
@@ -45,12 +44,14 @@
#include "ui/events/test/event_generator.h"
#include "ui/events/test/keyboard_layout.h"
#include "ui/gfx/render_text.h"
+#include "ui/gfx/render_text_test_api.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/controls/textfield/textfield_model.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/style/platform_style.h"
+#include "ui/views/test/test_ax_event_observer.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
@@ -72,7 +73,7 @@
#include "ui/wm/core/ime_util_chromeos.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/cocoa/secure_password_input.h"
#include "ui/base/cocoa/text_services_context_menu.h"
#endif
@@ -157,7 +158,7 @@ ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) {
// On Mac, emulate InputMethodMac behavior for character events. Composition
// still needs to be mocked, since it's not possible to generate test events
// which trigger the appropriate NSResponder action messages for composition.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (key->is_char())
return DispatchKeyEventPostIME(key);
#endif
@@ -271,7 +272,7 @@ class TestTextfield : public views::Textfield {
// ui::TextInputClient overrides:
void InsertChar(const ui::KeyEvent& e) override {
views::Textfield::InsertChar(e);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, characters are inserted directly rather than attempting to get a
// unicode character from the ui::KeyEvent (which isn't always possible).
key_received_ = true;
@@ -386,7 +387,8 @@ class TextfieldFocuser : public views::View {
base::string16 GetClipboardText(ui::ClipboardBuffer clipboard_buffer) {
base::string16 text;
- ui::Clipboard::GetForCurrentThread()->ReadText(clipboard_buffer, &text);
+ ui::Clipboard::GetForCurrentThread()->ReadText(
+ clipboard_buffer, /* data_dst = */ nullptr, &text);
return text;
}
@@ -493,7 +495,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
// True if native Mac keystrokes should be used (to avoid ifdef litter).
bool TestingNativeMac() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return true;
#else
return false;
@@ -568,7 +570,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
std::vector<uint8_t>(ui::kPropertyFromVKSize);
event.SetProperties(properties);
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
event_generator_->Dispatch(&event);
#else
input_method_->DispatchKeyEvent(&event);
@@ -713,7 +715,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
int menu_index = 0;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (textfield_has_selection) {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Look Up "Selection" */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
@@ -829,7 +831,6 @@ TEST_F(TextfieldTest, ModelChangesTest) {
// text programmatically.
last_contents_.clear();
textfield_->SetText(ASCIIToUTF16("this is"));
-
EXPECT_STR_EQ("this is", model_->text());
EXPECT_STR_EQ("this is", textfield_->GetText());
EXPECT_TRUE(last_contents_.empty());
@@ -843,6 +844,123 @@ TEST_F(TextfieldTest, ModelChangesTest) {
textfield_->SelectAll(false);
EXPECT_STR_EQ("this is a test", textfield_->GetSelectedText());
EXPECT_TRUE(last_contents_.empty());
+
+ textfield_->SetTextAndScrollAndSelectRange(ASCIIToUTF16("another test"), 3,
+ {}, {4, 5});
+ EXPECT_STR_EQ("another test", model_->text());
+ EXPECT_STR_EQ("another test", textfield_->GetText());
+ EXPECT_STR_EQ("h", textfield_->GetSelectedText());
+ EXPECT_TRUE(last_contents_.empty());
+}
+
+TEST_F(TextfieldTest, SetTextAndScrollAndSelectRange_Scrolling) {
+ InitTextfield();
+
+ // Size the textfield wide enough to hold 10 characters.
+ gfx::test::RenderTextTestApi render_text_test_api(test_api_->GetRenderText());
+ render_text_test_api.SetGlyphWidth(10);
+ // 10px/char * 10chars + 1px for cursor width
+ test_api_->GetRenderText()->SetDisplayRect(gfx::Rect(0, 0, 101, 20));
+
+ // Should scroll cursor into view.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {}, {0, 20});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -100);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(0, 20));
+
+ // Cursor position should not affect scroll.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 30, {}, {20, 20});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -100);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20));
+
+ // Scroll positions should affect scroll.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {30}, {20, 20});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -200);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20));
+
+ // Should scroll no more than necessary; e.g., scrolling right should put the
+ // cursor at the right edge.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {}, {15, 15});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -50);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(15));
+
+ // Should scroll no more than necessary; e.g., scrolling left should put the
+ // cursor at the left edge.
+ test_api_->SetDisplayOffsetX(-200); // Scroll all the way right.
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {}, {15, 15});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -150);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(15));
+
+ // Should scroll no more than necessary; e.g., scrolling to a position already
+ // in view should not change the offset.
+ test_api_->SetDisplayOffsetX(-100); // Scroll the middle 10 chars into view.
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {}, {15, 15});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -100);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(15));
+
+ // With multiple scroll positions, the Last scroll position should be scrolled
+ // to after previous scroll positions.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {30, 0}, {20, 20});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -100);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20));
+
+ // With multiple scroll positions, the previous scroll positions should be
+ // scrolled to anyways.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {30, 20}, {20, 20});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), -200);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20));
+
+ // With a non empty selection, only the selection end should affect scrolling.
+ test_api_->SetDisplayOffsetX(0);
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 0, {0}, {30, 0});
+ EXPECT_EQ(test_api_->GetDisplayOffsetX(), 0);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(30, 0));
+}
+
+TEST_F(TextfieldTest, SetTextAndScrollAndSelectRange_ModelEditHistory) {
+ InitTextfield();
+
+ // The cursor and selected range should reflect the |range| parameter.
+ textfield_->SetTextAndScrollAndSelectRange(
+ ASCIIToUTF16("0123456789_123456789_123456789"), 20, {}, {10, 15});
+ EXPECT_EQ(textfield_->GetCursorPosition(), 15u);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(10, 15));
+
+ // After undo, the cursor and selected range should reflect the state prior to
+ // the edit.
+ textfield_->InsertOrReplaceText(ASCIIToUTF16("xyz")); // 2nd edit
+ SendKeyEvent(ui::VKEY_Z, false, true); // Undo 2nd edit
+ EXPECT_EQ(textfield_->GetCursorPosition(), 15u);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(10, 15));
+
+ // After redo, the cursor and selected range should reflect the
+ // |cursor_position| parameter.
+ SendKeyEvent(ui::VKEY_Z, false, true); // Undo 2nd edit
+ SendKeyEvent(ui::VKEY_Z, false, true); // Undo 1st edit
+ SendKeyEvent(ui::VKEY_Z, true, true); // Redo 1st edit
+ EXPECT_EQ(textfield_->GetCursorPosition(), 20u);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20, 20));
+
+ // After undo, the cursor and selected range should reflect the state prior to
+ // the edit, even if that differs than the state after the current (1st) edit.
+ textfield_->InsertOrReplaceText(ASCIIToUTF16("xyz")); // (2')nd edit
+ SendKeyEvent(ui::VKEY_Z, false, true); // Undo (2')nd edit
+ EXPECT_EQ(textfield_->GetCursorPosition(), 20u);
+ EXPECT_EQ(textfield_->GetSelectedRange(), gfx::Range(20, 20));
}
TEST_F(TextfieldTest, KeyTest) {
@@ -864,7 +982,7 @@ TEST_F(TextfieldTest, KeyTest) {
EXPECT_STR_EQ("TexT!1!1", textfield_->GetText());
}
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Control key shouldn't generate a printable character on Linux.
TEST_F(TextfieldTest, KeyTestControlModifier) {
InitTextfield();
@@ -885,7 +1003,7 @@ TEST_F(TextfieldTest, KeyTestControlModifier) {
}
#endif
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_WIN) || defined(OS_APPLE)
#define MAYBE_KeysWithModifiersTest KeysWithModifiersTest
#else
// TODO(crbug.com/645104): Implement keyboard layout changing for other
@@ -969,7 +1087,7 @@ TEST_F(TextfieldTest, ControlAndSelectTest) {
SendHomeEvent(true);
// On Mac, the existing selection should be extended.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_STR_EQ("ZERO two three", textfield_->GetSelectedText());
#else
EXPECT_STR_EQ("ZERO ", textfield_->GetSelectedText());
@@ -1000,7 +1118,7 @@ TEST_F(TextfieldTest, WordSelection) {
// On Mac, the selection should reduce to a caret when the selection direction
// changes for a word selection.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange());
#else
EXPECT_STR_EQ("345", textfield_->GetSelectedText());
@@ -1008,7 +1126,7 @@ TEST_F(TextfieldTest, WordSelection) {
#endif
SendWordEvent(ui::VKEY_LEFT, true);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_STR_EQ("345", textfield_->GetSelectedText());
#else
EXPECT_STR_EQ("12 345", textfield_->GetSelectedText());
@@ -1033,7 +1151,7 @@ TEST_F(TextfieldTest, LineSelection) {
// Select line towards left. On Mac, the existing selection should be extended
// to cover the whole line.
SendHomeEvent(true);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(textfield_->GetText(), textfield_->GetSelectedText());
#else
EXPECT_STR_EQ("12 345", textfield_->GetSelectedText());
@@ -1042,7 +1160,7 @@ TEST_F(TextfieldTest, LineSelection) {
// Select line towards right.
SendEndEvent(true);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(textfield_->GetText(), textfield_->GetSelectedText());
#else
EXPECT_STR_EQ("67 89", textfield_->GetSelectedText());
@@ -1059,7 +1177,7 @@ TEST_F(TextfieldTest, MoveUpDownAndModifySelection) {
// commands.
SendKeyEvent(ui::VKEY_UP);
EXPECT_TRUE(textfield_->key_received());
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_TRUE(textfield_->key_handled());
EXPECT_EQ(gfx::Range(0), textfield_->GetSelectedRange());
#else
@@ -1069,7 +1187,7 @@ TEST_F(TextfieldTest, MoveUpDownAndModifySelection) {
SendKeyEvent(ui::VKEY_DOWN);
EXPECT_TRUE(textfield_->key_received());
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_TRUE(textfield_->key_handled());
EXPECT_EQ(gfx::Range(11), textfield_->GetSelectedRange());
#else
@@ -1099,7 +1217,7 @@ TEST_F(TextfieldTest, MovePageUpDownAndModifySelection) {
// MOVE_PAGE_[UP/DOWN] and the associated selection commands should only be
// enabled on Mac.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
textfield_->SetText(ASCIIToUTF16("12 34567 89"));
textfield_->SetEditableSelectionRange(gfx::Range(6));
@@ -1151,7 +1269,7 @@ TEST_F(TextfieldTest, MoveParagraphForwardBackwardAndModifySelection) {
ui::TextEditCommand::MOVE_PARAGRAPH_BACKWARD_AND_MODIFY_SELECTION);
// On Mac, the selection should reduce to a caret when the selection direction
// is reversed for MOVE_PARAGRAPH_[FORWARD/BACKWARD]_AND_MODIFY_SELECTION.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange());
#else
EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange());
@@ -1163,7 +1281,7 @@ TEST_F(TextfieldTest, MoveParagraphForwardBackwardAndModifySelection) {
test_api_->ExecuteTextEditCommand(
ui::TextEditCommand::MOVE_PARAGRAPH_FORWARD_AND_MODIFY_SELECTION);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
EXPECT_EQ(gfx::Range(6), textfield_->GetSelectedRange());
#else
EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange());
@@ -1216,7 +1334,7 @@ TEST_F(TextfieldTest, InsertionDeletionTest) {
SendWordEvent(ui::VKEY_LEFT, shift);
shift = true;
SendWordEvent(ui::VKEY_BACK, shift);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EXPECT_STR_EQ("three ", textfield_->GetText());
#else
EXPECT_STR_EQ("one three ", textfield_->GetText());
@@ -1237,7 +1355,7 @@ TEST_F(TextfieldTest, InsertionDeletionTest) {
SendWordEvent(ui::VKEY_RIGHT, shift);
shift = true;
SendWordEvent(ui::VKEY_DELETE, shift);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
EXPECT_STR_EQ(" two", textfield_->GetText());
#elif defined(OS_WIN)
EXPECT_STR_EQ("two four", textfield_->GetText());
@@ -1373,7 +1491,7 @@ TEST_F(TextfieldTest, TextInputType_InsertionTest) {
SendKeyEvent(ui::VKEY_A);
EXPECT_EQ(-1, textfield_->GetPasswordCharRevealIndex());
SendKeyEvent(kHebrewLetterSamekh, ui::EF_NONE, true /* from_vk */);
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Don't verifies the password character reveal on MacOS, because on MacOS,
// the text insertion is not done through TextInputClient::InsertChar().
EXPECT_EQ(1, textfield_->GetPasswordCharRevealIndex());
@@ -2318,7 +2436,7 @@ TEST_F(TextfieldTest, UndoRedoTest) {
// Ctrl+Y is bound to "Yank" and Cmd+Y is bound to "Show full history". So, on
// Mac, Cmd+Shift+Z is sent for the tests above and the Ctrl+Y test below is
// skipped.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z.
TEST_F(TextfieldTest, RedoWithCtrlY) {
@@ -2335,11 +2453,11 @@ TEST_F(TextfieldTest, RedoWithCtrlY) {
EXPECT_STR_EQ("a", textfield_->GetText());
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
// Non-Mac platforms don't have a key binding for Yank. Since this test is only
// run on Mac, it uses some Mac specific key bindings.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
TEST_F(TextfieldTest, Yank) {
InitTextfields(2);
@@ -2398,7 +2516,7 @@ TEST_F(TextfieldTest, Yank) {
EXPECT_STR_EQ("efabefeef", textfield_->GetText());
}
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
TEST_F(TextfieldTest, CutCopyPaste) {
InitTextfield();
@@ -2863,36 +2981,59 @@ TEST_F(TextfieldTest, OverflowInRTLTest) {
base::i18n::SetICUDefaultLocale(locale);
}
+TEST_F(TextfieldTest, CommitComposingTextTest) {
+ InitTextfield();
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("abc123");
+ textfield_->SetCompositionText(composition);
+ uint32_t composed_text_length =
+ textfield_->ConfirmCompositionText(/* keep_selection */ false);
+
+ EXPECT_EQ(composed_text_length, static_cast<uint32_t>(6));
+}
+
+TEST_F(TextfieldTest, CommitEmptyComposingTextTest) {
+ InitTextfield();
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("");
+ textfield_->SetCompositionText(composition);
+ uint32_t composed_text_length =
+ textfield_->ConfirmCompositionText(/* keep_selection */ false);
+
+ EXPECT_EQ(composed_text_length, static_cast<uint32_t>(0));
+}
+
TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest) {
InitTextfield();
ui::CompositionText composition;
composition.text = UTF8ToUTF16("abc123");
const uint32_t char_count = static_cast<uint32_t>(composition.text.length());
- ui::TextInputClient* client = textfield_;
// Compare the composition character bounds with surrounding cursor bounds.
for (uint32_t i = 0; i < char_count; ++i) {
composition.selection = gfx::Range(i);
- client->SetCompositionText(composition);
+ textfield_->SetCompositionText(composition);
gfx::Point cursor_origin = GetCursorBounds().origin();
views::View::ConvertPointToScreen(textfield_, &cursor_origin);
composition.selection = gfx::Range(i + 1);
- client->SetCompositionText(composition);
+ textfield_->SetCompositionText(composition);
gfx::Point next_cursor_bottom_left = GetCursorBounds().bottom_left();
views::View::ConvertPointToScreen(textfield_, &next_cursor_bottom_left);
gfx::Rect character;
- EXPECT_TRUE(client->GetCompositionCharacterBounds(i, &character));
+ EXPECT_TRUE(textfield_->GetCompositionCharacterBounds(i, &character));
EXPECT_EQ(character.origin(), cursor_origin) << " i=" << i;
EXPECT_EQ(character.bottom_right(), next_cursor_bottom_left) << " i=" << i;
}
// Return false if the index is out of range.
gfx::Rect rect;
- EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count, &rect));
- EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count + 1, &rect));
- EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count + 100, &rect));
+ EXPECT_FALSE(textfield_->GetCompositionCharacterBounds(char_count, &rect));
+ EXPECT_FALSE(
+ textfield_->GetCompositionCharacterBounds(char_count + 1, &rect));
+ EXPECT_FALSE(
+ textfield_->GetCompositionCharacterBounds(char_count + 100, &rect));
}
TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) {
@@ -2918,13 +3059,12 @@ TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) {
ui::CompositionText composition;
composition.text.assign(kUtf16Chars, kUtf16Chars + kUtf16CharsCount);
- ui::TextInputClient* client = textfield_;
- client->SetCompositionText(composition);
+ textfield_->SetCompositionText(composition);
// Make sure GetCompositionCharacterBounds never fails for index.
gfx::Rect rects[kUtf16CharsCount];
for (uint32_t i = 0; i < kUtf16CharsCount; ++i)
- EXPECT_TRUE(client->GetCompositionCharacterBounds(i, &rects[i]));
+ EXPECT_TRUE(textfield_->GetCompositionCharacterBounds(i, &rects[i]));
// Here we might expect the following results but it actually depends on how
// Uniscribe or HarfBuzz treats them with given font.
@@ -2933,6 +3073,141 @@ TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) {
// - rects[6] == rects[7]
}
+#if defined(OS_CHROMEOS)
+TEST_F(TextfieldTest, SetAutocorrectRangeText) {
+ InitTextfield();
+
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("Initial txt");
+ textfield_->SetCompositionText(composition);
+ textfield_->SetAutocorrectRange(ASCIIToUTF16("text replacement"),
+ gfx::Range(8, 11));
+
+ gfx::Range autocorrect_range = textfield_->GetAutocorrectRange();
+ EXPECT_EQ(autocorrect_range, gfx::Range(8, 24));
+
+ base::string16 text;
+ textfield_->GetTextFromRange(gfx::Range(0, 24), &text);
+ EXPECT_EQ(text, UTF8ToUTF16("Initial text replacement"));
+}
+
+TEST_F(TextfieldTest, SetAutocorrectRangeExplicitlySet) {
+ InitTextfield();
+ textfield_->InsertText(UTF8ToUTF16("Initial txt"));
+ textfield_->SetAutocorrectRange(ASCIIToUTF16("text replacement"),
+ gfx::Range(8, 11));
+
+ gfx::Range autocorrectRange = textfield_->GetAutocorrectRange();
+ EXPECT_EQ(autocorrectRange, gfx::Range(8, 24));
+
+ base::string16 text;
+ textfield_->GetTextFromRange(gfx::Range(0, 24), &text);
+ EXPECT_EQ(text, UTF8ToUTF16("Initial text replacement"));
+}
+
+TEST_F(TextfieldTest, DoesNotSetAutocorrectRangeWhenRangeGivenIsInvalid) {
+ InitTextfield();
+
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("Initial");
+ textfield_->SetCompositionText(composition);
+
+ EXPECT_FALSE(textfield_->SetAutocorrectRange(ASCIIToUTF16("text replacement"),
+ gfx::Range(8, 11)));
+ EXPECT_EQ(gfx::Range(0, 0), textfield_->GetAutocorrectRange());
+ gfx::Range range;
+ textfield_->GetTextRange(&range);
+ base::string16 text;
+ textfield_->GetTextFromRange(range, &text);
+ EXPECT_EQ(composition.text, text);
+}
+
+TEST_F(TextfieldTest,
+ ClearsAutocorrectRangeWhenSetAutocorrectRangeWithEmptyText) {
+ InitTextfield();
+
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("Initial");
+ textfield_->SetCompositionText(composition);
+
+ EXPECT_TRUE(
+ textfield_->SetAutocorrectRange(base::EmptyString16(), gfx::Range(0, 2)));
+ EXPECT_EQ(gfx::Range(0, 0), textfield_->GetAutocorrectRange());
+ gfx::Range range;
+ textfield_->GetTextRange(&range);
+ base::string16 text;
+ textfield_->GetTextFromRange(range, &text);
+ EXPECT_EQ(composition.text, text);
+}
+
+TEST_F(TextfieldTest,
+ ClearsAutocorrectRangeWhenSetAutocorrectRangeWithEmptyRange) {
+ InitTextfield();
+
+ ui::CompositionText composition;
+ composition.text = UTF8ToUTF16("Initial");
+ textfield_->SetCompositionText(composition);
+
+ EXPECT_TRUE(
+ textfield_->SetAutocorrectRange(UTF8ToUTF16("Test"), gfx::Range(0, 0)));
+ EXPECT_EQ(gfx::Range(0, 0), textfield_->GetAutocorrectRange());
+ gfx::Range range;
+ textfield_->GetTextRange(&range);
+ base::string16 text;
+ textfield_->GetTextFromRange(range, &text);
+ EXPECT_EQ(composition.text, text);
+}
+
+TEST_F(TextfieldTest, ClearAutocorrectRange) {
+ InitTextfield();
+ textfield_->InsertText(UTF8ToUTF16("Initial txt"));
+ textfield_->SetAutocorrectRange(ASCIIToUTF16("text replacement"),
+ gfx::Range(8, 11));
+
+ EXPECT_EQ(textfield_->GetText(), UTF8ToUTF16("Initial text replacement"));
+ EXPECT_EQ(textfield_->GetAutocorrectRange(), gfx::Range(8, 24));
+
+ textfield_->ClearAutocorrectRange();
+
+ EXPECT_EQ(textfield_->GetAutocorrectRange(), gfx::Range());
+}
+
+TEST_F(TextfieldTest, GetAutocorrectCharacterBoundsTest) {
+ InitTextfield();
+
+ textfield_->InsertText(UTF8ToUTF16("hello placeholder text"));
+ textfield_->SetAutocorrectRange(ASCIIToUTF16("longlonglongtext"),
+ gfx::Range(3, 10));
+
+ EXPECT_EQ(textfield_->GetAutocorrectRange(), gfx::Range(3, 19));
+
+ gfx::Rect rect_for_long_text = textfield_->GetAutocorrectCharacterBounds();
+
+ // Clear the text
+ textfield_->DeleteRange(gfx::Range(0, 99));
+
+ textfield_->InsertText(UTF8ToUTF16("hello placeholder text"));
+ textfield_->SetAutocorrectRange(ASCIIToUTF16("short"), gfx::Range(3, 10));
+
+ EXPECT_EQ(textfield_->GetAutocorrectRange(), gfx::Range(3, 8));
+
+ gfx::Rect rect_for_short_text = textfield_->GetAutocorrectCharacterBounds();
+
+ EXPECT_LT(rect_for_short_text.x(), rect_for_long_text.x());
+ EXPECT_EQ(rect_for_short_text.y(), rect_for_long_text.y());
+ EXPECT_EQ(rect_for_short_text.height(), rect_for_long_text.height());
+ // TODO(crbug.com/1108170): Investigate why the rectangle width is wrong.
+ // The value seems to be wrong due to the incorrect value being returned from
+ // RenderText::GetCursorBounds(). Unfortuantly, that is tricky to fix, since
+ // RenderText is used in other parts of the codebase.
+ // When fixed, the following EXPECT statement should pass.
+ // EXPECT_LT(rect_for_short_text.width(), rect_for_long_text.width());
+}
+
+// TODO(crbug.com/1108170): Add a test to check that when the composition /
+// surrounding text is updated, the AutocorrectRange is updated accordingly.
+#endif // OS_CHROMEOS
+
// The word we select by double clicking should remain selected regardless of
// where we drag the mouse afterwards without releasing the left button.
TEST_F(TextfieldTest, KeepInitiallySelectedWord) {
@@ -3202,6 +3477,22 @@ TEST_F(TextfieldTest, CursorBlinkRestartsOnInsertOrReplace) {
EXPECT_TRUE(test_api_->IsCursorBlinkTimerRunning());
}
+// Verifies setting the accessible name will call NotifyAccessibilityEvent.
+TEST_F(TextfieldTest, SetAccessibleNameNotifiesAccessibilityEvent) {
+ InitTextfield();
+ base::string16 test_tooltip_text = ASCIIToUTF16("Test Accessible Name");
+ test::TestAXEventObserver observer;
+ EXPECT_EQ(0, observer.text_changed_event_count());
+ textfield_->SetAccessibleName(test_tooltip_text);
+ EXPECT_EQ(1, observer.text_changed_event_count());
+ EXPECT_EQ(test_tooltip_text, textfield_->GetAccessibleName());
+ ui::AXNodeData data;
+ textfield_->GetAccessibleNodeData(&data);
+ const std::string& name =
+ data.GetStringAttribute(ax::mojom::StringAttribute::kName);
+ EXPECT_EQ(test_tooltip_text, ASCIIToUTF16(name));
+}
+
#if defined(OS_CHROMEOS)
// Check that when accessibility virtual keyboard is enabled, windows are
// shifted up when focused and restored when focus is lost.
@@ -3341,7 +3632,7 @@ TEST_F(TextfieldTouchSelectionTest, TouchSelectionInUnfocusableTextfield) {
}
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_TapOnSelection DISABLED_TapOnSelection
#else
#define MAYBE_TapOnSelection TapOnSelection
@@ -3410,6 +3701,22 @@ TEST_F(TextfieldTest, CursorVisibility) {
EXPECT_TRUE(test_api_->IsCursorVisible());
}
+// Tests that Textfield::FitToLocalBounds() sets the RenderText's display rect
+// to the view's bounds, taking the border into account.
+TEST_F(TextfieldTest, FitToLocalBounds) {
+ const int kDisplayRectWidth = 100;
+ const int kBorderWidth = 5;
+ InitTextfield();
+ textfield_->SetBounds(0, 0, kDisplayRectWidth, 100);
+ textfield_->SetBorder(views::CreateEmptyBorder(
+ gfx::Insets(kBorderWidth, kBorderWidth, kBorderWidth, kBorderWidth)));
+ test_api_->GetRenderText()->SetDisplayRect(gfx::Rect(0, 0, 20, 20));
+ ASSERT_EQ(20, test_api_->GetRenderText()->display_rect().width());
+ textfield_->FitToLocalBounds();
+ EXPECT_EQ(kDisplayRectWidth - 2 * kBorderWidth,
+ test_api_->GetRenderText()->display_rect().width());
+}
+
// Verify that cursor view height does not exceed the textfield height.
TEST_F(TextfieldTest, CursorViewHeight) {
InitTextfield();
@@ -3601,7 +3908,7 @@ TEST_F(TextfieldTest, EmojiItem_FieldWithText) {
InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller());
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, when there is text, the "Look up" item (+ separator) takes the top
// position, and emoji comes after.
constexpr int kExpectedEmojiIndex = 2;
@@ -3621,7 +3928,7 @@ TEST_F(TextfieldTest, EmojiItem_FieldWithText) {
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed.
TEST_F(TextfieldTest, TextServicesContextMenuTextDirectionTest) {
@@ -3688,7 +3995,7 @@ TEST_F(TextfieldTest, SecurePasswordInput) {
textfield_->OnBlur();
EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
}
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_APPLE)
TEST_F(TextfieldTest, AccessibilitySelectionEvents) {
const std::string& kText = "abcdef";
diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc
index 69333ebe84c..b42ba498d95 100644
--- a/chromium/ui/views/controls/tree/tree_view.cc
+++ b/chromium/ui/views/controls/tree/tree_view.cc
@@ -84,7 +84,7 @@ TreeView::TreeView()
drawing_provider_(std::make_unique<TreeViewDrawingProvider>()) {
// Always focusable, even on Mac (consistent with NSOutlineView).
SetFocusBehavior(FocusBehavior::ALWAYS);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
constexpr bool kUseMdIcons = true;
#else
constexpr bool kUseMdIcons = false;
@@ -890,7 +890,7 @@ std::unique_ptr<AXVirtualView> TreeView::CreateAndSetAccessibilityView(
ui::AXNodeData& node_data = ax_view->GetCustomData();
node_data.role = ax::mojom::Role::kTreeItem;
if (base::i18n::IsRTL())
- node_data.SetTextDirection(ax::mojom::TextDirection::kRtl);
+ node_data.SetTextDirection(ax::mojom::WritingDirection::kRtl);
base::RepeatingCallback<void(ui::AXNodeData*)> selected_callback =
base::BindRepeating(&TreeView::PopulateAccessibilityData,
diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc
index 6081d640219..0173210e0d1 100644
--- a/chromium/ui/views/controls/tree/tree_view_unittest.cc
+++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc
@@ -19,6 +19,7 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
#include "ui/base/models/tree_node_model.h"
+#include "ui/compositor/canvas_painter.h"
#include "ui/views/accessibility/ax_virtual_view.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
@@ -162,11 +163,11 @@ class TreeViewTest : public ViewsTestBase {
void TreeViewTest::SetUp() {
ViewsTestBase::SetUp();
widget_ = std::make_unique<Widget>();
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ Widget::InitParams params =
+ CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = gfx::Rect(0, 0, 200, 200);
widget_->Init(std::move(params));
- tree_ =
- widget_->GetContentsView()->AddChildView(std::make_unique<TreeView>());
+ tree_ = widget_->SetContentsView(std::make_unique<TreeView>());
tree_->RequestFocus();
ViewAccessibility::AccessibilityEventsCallback accessibility_events_callback =
@@ -376,6 +377,16 @@ TEST_F(TreeViewTest, MetadataTest) {
test::TestViewMetadata(tree_);
}
+TEST_F(TreeViewTest, TreeViewPaintCoverage) {
+ tree_->SetModel(&model_);
+ SkBitmap bitmap;
+ gfx::Size size = tree_->size();
+ ui::CanvasPainter canvas_painter(&bitmap, size, 1.f, SK_ColorTRANSPARENT,
+ false);
+ widget_->GetRootView()->Paint(
+ PaintInfo::CreateRootPaintInfo(canvas_painter.context(), size));
+}
+
// Verifies setting model correctly updates internal state.
TEST_F(TreeViewTest, SetModel) {
tree_->SetModel(&model_);
diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.cc b/chromium/ui/views/controls/views_text_services_context_menu_base.cc
index a9b53aeb9c2..a2c0df01350 100644
--- a/chromium/ui/views/controls/views_text_services_context_menu_base.cc
+++ b/chromium/ui/views/controls/views_text_services_context_menu_base.cc
@@ -50,7 +50,7 @@ bool ViewsTextServicesContextMenuBase::GetAcceleratorForCommandId(
#if defined(OS_WIN)
*accelerator = ui::Accelerator(ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN);
return true;
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
*accelerator = ui::Accelerator(ui::VKEY_SPACE,
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
return true;
@@ -85,7 +85,7 @@ bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
return command_id == IDS_CONTENT_CONTEXT_EMOJI;
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// static
std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.h b/chromium/ui/views/controls/views_text_services_context_menu_base.h
index 3c8484778f8..2318cf80d6b 100644
--- a/chromium/ui/views/controls/views_text_services_context_menu_base.h
+++ b/chromium/ui/views/controls/views_text_services_context_menu_base.h
@@ -31,7 +31,7 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu {
bool SupportsCommand(int command_id) const override;
protected:
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
Textfield* client() { return client_; }
const Textfield* client() const { return client_; }
#endif
diff --git a/chromium/ui/views/controls/webview/BUILD.gn b/chromium/ui/views/controls/webview/BUILD.gn
index 66ba75adfcc..ba38ba89adc 100644
--- a/chromium/ui/views/controls/webview/BUILD.gn
+++ b/chromium/ui/views/controls/webview/BUILD.gn
@@ -2,15 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
# Reset sources_assignment_filter for the BUILD.gn file to prevent
# regression during the migration of Chromium away from the feature.
# See docs/no_sources_assignment_filter.md for more information.
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("webview") {
+component("webview") {
sources = [
"unhandled_keyboard_event_handler.cc",
"unhandled_keyboard_event_handler.h",
@@ -34,7 +32,7 @@ jumbo_component("webview") {
defines = [ "WEBVIEW_IMPLEMENTATION" ]
if (is_mac) {
- libs = [ "CoreFoundation.framework" ]
+ frameworks = [ "CoreFoundation.framework" ]
}
deps = [
@@ -59,7 +57,7 @@ jumbo_component("webview") {
"//ui/views",
]
- if (is_linux || is_android || is_fuchsia) {
+ if (is_linux || is_chromeos || is_android || is_fuchsia) {
sources += [ "unhandled_keyboard_event_handler_default.cc" ]
}
}
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc
index f8c7d7670ef..759b9c6c418 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.cc
+++ b/chromium/ui/views/controls/webview/web_dialog_view.cc
@@ -74,13 +74,11 @@ void ObservableWebView::ResetDelegate() {
WebDialogView::WebDialogView(content::BrowserContext* context,
WebDialogDelegate* delegate,
- std::unique_ptr<WebContentsHandler> handler,
- bool use_dialog_frame)
+ std::unique_ptr<WebContentsHandler> handler)
: ClientView(nullptr, nullptr),
WebDialogWebContentsDelegate(context, std::move(handler)),
delegate_(delegate),
- web_view_(new ObservableWebView(context, delegate)),
- use_dialog_frame_(use_dialog_frame) {
+ web_view_(new ObservableWebView(context, delegate)) {
web_view_->set_allow_accelerators(true);
AddChildView(web_view_);
set_contents_view(web_view_);
@@ -140,12 +138,18 @@ void WebDialogView::ViewHierarchyChanged(
InitDialog();
}
-bool WebDialogView::CanClose() {
+views::CloseRequestResult WebDialogView::OnWindowCloseRequested() {
// Don't close UI if |delegate_| does not allow users to close it by
// clicking on "x" button or pressing Escape shortcut key on hosting
// dialog.
- if (!delegate_->CanCloseDialog() && !close_contents_called_)
- return false;
+ if (!is_attempting_close_dialog_ && !delegate_->OnDialogCloseRequested()) {
+ if (!close_contents_called_)
+ return views::CloseRequestResult::kCannotClose;
+ // This is a web dialog, if the WebContents has been closed, there is no
+ // reason to keep the dialog alive.
+ LOG(ERROR) << "delegate tries to stop closing when CloseContents() has "
+ "been called";
+ }
// If CloseContents() is called before CanClose(), which is called by
// RenderViewHostImpl::ClosePageIgnoringUnloadEvents, it indicates
@@ -154,7 +158,7 @@ bool WebDialogView::CanClose() {
close_contents_called_) {
is_attempting_close_dialog_ = false;
before_unload_fired_ = false;
- return true;
+ return views::CloseRequestResult::kCanClose;
}
if (!is_attempting_close_dialog_) {
@@ -162,14 +166,14 @@ bool WebDialogView::CanClose() {
is_attempting_close_dialog_ = true;
web_view_->web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
}
- return false;
+ return views::CloseRequestResult::kCannotClose;
}
////////////////////////////////////////////////////////////////////////////////
// WebDialogView, views::WidgetDelegate implementation:
bool WebDialogView::OnCloseRequested(Widget::ClosedReason close_reason) {
- return !delegate_ || delegate_->OnDialogCloseRequested();
+ return !delegate_ || delegate_->DeprecatedOnDialogCloseRequested();
}
bool WebDialogView::CanResize() const {
@@ -216,10 +220,20 @@ views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) {
return this;
}
-NonClientFrameView* WebDialogView::CreateNonClientFrameView(Widget* widget) {
- if (use_dialog_frame_)
- return DialogDelegate::CreateDialogFrameView(widget);
- return WidgetDelegate::CreateNonClientFrameView(widget);
+std::unique_ptr<NonClientFrameView> WebDialogView::CreateNonClientFrameView(
+ Widget* widget) {
+ if (!delegate_)
+ return WidgetDelegate::CreateNonClientFrameView(widget);
+
+ switch (delegate_->GetWebDialogFrameKind()) {
+ case WebDialogDelegate::FrameKind::kNonClient:
+ return WidgetDelegate::CreateNonClientFrameView(widget);
+ case WebDialogDelegate::FrameKind::kDialog:
+ return DialogDelegate::CreateDialogFrameView(widget);
+ default:
+ NOTREACHED() << "Unknown frame kind type enum specified.";
+ return std::unique_ptr<NonClientFrameView>{};
+ }
}
views::View* WebDialogView::GetInitiallyFocusedView() {
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h
index 5b04647c4a3..31164a4c8e8 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.h
+++ b/chromium/ui/views/controls/webview/web_dialog_view.h
@@ -78,8 +78,7 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
// client frame view.
WebDialogView(content::BrowserContext* context,
ui::WebDialogDelegate* delegate,
- std::unique_ptr<WebContentsHandler> handler,
- bool use_dialog_frame = false);
+ std::unique_ptr<WebContentsHandler> handler);
~WebDialogView() override;
content::WebContents* web_contents();
@@ -90,7 +89,7 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
- bool CanClose() override;
+ views::CloseRequestResult OnWindowCloseRequested() override;
// WidgetDelegate:
bool OnCloseRequested(Widget::ClosedReason close_reason) override;
@@ -102,7 +101,8 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
void WindowClosing() override;
View* GetContentsView() override;
ClientView* CreateClientView(Widget* widget) override;
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override;
View* GetInitiallyFocusedView() override;
bool ShouldShowWindowTitle() const override;
bool ShouldShowCloseButton() const override;
@@ -193,9 +193,6 @@ class WEBVIEW_EXPORT WebDialogView : public ClientView,
// Handler for unhandled key events from renderer.
UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
- // Whether to use dialog frame view for non client frame view.
- bool use_dialog_frame_ = false;
-
bool disable_url_load_for_test_ = false;
DISALLOW_COPY_AND_ASSIGN(WebDialogView);
diff --git a/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc b/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc
index cb149be77fa..004f97daf2a 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc
+++ b/chromium/ui/views/controls/webview/web_dialog_view_unittest.cc
@@ -39,7 +39,7 @@ class TestWebDialogViewWebDialogDelegate
}
// ui::WebDialogDelegate
- bool CanCloseDialog() const override { return true; }
+ bool OnDialogCloseRequested() override { return true; }
bool ShouldCloseDialogOnEscape() const override { return close_on_escape_; }
ui::ModalType GetDialogModalType() const override {
return ui::MODAL_TYPE_WINDOW;
diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc
index 0c523877565..639ba0b04be 100644
--- a/chromium/ui/views/controls/webview/webview.cc
+++ b/chromium/ui/views/controls/webview/webview.cc
@@ -60,9 +60,9 @@ WebView::ScopedWebContentsCreatorForTesting::
////////////////////////////////////////////////////////////////////////////////
// WebView, public:
-WebView::WebView(content::BrowserContext* browser_context)
- : browser_context_(browser_context) {
+WebView::WebView(content::BrowserContext* browser_context) {
ui::AXPlatformNode::AddAXModeObserver(this);
+ SetBrowserContext(browser_context);
}
WebView::~WebView() {
@@ -72,6 +72,8 @@ WebView::~WebView() {
content::WebContents* WebView::GetWebContents() {
if (!web_contents()) {
+ if (!browser_context_)
+ return nullptr;
wc_owner_ = CreateWebContents(browser_context_);
wc_owner_->SetDelegate(this);
SetWebContents(wc_owner_.get());
@@ -109,7 +111,17 @@ void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
embed_fullscreen_widget_mode_enabled_ = enable;
}
+content::BrowserContext* WebView::GetBrowserContext() {
+ return browser_context_;
+}
+
+void WebView::SetBrowserContext(content::BrowserContext* browser_context) {
+ browser_context_ = browser_context;
+}
+
void WebView::LoadInitialURL(const GURL& url) {
+ // Loading requires a valid WebContents.
+ DCHECK(GetWebContents());
GetWebContents()->GetController().LoadURL(url, content::Referrer(),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
@@ -260,8 +272,17 @@ gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
if (web_contents() && !web_contents()->IsCrashed()) {
content::RenderWidgetHostView* host_view =
web_contents()->GetRenderWidgetHostView();
- if (host_view)
- return host_view->GetNativeViewAccessible();
+ if (host_view) {
+ gfx::NativeViewAccessible accessible =
+ host_view->GetNativeViewAccessible();
+ // |accessible| needs to know whether this is the primary WebContents.
+ if (auto* ax_platform_node =
+ ui::AXPlatformNode::FromNativeViewAccessible(accessible)) {
+ ax_platform_node->SetIsPrimaryWebContentsForWindow(
+ is_primary_web_contents_for_window_);
+ }
+ return accessible;
+ }
}
return View::GetNativeViewAccessible();
}
@@ -341,6 +362,10 @@ void WebView::RenderProcessGone(base::TerminationStatus status) {
NotifyAccessibilityWebContentsChanged();
}
+void WebView::AXTreeIDForMainFrameHasChanged() {
+ NotifyAccessibilityWebContentsChanged();
+}
+
void WebView::ResizeDueToAutoResize(content::WebContents* source,
const gfx::Size& new_size) {
if (source != web_contents())
diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h
index 624dcb1a4d8..f18eea14290 100644
--- a/chromium/ui/views/controls/webview/webview.h
+++ b/chromium/ui/views/controls/webview/webview.h
@@ -42,11 +42,12 @@ class WEBVIEW_EXPORT WebView : public View,
public:
METADATA_HEADER(WebView);
- explicit WebView(content::BrowserContext* browser_context);
+ explicit WebView(content::BrowserContext* browser_context = nullptr);
~WebView() override;
- // This creates a WebContents if none is yet associated with this WebView. The
- // WebView owns this implicitly created WebContents.
+ // This creates a WebContents if |kBrowserContext| has been set and there is
+ // not yet a WebContents associated with this WebView, otherwise it will
+ // return a nullptr.
content::WebContents* GetWebContents();
// WebView does not assume ownership of WebContents set via this method, only
@@ -59,7 +60,8 @@ class WEBVIEW_EXPORT WebView : public View,
// widget or restore the normal WebContentsView.
void SetEmbedFullscreenWidgetMode(bool mode);
- content::BrowserContext* browser_context() { return browser_context_; }
+ content::BrowserContext* GetBrowserContext();
+ void SetBrowserContext(content::BrowserContext* browser_context);
// Loads the initial URL to display in the attached WebContents. Creates the
// WebContents if none is attached yet. Note that this is intended as a
@@ -87,6 +89,11 @@ class WEBVIEW_EXPORT WebView : public View,
// if the web contents is changed.
void SetCrashedOverlayView(View* crashed_overlay_view);
+ // Sets whether this is the primary web contents for the window.
+ void set_is_primary_web_contents_for_window(bool is_primary) {
+ is_primary_web_contents_for_window_ = is_primary;
+ }
+
// When used to host UI, we need to explicitly allow accelerators to be
// processed. Default is false.
void set_allow_accelerators(bool allow_accelerators) {
@@ -157,6 +164,7 @@ class WEBVIEW_EXPORT WebView : public View,
void OnWebContentsFocused(
content::RenderWidgetHost* render_widget_host) override;
void RenderProcessGone(base::TerminationStatus status) override;
+ void AXTreeIDForMainFrameHasChanged() override;
// Override from ui::AXModeObserver
void OnAXModeAdded(ui::AXMode mode) override;
@@ -196,6 +204,7 @@ class WEBVIEW_EXPORT WebView : public View,
content::BrowserContext* browser_context_;
bool allow_accelerators_ = false;
View* crashed_overlay_view_ = nullptr;
+ bool is_primary_web_contents_for_window_ = false;
// Minimum and maximum sizes to determine WebView bounds for auto-resizing.
// Empty if auto resize is not enabled.
diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc
index 5c28d3091f8..1f2322d2fb9 100644
--- a/chromium/ui/views/controls/webview/webview_unittest.cc
+++ b/chromium/ui/views/controls/webview/webview_unittest.cc
@@ -566,6 +566,25 @@ TEST_F(WebViewUnitTest, CrashedOverlayViewOwnedbyClient) {
delete crashed_overlay_view;
}
+// Tests to make sure we can default construct the WebView class and set the
+// BrowserContext after construction.
+TEST_F(WebViewUnitTest, DefaultConstructability) {
+ auto browser_context = std::make_unique<content::TestBrowserContext>();
+ auto web_view = std::make_unique<WebView>();
+
+ // Test to make sure the WebView returns a nullptr in the absence of an
+ // explicitly supplied WebContents and BrowserContext.
+ EXPECT_EQ(nullptr, web_view->GetWebContents());
+
+ web_view->SetBrowserContext(browser_context.get());
+
+ // WebView should be able to create a WebContents object from the previously
+ // set |browser_context|.
+ auto* web_contents = web_view->GetWebContents();
+ EXPECT_NE(nullptr, web_contents);
+ EXPECT_EQ(browser_context.get(), web_contents->GetBrowserContext());
+}
+
#if defined(USE_AURA)
namespace {
diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc
index 9cee7148dc9..21ad5c996b5 100644
--- a/chromium/ui/views/corewm/tooltip_aura.cc
+++ b/chromium/ui/views/corewm/tooltip_aura.cc
@@ -209,7 +209,7 @@ gfx::Rect TooltipAura::GetTooltipBounds(const gfx::Point& mouse_pos,
return tooltip_rect;
}
-void TooltipAura::CreateTooltipWidget() {
+void TooltipAura::CreateTooltipWidget(const gfx::Rect& bounds) {
DCHECK(!widget_);
DCHECK(tooltip_window_);
widget_ = new TooltipWidget;
@@ -221,6 +221,7 @@ void TooltipAura::CreateTooltipWidget() {
DCHECK(params.context);
params.z_order = ui::ZOrderLevel::kFloatingUIElement;
params.accept_events = false;
+ params.bounds = bounds;
if (CanUseTranslucentTooltipWidget())
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
@@ -228,7 +229,6 @@ void TooltipAura::CreateTooltipWidget() {
// which just amount to overkill for this UI.
params.force_software_compositing = true;
widget_->Init(std::move(params));
- widget_->SetTooltipView(std::make_unique<TooltipView>());
}
void TooltipAura::DestroyWidget() {
@@ -251,15 +251,21 @@ void TooltipAura::SetText(aura::Window* window,
tooltip_window_ = window;
if (!widget_) {
- CreateTooltipWidget();
+ auto new_tooltip_view = std::make_unique<TooltipView>();
+ new_tooltip_view->SetMaxWidth(GetMaxWidth(location));
+ new_tooltip_view->SetText(tooltip_text);
+ CreateTooltipWidget(
+ GetTooltipBounds(location, new_tooltip_view->GetPreferredSize()));
+ widget_->SetTooltipView(std::move(new_tooltip_view));
widget_->AddObserver(this);
+ } else {
+ TooltipView* old_tooltip_view = widget_->GetTooltipView();
+ old_tooltip_view->SetMaxWidth(GetMaxWidth(location));
+ old_tooltip_view->SetText(tooltip_text);
+ widget_->SetBounds(
+ GetTooltipBounds(location, old_tooltip_view->GetPreferredSize()));
}
- TooltipView* tooltip_view = widget_->GetTooltipView();
-
- tooltip_view->SetMaxWidth(GetMaxWidth(location));
- tooltip_view->SetText(tooltip_text);
-
ui::NativeTheme* native_theme = widget_->GetNativeTheme();
auto background_color =
native_theme->GetSystemColor(ui::NativeTheme::kColorId_TooltipBackground);
@@ -273,19 +279,9 @@ void TooltipAura::SetText(aura::Window* window,
if (!CanUseTranslucentTooltipWidget())
foreground_color =
color_utils::GetResultingPaintColor(foreground_color, background_color);
+ TooltipView* tooltip_view = widget_->GetTooltipView();
tooltip_view->SetBackgroundColor(background_color, foreground_color);
tooltip_view->SetForegroundColor(foreground_color);
-
- // Calculate the tooltip preferred size after all tooltip attributes are
- // updated - tooltip updates (for example setting text color) may invalidate
- // the tooltip render text layout, which would make layout run just done to
- // calculate the tooltip string size get immendiately disregarded.
- // This also addresses https://crbug.com/2181825 (after color update,
- // GetPreferredSize() will generate fresh render text layout, even if the
- // actual tooltip text hasn't changed).
- const gfx::Rect adjusted_bounds =
- GetTooltipBounds(location, tooltip_view->GetPreferredSize());
- widget_->SetBounds(adjusted_bounds);
}
void TooltipAura::Show() {
diff --git a/chromium/ui/views/corewm/tooltip_aura.h b/chromium/ui/views/corewm/tooltip_aura.h
index 56e2c913762..1432e650e8f 100644
--- a/chromium/ui/views/corewm/tooltip_aura.h
+++ b/chromium/ui/views/corewm/tooltip_aura.h
@@ -48,7 +48,7 @@ class VIEWS_EXPORT TooltipAura : public Tooltip, public WidgetObserver {
const gfx::Size& tooltip_size);
// Sets |widget_| to a new instance of TooltipWidget.
- void CreateTooltipWidget();
+ void CreateTooltipWidget(const gfx::Rect& bounds);
// Destroys |widget_|.
void DestroyWidget();
diff --git a/chromium/ui/views/drag_utils.h b/chromium/ui/views/drag_utils.h
index 1604d9ec0f5..40cf7b70fb0 100644
--- a/chromium/ui/views/drag_utils.h
+++ b/chromium/ui/views/drag_utils.h
@@ -7,7 +7,7 @@
#include <memory>
-#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/views_export.h"
@@ -24,7 +24,7 @@ VIEWS_EXPORT void RunShellDrag(gfx::NativeView view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source);
+ ui::mojom::DragEventSource source);
// Returns the device scale for the display associated with this |widget|'s
// native view.
diff --git a/chromium/ui/views/drag_utils_aura.cc b/chromium/ui/views/drag_utils_aura.cc
index 3142d9c0f56..f88836919f1 100644
--- a/chromium/ui/views/drag_utils_aura.cc
+++ b/chromium/ui/views/drag_utils_aura.cc
@@ -15,7 +15,7 @@ void RunShellDrag(gfx::NativeView view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
gfx::Point screen_location(location);
wm::ConvertPointToScreen(view, &screen_location);
aura::Window* root_window = view->GetRootWindow();
diff --git a/chromium/ui/views/drag_utils_mac.mm b/chromium/ui/views/drag_utils_mac.mm
index 59dc6eae3f3..256913aecae 100644
--- a/chromium/ui/views/drag_utils_mac.mm
+++ b/chromium/ui/views/drag_utils_mac.mm
@@ -10,7 +10,7 @@ void RunShellDrag(gfx::NativeView view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
NOTIMPLEMENTED();
}
diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn
index ba6f3d7603c..8daf32b2866 100644
--- a/chromium/ui/views/examples/BUILD.gn
+++ b/chromium/ui/views/examples/BUILD.gn
@@ -2,12 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
import("//tools/grit/grit_rule.gni")
-jumbo_component("views_examples_lib") {
+component("views_examples_lib") {
testonly = true
sources = [
@@ -45,8 +44,8 @@ jumbo_component("views_examples_lib") {
"layout_example_base.h",
"link_example.cc",
"link_example.h",
- "login_bubble_dialog.cc",
- "login_bubble_dialog.h",
+ "login_bubble_dialog_example.cc",
+ "login_bubble_dialog_example.h",
"menu_example.cc",
"menu_example.h",
"message_box_example.cc",
@@ -169,6 +168,13 @@ source_set("views_examples_proc") {
if (use_x11) {
deps += [ "//ui/gfx/x" ]
}
+ if (is_chromeos) {
+ sources += [
+ "examples_views_delegate_chromeos.cc",
+ "examples_views_delegate_chromeos.h",
+ ]
+ deps += [ "//ui/wm:test_support" ]
+ }
}
executable("views_examples") {
@@ -186,7 +192,7 @@ executable("views_examples") {
]
}
-jumbo_component("views_examples_with_content_lib") {
+component("views_examples_with_content_lib") {
testonly = true
sources = [
"examples_window_with_content.cc",
diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS
index cf9717c4951..c45f382ae97 100644
--- a/chromium/ui/views/examples/DEPS
+++ b/chromium/ui/views/examples/DEPS
@@ -11,3 +11,12 @@ include_rules = [
"+ui/snapshot", # Enable Skia Gold testing
"+ui/views_content_client",
]
+
+specific_include_rules = {
+ "examples_main_proc.cc": [
+ "+ui/wm/core/wm_state.h",
+ ],
+ "examples_views_delegate_chromeos.cc": [
+ "+ui/wm/test/wm_test_helper.h",
+ ],
+}
diff --git a/chromium/ui/views/examples/ax_example.cc b/chromium/ui/views/examples/ax_example.cc
index 9f4c28aa52f..03513d07f21 100644
--- a/chromium/ui/views/examples/ax_example.cc
+++ b/chromium/ui/views/examples/ax_example.cc
@@ -30,7 +30,7 @@ void AxExample::CreateExampleView(View* container) {
layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
announce_button_ = container->AddChildView(
- MdTextButton::Create(this, base::ASCIIToUTF16("AnnounceText")));
+ std::make_unique<MdTextButton>(this, base::ASCIIToUTF16("AnnounceText")));
}
void AxExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/chromium/ui/views/examples/bubble_example.cc b/chromium/ui/views/examples/bubble_example.cc
index 040cb595a18..9cb925e7761 100644
--- a/chromium/ui/views/examples/bubble_example.cc
+++ b/chromium/ui/views/examples/bubble_example.cc
@@ -118,6 +118,7 @@ void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) {
else if (event.IsAltDown())
arrow = BubbleBorder::FLOAT;
+ // |bubble| will be destroyed by its widget when the widget is destroyed.
ExampleBubble* bubble = new ExampleBubble(sender, arrow);
bubble->set_color(colors[(color_index++) % base::size(colors)]);
@@ -135,9 +136,8 @@ void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) {
if (sender == persistent_)
bubble->set_close_on_deactivate(false);
- BubbleDialogDelegateView::CreateBubble(bubble);
+ BubbleDialogDelegateView::CreateBubble(bubble)->Show();
- bubble->GetWidget()->Show();
LogStatus(
"Click with optional modifiers: [Ctrl] for set_arrow(NONE), "
"[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration.");
diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc
index 783c02e2f7c..61338598cc7 100644
--- a/chromium/ui/views/examples/button_example.cc
+++ b/chromium/ui/views/examples/button_example.cc
@@ -53,16 +53,16 @@ void ButtonExample::CreateExampleView(View* container) {
label_button->set_request_focus_on_press(true);
label_button_ = container->AddChildView(std::move(label_button));
- md_button_ = container->AddChildView(
- MdTextButton::Create(this, base::ASCIIToUTF16("Material Design")));
+ md_button_ = container->AddChildView(std::make_unique<views::MdTextButton>(
+ this, base::ASCIIToUTF16("Material Design")));
- auto md_disabled_button = MdTextButton::Create(
+ auto md_disabled_button = std::make_unique<views::MdTextButton>(
this, ASCIIToUTF16("Material Design Disabled Button"));
md_disabled_button->SetState(Button::STATE_DISABLED);
md_disabled_button_ = container->AddChildView(std::move(md_disabled_button));
- auto md_default_button =
- MdTextButton::Create(this, base::ASCIIToUTF16("Default"));
+ auto md_default_button = std::make_unique<views::MdTextButton>(
+ this, base::ASCIIToUTF16("Default"));
md_default_button->SetIsDefault(true);
md_default_button_ = container->AddChildView(std::move(md_default_button));
diff --git a/chromium/ui/views/examples/button_sticker_sheet.cc b/chromium/ui/views/examples/button_sticker_sheet.cc
index 1780375bb8b..2c5fc4ba542 100644
--- a/chromium/ui/views/examples/button_sticker_sheet.cc
+++ b/chromium/ui/views/examples/button_sticker_sheet.cc
@@ -80,12 +80,12 @@ std::vector<std::unique_ptr<MdTextButton>> MakeButtonsInState(
Button::ButtonState state) {
std::vector<std::unique_ptr<MdTextButton>> buttons;
const base::string16 button_text = base::ASCIIToUTF16("Button");
- auto primary = MdTextButton::Create(listener, button_text);
+ auto primary = std::make_unique<views::MdTextButton>(listener, button_text);
primary->SetProminent(true);
primary->SetState(state);
buttons.push_back(std::move(primary));
- auto secondary = MdTextButton::Create(listener, button_text);
+ auto secondary = std::make_unique<views::MdTextButton>(listener, button_text);
secondary->SetState(state);
buttons.push_back(std::move(secondary));
return buttons;
diff --git a/chromium/ui/views/examples/colored_dialog_example.cc b/chromium/ui/views/examples/colored_dialog_example.cc
index de07088cb51..8e3c367c511 100644
--- a/chromium/ui/views/examples/colored_dialog_example.cc
+++ b/chromium/ui/views/examples/colored_dialog_example.cc
@@ -55,9 +55,7 @@ class TextVectorImageButton : public views::MdTextButton {
TextVectorImageButton(ButtonListener* listener,
const base::string16& text,
const gfx::VectorIcon& icon)
- : MdTextButton(listener, style::CONTEXT_BUTTON_MD), icon_(icon) {
- SetText(text);
- }
+ : MdTextButton(listener, text), icon_(icon) {}
TextVectorImageButton(const TextVectorImageButton&) = delete;
TextVectorImageButton& operator=(const TextVectorImageButton&) = delete;
~TextVectorImageButton() override = default;
diff --git a/chromium/ui/views/examples/create_examples.cc b/chromium/ui/views/examples/create_examples.cc
index b9805665713..1046b4a32d9 100644
--- a/chromium/ui/views/examples/create_examples.cc
+++ b/chromium/ui/views/examples/create_examples.cc
@@ -18,7 +18,7 @@
#include "ui/views/examples/flex_layout_example.h"
#include "ui/views/examples/label_example.h"
#include "ui/views/examples/link_example.h"
-#include "ui/views/examples/login_bubble_dialog.h"
+#include "ui/views/examples/login_bubble_dialog_example.h"
#include "ui/views/examples/menu_example.h"
#include "ui/views/examples/message_box_example.h"
#include "ui/views/examples/multiline_example.h"
diff --git a/chromium/ui/views/examples/dialog_example.cc b/chromium/ui/views/examples/dialog_example.cc
index 70e7fd417d7..60e64c8efc3 100644
--- a/chromium/ui/views/examples/dialog_example.cc
+++ b/chromium/ui/views/examples/dialog_example.cc
@@ -49,18 +49,16 @@ class DialogExample::Delegate : public virtual DialogType {
void InitDelegate() {
this->SetLayoutManager(std::make_unique<FillLayout>());
- Label* body = new Label(parent_->body_->GetText());
+ auto body = std::make_unique<Label>(parent_->body_->GetText());
body->SetMultiLine(true);
body->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- this->AddChildView(body);
+ // Give the example code a way to change the body text.
+ parent_->last_body_label_ = this->AddChildView(std::move(body));
if (parent_->has_extra_button_->GetChecked()) {
- DialogDelegate::SetExtraView(MdTextButton::Create(
+ DialogDelegate::SetExtraView(std::make_unique<views::MdTextButton>(
nullptr, parent_->extra_button_label_->GetText()));
}
-
- // Give the example code a way to change the body text.
- parent_->last_body_label_ = body;
}
protected:
@@ -174,8 +172,8 @@ void DialogExample::CreateExampleView(View* container) {
kFixed, kButtonsColumnId, kFixed,
provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
- show_ =
- layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("Show")));
+ show_ = layout->AddView(
+ std::make_unique<views::MdTextButton>(this, base::ASCIIToUTF16("Show")));
}
void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) {
@@ -247,10 +245,12 @@ void DialogExample::ResizeDialog() {
void DialogExample::ButtonPressed(Button* sender, const ui::Event& event) {
if (sender == show_) {
if (bubble_->GetChecked()) {
+ // |bubble| will be destroyed by its widget when the widget is destroyed.
Bubble* bubble = new Bubble(this, sender);
last_dialog_ = bubble;
BubbleDialogDelegateView::CreateBubble(bubble);
} else {
+ // |dialog| will be destroyed by its widget when the widget is destroyed.
Dialog* dialog = new Dialog(this);
last_dialog_ = dialog;
dialog->InitDelegate();
@@ -312,7 +312,7 @@ void DialogExample::ContentsChanged(Textfield* sender,
void DialogExample::OnPerformAction(Combobox* combobox) {
bool enable = bubble_->GetChecked() || GetModalType() != ui::MODAL_TYPE_CHILD;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
enable = enable && GetModalType() != ui::MODAL_TYPE_SYSTEM;
#endif
show_->SetEnabled(enable);
diff --git a/chromium/ui/views/examples/example_base.cc b/chromium/ui/views/examples/example_base.cc
index 3602e15a474..f674b5ddea0 100644
--- a/chromium/ui/views/examples/example_base.cc
+++ b/chromium/ui/views/examples/example_base.cc
@@ -11,9 +11,8 @@ namespace examples {
ExampleBase::~ExampleBase() = default;
-ExampleBase::ExampleBase(const char* title) : example_title_(title) {
- container_ = new View();
-}
+ExampleBase::ExampleBase(const char* title)
+ : example_title_(title), container_(std::make_unique<View>()) {}
} // namespace examples
} // namespace views
diff --git a/chromium/ui/views/examples/example_base.h b/chromium/ui/views/examples/example_base.h
index 69e9105692a..60db7fa7f9f 100644
--- a/chromium/ui/views/examples/example_base.h
+++ b/chromium/ui/views/examples/example_base.h
@@ -5,6 +5,7 @@
#ifndef UI_VIEWS_EXAMPLES_EXAMPLE_BASE_H_
#define UI_VIEWS_EXAMPLES_EXAMPLE_BASE_H_
+#include <memory>
#include <string>
#include <vector>
@@ -24,7 +25,7 @@ class VIEWS_EXAMPLES_EXPORT ExampleBase {
virtual void CreateExampleView(View* parent) = 0;
const std::string& example_title() const { return example_title_; }
- View* example_view() { return container_; }
+ View* example_view() { return container_.get(); }
protected:
explicit ExampleBase(const char* title);
@@ -34,7 +35,7 @@ class VIEWS_EXAMPLES_EXPORT ExampleBase {
std::string example_title_;
// The view that contains the views example.
- View* container_;
+ std::unique_ptr<View> container_;
DISALLOW_COPY_AND_ASSIGN(ExampleBase);
};
diff --git a/chromium/ui/views/examples/examples_main_proc.cc b/chromium/ui/views/examples/examples_main_proc.cc
index ffdbc86fd5d..9c276783152 100644
--- a/chromium/ui/views/examples/examples_main_proc.cc
+++ b/chromium/ui/views/examples/examples_main_proc.cc
@@ -5,6 +5,7 @@
#include "ui/views/examples/examples_main_proc.h"
#include <memory>
+#include <string>
#include "base/base_switches.h"
#include "base/bind.h"
@@ -23,6 +24,7 @@
#include "base/test/test_discardable_memory_allocator.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
+#include "components/viz/common/features.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -50,6 +52,10 @@
#include "ui/wm/core/wm_state.h"
#endif
+#if defined(OS_CHROMEOS)
+#include "ui/views/examples/examples_views_delegate_chromeos.h"
+#endif
+
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_screen.h"
#endif
@@ -60,6 +66,7 @@
#endif
#if defined(USE_OZONE)
+#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
@@ -81,6 +88,14 @@ ExamplesExitCode ExamplesMainProc(bool under_test) {
// window to not render. See http://crbug.com/936249.
command_line->AppendSwitch(switches::kDisableDirectComposition);
+ // Disable skia renderer to use GL instead.
+ std::string disabled =
+ command_line->GetSwitchValueASCII(switches::kDisableFeatures);
+ if (!disabled.empty())
+ disabled += ",";
+ disabled += features::kUseSkiaRenderer.name;
+ command_line->AppendSwitchASCII(switches::kDisableFeatures, disabled);
+
base::FeatureList::InitializeInstance(
command_line->GetSwitchValueASCII(switches::kEnableFeatures),
command_line->GetSwitchValueASCII(switches::kDisableFeatures));
@@ -91,9 +106,11 @@ ExamplesExitCode ExamplesMainProc(bool under_test) {
mojo::core::Init();
#if defined(USE_OZONE)
- ui::OzonePlatform::InitParams params;
- params.single_process = true;
- ui::OzonePlatform::InitializeForGPU(params);
+ if (features::IsUsingOzonePlatform()) {
+ ui::OzonePlatform::InitParams params;
+ params.single_process = true;
+ ui::OzonePlatform::InitializeForGPU(params);
+ }
#endif
gl::init::InitializeGLOneOff();
@@ -141,10 +158,14 @@ ExamplesExitCode ExamplesMainProc(bool under_test) {
ExamplesExitCode compare_result = ExamplesExitCode::kSucceeded;
{
+#if defined(OS_CHROMEOS)
+ ExamplesViewsDelegateChromeOS views_delegate;
+#else
views::DesktopTestViewsDelegate views_delegate;
#if defined(USE_AURA)
wm::WMState wm_state;
#endif
+#endif
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
std::unique_ptr<display::Screen> desktop_screen =
base::WrapUnique(views::CreateDesktopScreen());
diff --git a/chromium/ui/views/examples/examples_views_delegate_chromeos.cc b/chromium/ui/views/examples/examples_views_delegate_chromeos.cc
new file mode 100644
index 00000000000..cc3365f3e19
--- /dev/null
+++ b/chromium/ui/views/examples/examples_views_delegate_chromeos.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 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 "ui/views/examples/examples_views_delegate_chromeos.h"
+
+#include "ui/views/examples/examples_window.h"
+#include "ui/wm/test/wm_test_helper.h"
+
+namespace views {
+namespace examples {
+
+namespace {
+constexpr gfx::Size kDefaultSize(1024, 768);
+} // namespace
+
+ExamplesViewsDelegateChromeOS::ExamplesViewsDelegateChromeOS()
+ : observer_(this) {}
+
+ExamplesViewsDelegateChromeOS::~ExamplesViewsDelegateChromeOS() = default;
+
+void ExamplesViewsDelegateChromeOS::OnBeforeWidgetInit(
+ Widget::InitParams* params,
+ internal::NativeWidgetDelegate* delegate) {
+ views::TestViewsDelegate::OnBeforeWidgetInit(params, delegate);
+ if (!params->parent && !params->context) {
+ DCHECK(!wm_helper_);
+
+ wm_helper_ = std::make_unique<wm::WMTestHelper>(kDefaultSize);
+ wm_helper_->host()->Show();
+ observer_.Add(wm_helper_->host());
+ params->context = wm_helper_->host()->window();
+ }
+}
+
+void ExamplesViewsDelegateChromeOS::OnHostCloseRequested(
+ aura::WindowTreeHost* host) {
+ Widget* widget = GetExamplesWidget();
+ if (widget) {
+ observer_.Remove(host);
+ widget->Close();
+ }
+}
+
+} // namespace examples
+} // namespace views
diff --git a/chromium/ui/views/examples/examples_views_delegate_chromeos.h b/chromium/ui/views/examples/examples_views_delegate_chromeos.h
new file mode 100644
index 00000000000..1bba04ff8ba
--- /dev/null
+++ b/chromium/ui/views/examples/examples_views_delegate_chromeos.h
@@ -0,0 +1,43 @@
+// Copyright (c) 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 UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
+#define UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
+
+#include <memory>
+
+#include "base/scoped_observer.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/aura/window_tree_host_observer.h"
+#include "ui/views/test/desktop_test_views_delegate.h"
+
+namespace wm {
+class WMTestHelper;
+}
+
+namespace views {
+namespace examples {
+
+class ExamplesViewsDelegateChromeOS : public DesktopTestViewsDelegate,
+ public aura::WindowTreeHostObserver {
+ public:
+ ExamplesViewsDelegateChromeOS();
+ ~ExamplesViewsDelegateChromeOS() override;
+
+ private:
+ // ViewsDelegate:
+ void OnBeforeWidgetInit(Widget::InitParams* params,
+ internal::NativeWidgetDelegate* delegate) override;
+
+ // aura::WindowTreeHostObserver:
+ void OnHostCloseRequested(aura::WindowTreeHost* host) override;
+
+ ScopedObserver<aura::WindowTreeHost, aura::WindowTreeHostObserver> observer_;
+ std::unique_ptr<wm::WMTestHelper> wm_helper_;
+};
+
+} // namespace examples
+} // namespace views
+
+#endif // UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc
index c8cb0f2ff9f..8b0a5ae3411 100644
--- a/chromium/ui/views/examples/examples_window.cc
+++ b/chromium/ui/views/examples/examples_window.cc
@@ -207,6 +207,12 @@ class ExamplesWindowContents : public WidgetDelegateView,
// static
ExamplesWindowContents* ExamplesWindowContents::instance_ = nullptr;
+Widget* GetExamplesWidget() {
+ return ExamplesWindowContents::instance()
+ ? ExamplesWindowContents::instance()->GetWidget()
+ : nullptr;
+}
+
void ShowExamplesWindow(base::OnceClosure on_close,
ExampleVector examples,
gfx::NativeWindow window_context) {
diff --git a/chromium/ui/views/examples/examples_window.h b/chromium/ui/views/examples/examples_window.h
index 93cff092763..05aa540ea1c 100644
--- a/chromium/ui/views/examples/examples_window.h
+++ b/chromium/ui/views/examples/examples_window.h
@@ -17,10 +17,15 @@
#include "ui/views/examples/views_examples_export.h"
namespace views {
+class Widget;
+
namespace examples {
VIEWS_EXAMPLES_EXPORT extern const char kExamplesWidgetName[];
+// Returns the current widget.
+VIEWS_EXAMPLES_EXPORT Widget* GetExamplesWidget();
+
// Shows a window with the views examples in it. |extra_examples| contains any
// additional examples to add. |window_context| is used to determine where the
// window should be created (see |Widget::InitParams::context| for details).
diff --git a/chromium/ui/views/examples/examples_with_content_main.cc b/chromium/ui/views/examples/examples_with_content_main.cc
index 501a2fb7131..fdc4a4f8bd6 100644
--- a/chromium/ui/views/examples/examples_with_content_main.cc
+++ b/chromium/ui/views/examples/examples_with_content_main.cc
@@ -12,7 +12,7 @@
#include "ui/views/examples/examples_window_with_content.h"
#include "ui/views_content_client/views_content_client.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "sandbox/mac/seatbelt_exec.h"
#endif
@@ -65,7 +65,7 @@ int main(int argc, const char** argv) {
ui::ViewsContentClient views_content_client(argc, argv);
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// ViewsContentClient expects a const char** argv and
// CreateFromArgumentsResult expects a regular char** argv. Given this is a
diff --git a/chromium/ui/views/examples/layout_example_base.cc b/chromium/ui/views/examples/layout_example_base.cc
index a2e85e23749..dabba6ad75d 100644
--- a/chromium/ui/views/examples/layout_example_base.cc
+++ b/chromium/ui/views/examples/layout_example_base.cc
@@ -239,7 +239,7 @@ void LayoutExampleBase::CreateExampleView(View* container) {
int vertical_pos = kLayoutExampleVerticalSpacing;
int horizontal_pos = kLayoutExampleLeftPadding;
- auto add_button = MdTextButton::Create(
+ auto add_button = std::make_unique<MdTextButton>(
this, l10n_util::GetStringUTF16(IDS_LAYOUT_BASE_ADD_LABEL));
add_button->SetPosition(gfx::Point(horizontal_pos, vertical_pos));
add_button->SizeToPreferredSize();
diff --git a/chromium/ui/views/examples/login_bubble_dialog.cc b/chromium/ui/views/examples/login_bubble_dialog_example.cc
index a83e00b9d78..c7502cc8be4 100644
--- a/chromium/ui/views/examples/login_bubble_dialog.cc
+++ b/chromium/ui/views/examples/login_bubble_dialog_example.cc
@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/views/examples/login_bubble_dialog.h"
+#include "ui/views/examples/login_bubble_dialog_example.h"
+
+#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/callback_forward.h"
@@ -47,6 +50,8 @@ Textfield* AddFormRow(LoginBubbleDialogView* bubble,
void LoginBubbleDialogView::Show(View* anchor_view,
BubbleBorder::Arrow anchor_position,
OnSubmitCallback accept_callback) {
+ // LoginBubbleDialogView will be destroyed by the widget when the created
+ // widget is destroyed.
BubbleDialogDelegateView::CreateBubble(
new LoginBubbleDialogView(anchor_view, anchor_position,
std::move(accept_callback)))
@@ -128,22 +133,24 @@ void LoginBubbleDialogExample::CreateExampleView(View* container) {
GridLayout::kFixedSize,
GridLayout::ColumnSize::kUsePreferred, 0, 0);
column_set->AddPaddingColumn(0, label_padding);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0,
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1.0,
GridLayout::ColumnSize::kUsePreferred, 0, 0);
layout->StartRowWithPadding(0, 0, 0, related_control_padding);
- button_ = layout->AddView(
- MdTextButton::Create(this, GetStringUTF16(IDS_LOGIN_SHOW_BUTTON_LABEL)));
+ button_ = layout->AddView(std::make_unique<MdTextButton>(
+ this, GetStringUTF16(IDS_LOGIN_SHOW_BUTTON_LABEL)));
layout->StartRowWithPadding(0, 0, 0, related_control_padding);
- layout->AddView(std::make_unique<Label>(
+ username_label_ = layout->AddView(std::make_unique<Label>(
l10n_util::GetStringUTF16(IDS_LOGIN_USERNAME_LABEL)));
- username_label_ = layout->AddView(std::make_unique<Label>());
+ username_label_->SetVisible(false);
+ username_input_ = layout->AddView(std::make_unique<Label>());
layout->StartRowWithPadding(0, 0, 0, related_control_padding);
- layout->AddView(std::make_unique<Label>(
+ password_label_ = layout->AddView(std::make_unique<Label>(
l10n_util::GetStringUTF16(IDS_LOGIN_PASSWORD_LABEL)));
- password_label_ = layout->AddView(std::make_unique<Label>());
+ password_label_->SetVisible(false);
+ password_input_ = layout->AddView(std::make_unique<Label>());
}
void LoginBubbleDialogExample::ButtonPressed(Button* sender,
@@ -156,8 +163,10 @@ void LoginBubbleDialogExample::ButtonPressed(Button* sender,
void LoginBubbleDialogExample::OnSubmit(base::string16 username,
base::string16 password) {
- username_label_->SetText(username);
- password_label_->SetText(password);
+ username_label_->SetVisible(true);
+ username_input_->SetText(username);
+ password_label_->SetVisible(true);
+ password_input_->SetText(password);
}
} // namespace examples
diff --git a/chromium/ui/views/examples/login_bubble_dialog.h b/chromium/ui/views/examples/login_bubble_dialog_example.h
index d62ad3ef64b..d4323ad2190 100644
--- a/chromium/ui/views/examples/login_bubble_dialog.h
+++ b/chromium/ui/views/examples/login_bubble_dialog_example.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_
-#define UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_
+#ifndef UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_EXAMPLE_H_
+#define UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_EXAMPLE_H_
#include "base/strings/string16.h"
#include "ui/views/bubble/bubble_border.h"
@@ -62,10 +62,12 @@ class LoginBubbleDialogExample : public ExampleBase, public ButtonListener {
private:
LabelButton* button_ = nullptr;
Label* username_label_ = nullptr;
+ Label* username_input_ = nullptr;
Label* password_label_ = nullptr;
+ Label* password_input_ = nullptr;
};
} // namespace examples
} // namespace views
-#endif // UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_H_
+#endif // UI_VIEWS_EXAMPLES_LOGIN_BUBBLE_DIALOG_EXAMPLE_H_
diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc
index 76d851f1a6b..7eae61ccf23 100644
--- a/chromium/ui/views/examples/menu_example.cc
+++ b/chromium/ui/views/examples/menu_example.cc
@@ -172,7 +172,7 @@ void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) {
// ExampleMenuButton -----------------------------------------------------------
ExampleMenuButton::ExampleMenuButton(const base::string16& test)
- : MenuButton(test, this) {}
+ : MenuButton(this, test) {}
ExampleMenuButton::~ExampleMenuButton() = default;
diff --git a/chromium/ui/views/examples/message_box_example.cc b/chromium/ui/views/examples/message_box_example.cc
index 13aa6925d66..120609a973b 100644
--- a/chromium/ui/views/examples/message_box_example.cc
+++ b/chromium/ui/views/examples/message_box_example.cc
@@ -13,7 +13,8 @@
#include "ui/views/controls/message_box_view.h"
#include "ui/views/examples/examples_window.h"
#include "ui/views/examples/grit/views_examples_resources.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/flex_layout.h"
#include "ui/views/view.h"
using l10n_util::GetStringUTF16;
@@ -28,33 +29,22 @@ MessageBoxExample::MessageBoxExample()
MessageBoxExample::~MessageBoxExample() = default;
void MessageBoxExample::CreateExampleView(View* container) {
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
+ container->SetLayoutManager(
+ std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical));
- auto message_box_view = std::make_unique<MessageBoxView>(
- MessageBoxView::InitParams(GetStringUTF16(IDS_MESSAGE_INTRO_LABEL)));
- message_box_view->SetCheckBoxLabel(
+ message_box_view_ = container->AddChildView(std::make_unique<MessageBoxView>(
+ MessageBoxView::InitParams(GetStringUTF16(IDS_MESSAGE_INTRO_LABEL))));
+ message_box_view_->SetCheckBoxLabel(
GetStringUTF16(IDS_MESSAGE_CHECK_BOX_LABEL));
- const int message_box_column = 0;
- ColumnSet* column_set = layout->AddColumnSet(message_box_column);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- layout->StartRow(1 /* expand */, message_box_column);
- message_box_view_ = layout->AddView(std::move(message_box_view));
+ View* button_panel = container->AddChildView(std::make_unique<View>());
+ button_panel->SetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal)
+ .SetMainAxisAlignment(LayoutAlignment::kStart);
- const int button_column = 1;
- column_set = layout->AddColumnSet(button_column);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
-
- layout->StartRow(0 /* no expand */, button_column);
-
- status_ = layout->AddView(std::make_unique<LabelButton>(
+ status_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_MESSAGE_STATUS_LABEL)));
- toggle_ = layout->AddView(std::make_unique<LabelButton>(
+ toggle_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_MESSAGE_TOGGLE_LABEL)));
}
diff --git a/chromium/ui/views/examples/progress_bar_example.cc b/chromium/ui/views/examples/progress_bar_example.cc
index 4879f5fe217..b101feb20e3 100644
--- a/chromium/ui/views/examples/progress_bar_example.cc
+++ b/chromium/ui/views/examples/progress_bar_example.cc
@@ -43,11 +43,11 @@ void ProgressBarExample::CreateExampleView(View* container) {
GridLayout::ColumnSize::kUsePreferred, 0, 0);
layout->StartRow(0, 0);
- minus_button_ =
- layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("-")));
+ minus_button_ = layout->AddView(
+ std::make_unique<views::MdTextButton>(this, base::ASCIIToUTF16("-")));
progress_bar_ = layout->AddView(std::make_unique<ProgressBar>());
- plus_button_ =
- layout->AddView(MdTextButton::Create(this, base::ASCIIToUTF16("+")));
+ plus_button_ = layout->AddView(
+ std::make_unique<views::MdTextButton>(this, base::ASCIIToUTF16("+")));
layout->StartRowWithPadding(0, 0, 0, 10);
layout->AddView(
diff --git a/chromium/ui/views/examples/radio_button_example.cc b/chromium/ui/views/examples/radio_button_example.cc
index 598c8b18a20..6345a5f1fe4 100644
--- a/chromium/ui/views/examples/radio_button_example.cc
+++ b/chromium/ui/views/examples/radio_button_example.cc
@@ -16,7 +16,7 @@
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/examples/examples_window.h"
#include "ui/views/examples/grit/views_examples_resources.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/box_layout.h"
#include "ui/views/view.h"
using l10n_util::GetStringUTF16;
@@ -39,25 +39,21 @@ RadioButtonExample::RadioButtonExample()
RadioButtonExample::~RadioButtonExample() = default;
void RadioButtonExample::CreateExampleView(View* container) {
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
- ColumnSet* column_set = layout->AddColumnSet(0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
+ container->SetLayoutManager(
+ std::make_unique<views::BoxLayout>(BoxLayout::Orientation::kVertical));
+
const int group = 1;
for (size_t i = 0; i < 3; ++i) {
- layout->StartRow(0, 0);
- radio_buttons_.push_back(layout->AddView(std::make_unique<RadioButton>(
- base::UTF8ToUTF16(base::StringPrintf("Radio %d in group %d",
- static_cast<int>(i) + 1, group)),
- group)));
+ radio_buttons_.push_back(
+ container->AddChildView(std::make_unique<RadioButton>(
+ base::UTF8ToUTF16(base::StringPrintf(
+ "Radio %d in group %d", static_cast<int>(i) + 1, group)),
+ group)));
}
- layout->StartRow(0, 0);
- select_ = layout->AddView(std::make_unique<LabelButton>(
+ select_ = container->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_RADIO_BUTTON_SELECT_BUTTON_LABEL)));
- layout->StartRow(0, 0);
- status_ = layout->AddView(std::make_unique<LabelButton>(
+ status_ = container->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_RADIO_BUTTON_STATUS_LABEL)));
}
diff --git a/chromium/ui/views/examples/scroll_view_example.cc b/chromium/ui/views/examples/scroll_view_example.cc
index 90a68d46c93..91c3756518d 100644
--- a/chromium/ui/views/examples/scroll_view_example.cc
+++ b/chromium/ui/views/examples/scroll_view_example.cc
@@ -20,8 +20,10 @@
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/examples/grit/views_examples_resources.h"
#include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
using l10n_util::GetStringUTF16;
using l10n_util::GetStringUTF8;
@@ -83,33 +85,35 @@ void ScrollViewExample::CreateExampleView(View* container) {
scrollable_->SetBounds(0, 0, 1000, 100);
scrollable_->SetColor(SK_ColorYELLOW, SK_ColorCYAN);
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
+ container->SetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kVertical);
+
+ auto full_flex = FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
+ MaximumFlexSizeRule::kUnbounded)
+ .WithWeight(1);
// Add scroll view.
- ColumnSet* column_set = layout->AddColumnSet(0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- layout->StartRow(1, 0);
- scroll_view_ = layout->AddView(std::move(scroll_view));
+ scroll_view_ = container->AddChildView(std::move(scroll_view));
+ scroll_view_->SetProperty(views::kFlexBehaviorKey, full_flex);
// Add control buttons.
- column_set = layout->AddColumnSet(1);
- for (size_t i = 0; i < 5; i++) {
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- }
- layout->StartRow(0, 1);
- wide_ = layout->AddView(std::make_unique<LabelButton>(
+ auto* button_panel = container->AddChildView(std::make_unique<View>());
+ button_panel->SetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal);
+
+ wide_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_SCROLL_VIEW_WIDE_LABEL)));
- tall_ = layout->AddView(std::make_unique<LabelButton>(
+ tall_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_SCROLL_VIEW_TALL_LABEL)));
- big_square_ = layout->AddView(std::make_unique<LabelButton>(
+ big_square_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_SCROLL_VIEW_BIG_SQUARE_LABEL)));
- small_square_ = layout->AddView(std::make_unique<LabelButton>(
+ small_square_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_SCROLL_VIEW_SMALL_SQUARE_LABEL)));
- scroll_to_ = layout->AddView(std::make_unique<LabelButton>(
+ scroll_to_ = button_panel->AddChildView(std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_SCROLL_VIEW_SCROLL_TO_LABEL)));
+
+ for (View* child : button_panel->children())
+ child->SetProperty(views::kFlexBehaviorKey, full_flex);
}
void ScrollViewExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/chromium/ui/views/examples/tabbed_pane_example.cc b/chromium/ui/views/examples/tabbed_pane_example.cc
index b561f983ffc..11bcdcf14e6 100644
--- a/chromium/ui/views/examples/tabbed_pane_example.cc
+++ b/chromium/ui/views/examples/tabbed_pane_example.cc
@@ -13,7 +13,9 @@
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include "ui/views/examples/examples_window.h"
#include "ui/views/examples/grit/views_examples_resources.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/view_class_properties.h"
using l10n_util::GetStringUTF16;
using l10n_util::GetStringUTF8;
@@ -36,15 +38,15 @@ void TabbedPaneExample::CreateExampleView(View* container) {
auto select_at = std::make_unique<LabelButton>(
this, GetStringUTF16(IDS_TABBED_PANE_SELECT_1_LABEL));
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
+ container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kVertical);
- const int tabbed_pane_column = 0;
- ColumnSet* column_set = layout->AddColumnSet(tabbed_pane_column);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- layout->StartRow(1 /* expand */, tabbed_pane_column);
- tabbed_pane_ = layout->AddView(std::move(tabbed_pane));
+ auto full_flex = FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
+ MaximumFlexSizeRule::kUnbounded)
+ .WithWeight(1);
+
+ tabbed_pane_ = container->AddChildView(std::move(tabbed_pane));
+ tabbed_pane_->SetProperty(views::kFlexBehaviorKey, full_flex);
// Create a few tabs with a button first.
AddButton(GetStringUTF16(IDS_TABBED_PANE_TAB_1_LABEL));
@@ -52,17 +54,14 @@ void TabbedPaneExample::CreateExampleView(View* container) {
AddButton(GetStringUTF16(IDS_TABBED_PANE_TAB_3_LABEL));
// Add control buttons horizontally.
- const int button_column = 1;
- column_set = layout->AddColumnSet(button_column);
- for (size_t i = 0; i < 3; i++) {
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- }
-
- layout->StartRow(0 /* no expand */, button_column);
- add_ = layout->AddView(std::move(add));
- add_at_ = layout->AddView(std::move(add_at));
- select_at_ = layout->AddView(std::move(select_at));
+ auto* button_panel = container->AddChildView(std::make_unique<View>());
+ button_panel->SetLayoutManager(std::make_unique<views::FlexLayout>());
+ add_ = button_panel->AddChildView(std::move(add));
+ add_at_ = button_panel->AddChildView(std::move(add_at));
+ select_at_ = button_panel->AddChildView(std::move(select_at));
+
+ for (View* view : button_panel->children())
+ view->SetProperty(views::kFlexBehaviorKey, full_flex);
}
void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) {
diff --git a/chromium/ui/views/examples/table_example.cc b/chromium/ui/views/examples/table_example.cc
index c272f3b3eca..b2ba1eac5d1 100644
--- a/chromium/ui/views/examples/table_example.cc
+++ b/chromium/ui/views/examples/table_example.cc
@@ -16,7 +16,9 @@
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/examples/examples_window.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/view_class_properties.h"
using base::ASCIIToUTF16;
@@ -44,8 +46,8 @@ TableExample::~TableExample() {
}
void TableExample::CreateExampleView(View* container) {
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
+ container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kVertical);
std::vector<ui::TableColumn> columns;
columns.push_back(TestTableColumn(0, "Fruit"));
@@ -54,35 +56,26 @@ void TableExample::CreateExampleView(View* container) {
columns.push_back(TestTableColumn(2, "Origin"));
columns.push_back(TestTableColumn(3, "Price"));
columns.back().alignment = ui::TableColumn::RIGHT;
+
+ auto full_flex = FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
+ MaximumFlexSizeRule::kUnbounded)
+ .WithWeight(1);
+
+ // Make table
auto table = std::make_unique<TableView>(this, columns, ICON_AND_TEXT, true);
table->SetGrouper(this);
table->set_observer(this);
- icon1_.allocN32Pixels(16, 16);
- SkCanvas canvas1(icon1_);
- canvas1.drawColor(SK_ColorRED);
-
- icon2_.allocN32Pixels(16, 16);
- SkCanvas canvas2(icon2_);
- canvas2.drawColor(SK_ColorBLUE);
-
- ColumnSet* column_set = layout->AddColumnSet(0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- layout->StartRow(1 /* expand */, 0);
table_ = table.get();
- layout->AddView(TableView::CreateScrollViewWithTable(std::move(table)));
+ container
+ ->AddChildView(TableView::CreateScrollViewWithTable(std::move(table)))
+ ->SetProperty(views::kFlexBehaviorKey, full_flex);
- column_set = layout->AddColumnSet(1);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0.5f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
+ icon1_.allocN32Pixels(16, 16);
+ icon2_.allocN32Pixels(16, 16);
- layout->StartRow(0 /* no expand */, 1);
+ SkCanvas canvas1(icon1_), canvas2(icon2_);
+ canvas1.drawColor(SK_ColorRED);
+ canvas2.drawColor(SK_ColorBLUE);
auto make_checkbox =
[this](base::string16 text) -> std::unique_ptr<Checkbox> {
@@ -91,14 +84,21 @@ void TableExample::CreateExampleView(View* container) {
return result;
};
- column1_visible_checkbox_ =
- layout->AddView(make_checkbox(ASCIIToUTF16("Fruit column visible")));
- column2_visible_checkbox_ =
- layout->AddView(make_checkbox(ASCIIToUTF16("Color column visible")));
- column3_visible_checkbox_ =
- layout->AddView(make_checkbox(ASCIIToUTF16("Origin column visible")));
- column4_visible_checkbox_ =
- layout->AddView(make_checkbox(ASCIIToUTF16("Price column visible")));
+ // Make buttons
+ auto* button_panel = container->AddChildView(std::make_unique<View>());
+ button_panel->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal);
+ column1_visible_checkbox_ = button_panel->AddChildView(
+ make_checkbox(ASCIIToUTF16("Fruit column visible")));
+ column2_visible_checkbox_ = button_panel->AddChildView(
+ make_checkbox(ASCIIToUTF16("Color column visible")));
+ column3_visible_checkbox_ = button_panel->AddChildView(
+ make_checkbox(ASCIIToUTF16("Origin column visible")));
+ column4_visible_checkbox_ = button_panel->AddChildView(
+ make_checkbox(ASCIIToUTF16("Price column visible")));
+
+ for (View* child : button_panel->children())
+ child->SetProperty(views::kFlexBehaviorKey, full_flex);
}
int TableExample::RowCount() {
diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc
index dea712ae3e9..5c9d104514c 100644
--- a/chromium/ui/views/examples/tree_view_example.cc
+++ b/chromium/ui/views/examples/tree_view_example.cc
@@ -15,7 +15,9 @@
#include "ui/views/controls/tree/tree_view.h"
#include "ui/views/controls/tree/tree_view_drawing_provider.h"
#include "ui/views/examples/grit/views_examples_resources.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/view_class_properties.h"
using l10n_util::GetStringUTF16;
using l10n_util::GetStringUTF8;
@@ -105,29 +107,29 @@ void TreeViewExample::CreateExampleView(View* container) {
change_title->SetFocusForPlatform();
change_title->set_request_focus_on_press(true);
- GridLayout* layout =
- container->SetLayoutManager(std::make_unique<views::GridLayout>());
+ container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kVertical);
+
+ auto full_flex = FlexSpecification(MinimumFlexSizeRule::kScaleToZero,
+ MaximumFlexSizeRule::kUnbounded)
+ .WithWeight(1);
- const int tree_view_column = 0;
- ColumnSet* column_set = layout->AddColumnSet(tree_view_column);
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- layout->StartRow(1 /* expand */, tree_view_column);
tree_view_ = tree_view.get();
- layout->AddView(TreeView::CreateScrollViewWithTree(std::move(tree_view)));
+ container
+ ->AddChildView(TreeView::CreateScrollViewWithTree(std::move(tree_view)))
+ ->SetProperty(views::kFlexBehaviorKey, full_flex);
// Add control buttons horizontally.
- const int button_column = 1;
- column_set = layout->AddColumnSet(button_column);
- for (size_t i = 0; i < 3; i++) {
- column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
- GridLayout::ColumnSize::kUsePreferred, 0, 0);
- }
+ auto* button_panel = container->AddChildView(std::make_unique<View>());
+ button_panel->SetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal);
+
+ add_ = button_panel->AddChildView(std::move(add));
+ remove_ = button_panel->AddChildView(std::move(remove));
+ change_title_ = button_panel->AddChildView(std::move(change_title));
- layout->StartRow(0 /* no expand */, button_column);
- add_ = layout->AddView(std::move(add));
- remove_ = layout->AddView(std::move(remove));
- change_title_ = layout->AddView(std::move(change_title));
+ for (View* view : button_panel->children())
+ view->SetProperty(views::kFlexBehaviorKey, full_flex);
}
void TreeViewExample::AddNewNode() {
diff --git a/chromium/ui/views/examples/vector_example.cc b/chromium/ui/views/examples/vector_example.cc
index c447df47fcf..6ba3ef07b2f 100644
--- a/chromium/ui/views/examples/vector_example.cc
+++ b/chromium/ui/views/examples/vector_example.cc
@@ -70,8 +70,9 @@ class VectorIconGallery : public View,
file_container->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kHorizontal, gfx::Insets(10), 10));
file_chooser_ = file_container->AddChildView(std::move(file_chooser));
- file_go_button_ = file_container->AddChildView(
- MdTextButton::Create(this, GetStringUTF16(IDS_VECTOR_RENDER_LABEL)));
+ file_go_button_ =
+ file_container->AddChildView(std::make_unique<MdTextButton>(
+ this, GetStringUTF16(IDS_VECTOR_RENDER_LABEL)));
file_box->SetFlexForView(file_chooser_, 1);
AddChildView(std::move(file_container));
diff --git a/chromium/ui/views/examples/webview_example.cc b/chromium/ui/views/examples/webview_example.cc
index 1571dd2fc9e..338558927ee 100644
--- a/chromium/ui/views/examples/webview_example.cc
+++ b/chromium/ui/views/examples/webview_example.cc
@@ -24,10 +24,10 @@ WebViewExample::WebViewExample(content::BrowserContext* browser_context)
WebViewExample::~WebViewExample() = default;
void WebViewExample::CreateExampleView(View* container) {
- webview_ = new WebView(browser_context_);
+ webview_ =
+ container->AddChildView(std::make_unique<WebView>(browser_context_));
webview_->GetWebContents()->SetDelegate(this);
container->SetLayoutManager(std::make_unique<FillLayout>());
- container->AddChildView(webview_);
webview_->LoadInitialURL(GURL("http://www.google.com/"));
webview_->GetWebContents()->Focus();
diff --git a/chromium/ui/views/examples/widget_example.cc b/chromium/ui/views/examples/widget_example.cc
index 7a4106184e2..175c7cba4b6 100644
--- a/chromium/ui/views/examples/widget_example.cc
+++ b/chromium/ui/views/examples/widget_example.cc
@@ -51,11 +51,12 @@ WidgetDialogExample::WidgetDialogExample() {
SetBackground(CreateSolidBackground(SK_ColorGRAY));
SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kVertical, gfx::Insets(10), 10));
- SetExtraView(
- MdTextButton::Create(nullptr, GetStringUTF16(IDS_WIDGET_EXTRA_BUTTON)));
+ SetExtraView(std::make_unique<MdTextButton>(
+ nullptr, GetStringUTF16(IDS_WIDGET_EXTRA_BUTTON)));
SetFootnoteView(
std::make_unique<Label>(GetStringUTF16(IDS_WIDGET_FOOTNOTE_LABEL)));
- AddChildView(new Label(GetStringUTF16(IDS_WIDGET_DIALOG_CONTENTS_LABEL)));
+ AddChildView(std::make_unique<Label>(
+ GetStringUTF16(IDS_WIDGET_DIALOG_CONTENTS_LABEL)));
}
WidgetDialogExample::~WidgetDialogExample() = default;
@@ -79,7 +80,7 @@ void WidgetExample::CreateExampleView(View* container) {
DIALOG);
BuildButton(container, GetStringUTF16(IDS_WIDGET_MODAL_BUTTON_LABEL),
MODAL_DIALOG);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Windows does not support TYPE_CONTROL top-level widgets.
BuildButton(container, GetStringUTF16(IDS_WIDGET_CHILD_WIDGET_BUTTON_LABEL),
CHILD);
@@ -89,11 +90,11 @@ void WidgetExample::CreateExampleView(View* container) {
void WidgetExample::BuildButton(View* container,
const base::string16& label,
int tag) {
- LabelButton* button = new LabelButton(this, label);
+ LabelButton* button =
+ container->AddChildView(std::make_unique<LabelButton>(this, label));
button->SetFocusForPlatform();
button->set_request_focus_on_press(true);
button->set_tag(tag);
- container->AddChildView(button);
}
void WidgetExample::ShowWidget(View* sender, Widget::InitParams params) {
@@ -102,18 +103,18 @@ void WidgetExample::ShowWidget(View* sender, Widget::InitParams params) {
params.bounds =
gfx::Rect(sender->GetBoundsInScreen().CenterPoint(), gfx::Size(300, 200));
+ // A widget handles its own lifetime.
Widget* widget = new Widget();
widget->Init(std::move(params));
// If the Widget has no contents by default, add a view with a 'Close' button.
if (!widget->GetContentsView()) {
- View* contents = new View();
+ View* contents = widget->SetContentsView(std::make_unique<View>());
contents->SetLayoutManager(
std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
contents->SetBackground(CreateSolidBackground(SK_ColorGRAY));
BuildButton(contents, GetStringUTF16(IDS_WIDGET_CLOSE_BUTTON_LABEL),
CLOSE_WIDGET);
- widget->SetContentsView(contents);
}
widget->Show();
@@ -125,12 +126,16 @@ void WidgetExample::ButtonPressed(Button* sender, const ui::Event& event) {
ShowWidget(sender, Widget::InitParams(Widget::InitParams::TYPE_POPUP));
break;
case DIALOG: {
+ // WidgetDialogExample will be destroyed by the widget when the created
+ // widget is destroyed.
DialogDelegate::CreateDialogWidget(new WidgetDialogExample(), nullptr,
sender->GetWidget()->GetNativeView())
->Show();
break;
}
case MODAL_DIALOG: {
+ // ModalDialogExample will be destroyed by the widget when the created
+ // widget is destroyed.
DialogDelegate::CreateDialogWidget(new ModalDialogExample(), nullptr,
sender->GetWidget()->GetNativeView())
->Show();
diff --git a/chromium/ui/views/focus/external_focus_tracker.h b/chromium/ui/views/focus/external_focus_tracker.h
index 22acdd5eef1..5bd2ad6e560 100644
--- a/chromium/ui/views/focus/external_focus_tracker.h
+++ b/chromium/ui/views/focus/external_focus_tracker.h
@@ -8,7 +8,6 @@
#include <memory>
#include "base/compiler_specific.h"
-#include "base/macros.h"
#include "ui/views/focus/focus_manager.h"
namespace views {
@@ -32,6 +31,9 @@ class ViewTracker;
class VIEWS_EXPORT ExternalFocusTracker : public FocusChangeListener {
public:
ExternalFocusTracker(View* parent_view, FocusManager* focus_manager);
+
+ ExternalFocusTracker(const ExternalFocusTracker&) = delete;
+ ExternalFocusTracker& operator=(const ExternalFocusTracker&) = delete;
~ExternalFocusTracker() override;
// FocusChangeListener:
@@ -68,8 +70,6 @@ class VIEWS_EXPORT ExternalFocusTracker : public FocusChangeListener {
// Holds the last focused view.
std::unique_ptr<ViewTracker> last_focused_view_tracker_;
-
- DISALLOW_COPY_AND_ASSIGN(ExternalFocusTracker);
};
} // namespace views
diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc
index 536755394f4..a6900d0ff32 100644
--- a/chromium/ui/views/focus/focus_manager.cc
+++ b/chromium/ui/views/focus/focus_manager.cc
@@ -339,9 +339,9 @@ void FocusManager::SetFocusedViewWithReason(View* view,
// Change this to DCHECK once it's resolved.
CHECK(!view || ContainsView(view));
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// TODO(warx): There are some AccessiblePaneViewTest failed on macosx.
- // crbug.com/650859. Remove !defined(OS_MACOSX) once that is fixed.
+ // crbug.com/650859. Remove !defined(OS_APPLE) once that is fixed.
//
// If the widget isn't active store the focused view and then attempt to
// activate the widget. If activation succeeds |view| will be focused.
@@ -528,7 +528,7 @@ bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) {
if (delegate_ && delegate_->ProcessAccelerator(accelerator))
return true;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On MacOS accelerators are processed when a bubble is opened without
// manual redirection to bubble anchor widget. Including redirect on MacOS
// breaks processing accelerators by the bubble itself.
@@ -593,7 +593,7 @@ bool FocusManager::IsFocusable(View* view) const {
DCHECK(view);
// |keyboard_accessible_| is only used on Mac.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
return keyboard_accessible_ ? view->IsAccessibilityFocusable()
: view->IsFocusable();
#else
diff --git a/chromium/ui/views/focus/focus_manager.h b/chromium/ui/views/focus/focus_manager.h
index bd1a2aef25e..1f7df49ae79 100644
--- a/chromium/ui/views/focus/focus_manager.h
+++ b/chromium/ui/views/focus/focus_manager.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "base/macros.h"
#include "base/observer_list.h"
#include "ui/base/accelerators/accelerator_manager.h"
#include "ui/views/view_observer.h"
@@ -143,6 +142,9 @@ class VIEWS_EXPORT FocusManager : public ViewObserver {
enum class FocusCycleWrapping { kEnabled, kDisabled };
FocusManager(Widget* widget, std::unique_ptr<FocusManagerDelegate> delegate);
+
+ FocusManager(const FocusManager&) = delete;
+ FocusManager& operator=(const FocusManager&) = delete;
~FocusManager() override;
// Processes the passed key event for accelerators and keyboard traversal.
@@ -379,8 +381,6 @@ class VIEWS_EXPORT FocusManager : public ViewObserver {
// Whether FocusManager is currently trying to restore a focused view.
bool in_restoring_focused_view_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(FocusManager);
};
} // namespace views
diff --git a/chromium/ui/views/focus/focus_manager_factory.cc b/chromium/ui/views/focus/focus_manager_factory.cc
index 9a63c416054..e94cf82a7e9 100644
--- a/chromium/ui/views/focus/focus_manager_factory.cc
+++ b/chromium/ui/views/focus/focus_manager_factory.cc
@@ -14,15 +14,16 @@ namespace {
class DefaultFocusManagerFactory : public FocusManagerFactory {
public:
DefaultFocusManagerFactory() = default;
+
+ DefaultFocusManagerFactory(const DefaultFocusManagerFactory&) = delete;
+ DefaultFocusManagerFactory& operator=(const DefaultFocusManagerFactory&) =
+ delete;
~DefaultFocusManagerFactory() override = default;
protected:
std::unique_ptr<FocusManager> CreateFocusManager(Widget* widget) override {
return std::make_unique<FocusManager>(widget, nullptr /* delegate */);
}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DefaultFocusManagerFactory);
};
FocusManagerFactory* g_focus_manager_factory = nullptr;
diff --git a/chromium/ui/views/focus/focus_manager_factory.h b/chromium/ui/views/focus/focus_manager_factory.h
index 1a91b9e1d49..9b0994ebc81 100644
--- a/chromium/ui/views/focus/focus_manager_factory.h
+++ b/chromium/ui/views/focus/focus_manager_factory.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "base/macros.h"
#include "ui/views/views_export.h"
namespace views {
@@ -28,13 +27,13 @@ class VIEWS_EXPORT FocusManagerFactory {
protected:
FocusManagerFactory();
+
+ FocusManagerFactory(const FocusManagerFactory&) = delete;
+ FocusManagerFactory& operator=(const FocusManagerFactory&) = delete;
virtual ~FocusManagerFactory();
// Create a FocusManager for the given |widget|.
virtual std::unique_ptr<FocusManager> CreateFocusManager(Widget* widget) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FocusManagerFactory);
};
} // namespace views
diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc
index ff91ea00b53..984f6397f59 100644
--- a/chromium/ui/views/focus/focus_manager_unittest.cc
+++ b/chromium/ui/views/focus/focus_manager_unittest.cc
@@ -781,7 +781,7 @@ TEST_F(FocusManagerTest, StoreFocusedView) {
GetFocusManager()->ClearFocus();
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Test that the correct view is restored if full keyboard access is changed.
TEST_F(FocusManagerTest, StoreFocusedViewFullKeyboardAccess) {
View* view1 = new View;
diff --git a/chromium/ui/views/focus/focus_search.cc b/chromium/ui/views/focus/focus_search.cc
index 1cbf0b156d7..5b2d36d025b 100644
--- a/chromium/ui/views/focus/focus_search.cc
+++ b/chromium/ui/views/focus/focus_search.cc
@@ -16,7 +16,7 @@ namespace views {
FocusSearch::FocusSearch(View* root, bool cycle, bool accessibility_mode)
: root_(root), cycle_(cycle), accessibility_mode_(accessibility_mode) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, only the keyboard accessibility mode defined in FocusManager is
// used. No special accessibility mode should be applicable for a
// FocusTraversable.
diff --git a/chromium/ui/views/focus/focus_search.h b/chromium/ui/views/focus/focus_search.h
index f7dd1d1a6ed..f347f364a1e 100644
--- a/chromium/ui/views/focus/focus_search.h
+++ b/chromium/ui/views/focus/focus_search.h
@@ -6,7 +6,6 @@
#define UI_VIEWS_FOCUS_FOCUS_SEARCH_H_
#include "base/containers/flat_set.h"
-#include "base/macros.h"
#include "ui/views/view.h"
namespace views {
@@ -54,6 +53,9 @@ class VIEWS_EXPORT FocusSearch {
// needed and you want to check IsAccessibilityFocusable(), rather than
// IsFocusable().
FocusSearch(View* root, bool cycle, bool accessibility_mode);
+
+ FocusSearch(const FocusSearch&) = delete;
+ FocusSearch& operator=(const FocusSearch&) = delete;
virtual ~FocusSearch() = default;
// Finds the next view that should be focused and returns it. If a
@@ -149,8 +151,6 @@ class VIEWS_EXPORT FocusSearch {
View* root_;
bool cycle_;
bool accessibility_mode_;
-
- DISALLOW_COPY_AND_ASSIGN(FocusSearch);
};
} // namespace views
diff --git a/chromium/ui/views/focus/focus_traversal_unittest.cc b/chromium/ui/views/focus/focus_traversal_unittest.cc
index c8dcc449f51..9a6ff689974 100644
--- a/chromium/ui/views/focus/focus_traversal_unittest.cc
+++ b/chromium/ui/views/focus/focus_traversal_unittest.cc
@@ -138,6 +138,9 @@ class BorderView : public NativeViewHost {
SetFocusBehavior(FocusBehavior::NEVER);
}
+ BorderView(const BorderView&) = delete;
+ BorderView& operator=(const BorderView&) = delete;
+
virtual internal::RootView* GetContentsRootView() {
return static_cast<internal::RootView*>(widget_->GetRootView());
}
@@ -172,14 +175,14 @@ class BorderView : public NativeViewHost {
private:
View* child_;
std::unique_ptr<Widget> widget_;
-
- DISALLOW_COPY_AND_ASSIGN(BorderView);
};
} // namespace
class FocusTraversalTest : public FocusManagerTest {
public:
+ FocusTraversalTest(const FocusTraversalTest&) = delete;
+ FocusTraversalTest& operator=(const FocusTraversalTest&) = delete;
~FocusTraversalTest() override;
void InitContentView() override;
@@ -226,8 +229,6 @@ class FocusTraversalTest : public FocusManagerTest {
DummyComboboxModel combobox_model_;
PaneView* left_container_;
PaneView* right_container_;
-
- DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest);
};
FocusTraversalTest::FocusTraversalTest() = default;
@@ -371,7 +372,8 @@ void FocusTraversalTest::InitContentView() {
y += label_height + gap_between_labels;
- auto button = MdTextButton::Create(nullptr, ASCIIToUTF16("Click me"));
+ auto button =
+ std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("Click me"));
button->SetBounds(label_x, y + 10, 80, 30);
button->SetID(FRUIT_BUTTON_ID);
left_container_->AddChildView(std::move(button));
@@ -468,18 +470,18 @@ void FocusTraversalTest::InitContentView() {
y = 250;
int width = 60;
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("OK"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("OK"));
button->SetID(OK_BUTTON_ID);
button->SetIsDefault(true);
button->SetBounds(150, y, width, 30);
GetContentsView()->AddChildView(std::move(button));
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("Cancel"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("Cancel"));
button->SetID(CANCEL_BUTTON_ID);
button->SetBounds(220, y, width, 30);
GetContentsView()->AddChildView(std::move(button));
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("Help"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("Help"));
button->SetID(HELP_BUTTON_ID);
button->SetBounds(290, y, width, 30);
GetContentsView()->AddChildView(std::move(button));
@@ -529,7 +531,7 @@ void FocusTraversalTest::InitContentView() {
text_field_ptr->SetBounds(10, 10, 100, 20);
text_field_ptr->SetID(SEARCH_TEXTFIELD_ID);
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("Search"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("Search"));
button->SetBounds(112, 5, 60, 30);
button->SetID(SEARCH_BUTTON_ID);
border_contents->AddChildView(std::move(button));
@@ -553,11 +555,11 @@ void FocusTraversalTest::InitContentView() {
view_contents->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view_contents->SetBackground(CreateSolidBackground(SK_ColorBLUE));
view_contents->SetID(THUMBNAIL_CONTAINER_ID);
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("Star"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("Star"));
button->SetBounds(5, 5, 50, 30);
button->SetID(THUMBNAIL_STAR_ID);
view_contents->AddChildView(std::move(button));
- button = MdTextButton::Create(nullptr, ASCIIToUTF16("SuperStar"));
+ button = std::make_unique<MdTextButton>(nullptr, ASCIIToUTF16("SuperStar"));
button->SetBounds(60, 5, 100, 30);
button->SetID(THUMBNAIL_SUPER_STAR_ID);
view_contents->AddChildView(std::move(button));
@@ -623,7 +625,7 @@ TEST_F(FocusTraversalTest, NormalTraversal) {
AdvanceEntireFocusLoop(kTraversalIDs, true);
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Test focus traversal with full keyboard access off on Mac.
TEST_F(FocusTraversalTest, NormalTraversalMac) {
GetFocusManager()->SetKeyboardAccessible(false);
@@ -679,7 +681,7 @@ TEST_F(FocusTraversalTest, FullKeyboardToggle) {
EXPECT_EQ(THUMBNAIL_CONTAINER_ID,
GetFocusManager()->GetFocusedView()->GetID());
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) {
const int kDisabledIDs[] = {
@@ -812,15 +814,16 @@ TEST_F(FocusTraversalTest, PaneTraversal) {
class FocusTraversalNonFocusableTest : public FocusManagerTest {
public:
+ FocusTraversalNonFocusableTest(const FocusTraversalNonFocusableTest&) =
+ delete;
+ FocusTraversalNonFocusableTest& operator=(
+ const FocusTraversalNonFocusableTest&) = delete;
~FocusTraversalNonFocusableTest() override = default;
void InitContentView() override;
protected:
FocusTraversalNonFocusableTest() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FocusTraversalNonFocusableTest);
};
void FocusTraversalNonFocusableTest::InitContentView() {
diff --git a/chromium/ui/views/focus/widget_focus_manager.h b/chromium/ui/views/focus/widget_focus_manager.h
index e8a860bb798..e460a4bf0dc 100644
--- a/chromium/ui/views/focus/widget_focus_manager.h
+++ b/chromium/ui/views/focus/widget_focus_manager.h
@@ -5,7 +5,6 @@
#ifndef UI_VIEWS_FOCUS_WIDGET_FOCUS_MANAGER_H_
#define UI_VIEWS_FOCUS_WIDGET_FOCUS_MANAGER_H_
-#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "ui/gfx/native_widget_types.h"
@@ -53,13 +52,13 @@ class VIEWS_EXPORT WidgetFocusManager {
friend class base::NoDestructor<WidgetFocusManager>;
WidgetFocusManager();
+ WidgetFocusManager(const WidgetFocusManager&) = delete;
+ WidgetFocusManager& operator=(const WidgetFocusManager&) = delete;
base::ObserverList<WidgetFocusChangeListener>::Unchecked
focus_change_listeners_;
bool enabled_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(WidgetFocusManager);
};
// A basic helper class that is used to disable native focus change
@@ -67,10 +66,12 @@ class VIEWS_EXPORT WidgetFocusManager {
class VIEWS_EXPORT AutoNativeNotificationDisabler {
public:
AutoNativeNotificationDisabler();
- ~AutoNativeNotificationDisabler();
+ AutoNativeNotificationDisabler(const AutoNativeNotificationDisabler&) =
+ delete;
+ AutoNativeNotificationDisabler& operator=(
+ const AutoNativeNotificationDisabler&) = delete;
- private:
- DISALLOW_COPY_AND_ASSIGN(AutoNativeNotificationDisabler);
+ ~AutoNativeNotificationDisabler();
};
} // namespace views
diff --git a/chromium/ui/views/input_event_activation_protector.cc b/chromium/ui/views/input_event_activation_protector.cc
index 887b5e6d962..11676426e1c 100644
--- a/chromium/ui/views/input_event_activation_protector.cc
+++ b/chromium/ui/views/input_event_activation_protector.cc
@@ -21,6 +21,11 @@ bool InputEventActivationProtector::IsPossiblyUnintendedInteraction(
return false;
}
+ // Don't let key repeats close the dialog, they might've been held when the
+ // dialog pops up.
+ if (event.IsKeyEvent() && event.AsKeyEvent()->is_repeat())
+ return true;
+
if (!event.IsMouseEvent() && !event.IsTouchEvent())
return false;
diff --git a/chromium/ui/views/layout/animating_layout_manager.cc b/chromium/ui/views/layout/animating_layout_manager.cc
index d436f28da5e..d07358e2295 100644
--- a/chromium/ui/views/layout/animating_layout_manager.cc
+++ b/chromium/ui/views/layout/animating_layout_manager.cc
@@ -54,6 +54,27 @@ enum class LayoutFadeType {
kContinuingFade
};
+// Makes a copy of the given layout with only visible child views (non-visible
+// children are omitted).
+ProposedLayout WithOnlyVisibleViews(const ProposedLayout layout) {
+ ProposedLayout result;
+ result.host_size = layout.host_size;
+ std::copy_if(
+ layout.child_layouts.begin(), layout.child_layouts.end(),
+ std::back_inserter(result.child_layouts),
+ [](const ChildLayout& child_layout) { return child_layout.visible; });
+ return result;
+}
+
+// Returns true if the two proposed layouts have the same visible views, with
+// the same parameters, in the same order.
+bool HaveSameVisibleViews(const ProposedLayout& l1, const ProposedLayout& l2) {
+ // There is an approach that uses nested loops and dual iterators that is more
+ // efficient than copying, but since this method is only currently called when
+ // views are added to the layout, clarity is more important than speed.
+ return WithOnlyVisibleViews(l1) == WithOnlyVisibleViews(l2);
+}
+
} // namespace
// Holds data about a view that is fading in or out as part of an animation.
@@ -107,6 +128,11 @@ class AnimatingLayoutManager::AnimationDelegate
// invalidating the host to make sure the layout is up to date.
void MakeReadyForAnimation();
+ // Overrides the default animation container with |container|.
+ void SetAnimationContainerForTesting(gfx::AnimationContainer* container) {
+ animation_->SetContainer(container);
+ }
+
private:
// Observer used to watch for the host view being parented to a widget.
class ViewWidgetObserver : public ViewObserver {
@@ -273,6 +299,17 @@ void AnimatingLayoutManager::FadeOut(View* child_view) {
return;
}
+ // This handles a case where we are in the middle of an animation where we
+ // would have hidden the target view, but haven't hit Layout() yet, so haven't
+ // actually hidden it yet. Because we plan fade-outs off of the current layout
+ // if the view the child view is visible it will not get a proper fade-out and
+ // will remain visible but not properly laid out. We remedy this by hiding the
+ // view immediately.
+ const ChildLayout* const current_layout =
+ FindChildViewInLayout(current_layout_, child_view);
+ if ((!current_layout || !current_layout->visible) && child_view->GetVisible())
+ SetViewVisibility(child_view, false);
+
// Indicate that the view should become hidden in the layout without
// immediately changing its visibility. Instead, this triggers an animation
// which results in the view being hidden.
@@ -462,6 +499,24 @@ void AnimatingLayoutManager::OnInstalled(View* host) {
animation_delegate_ = std::make_unique<AnimationDelegate>(this);
}
+bool AnimatingLayoutManager::OnViewAdded(View* host, View* view) {
+ // Handle a case where we add a visible view that shouldn't be visible in the
+ // layout. In this case, there is no animation, no invalidation, and we just
+ // set the view to not be visible.
+ if (view->GetVisible() && cached_layout_size() && !is_animating_) {
+ const gfx::Size target_size = GetAvailableTargetLayoutSize();
+ ProposedLayout proposed_layout =
+ target_layout_manager()->GetProposedLayout(target_size);
+ if (HaveSameVisibleViews(current_layout_, proposed_layout)) {
+ SetViewVisibility(view, false);
+ current_layout_ = target_layout_ = proposed_layout;
+ return false;
+ }
+ }
+
+ return RecalculateTarget();
+}
+
void AnimatingLayoutManager::OnLayoutChanged() {
// This replaces the normal behavior of clearing cached layouts.
RecalculateTarget();
@@ -585,6 +640,7 @@ bool AnimatingLayoutManager::RecalculateTarget() {
// start or update an animation.
const ProposedLayout proposed_layout =
target_layout_manager()->GetProposedLayout(target_size);
+
if (target_layout_ == proposed_layout)
return false;
@@ -605,6 +661,11 @@ bool AnimatingLayoutManager::RecalculateTarget() {
// child views' visibility changing.)
starting_layout_ = current_layout_;
starting_offset_ = current_offset_;
+ } else if (starting_layout_ == target_layout_) {
+ // If we initiated but did not show any frames of an animation, and we are
+ // redirected to our starting layout then just reset the layout.
+ ResetLayoutToSize(target_size);
+ return false;
}
CalculateFadeInfos();
UpdateCurrentLayout(0.0);
diff --git a/chromium/ui/views/layout/animating_layout_manager.h b/chromium/ui/views/layout/animating_layout_manager.h
index 70561ebe5c7..cbc7574d702 100644
--- a/chromium/ui/views/layout/animating_layout_manager.h
+++ b/chromium/ui/views/layout/animating_layout_manager.h
@@ -202,11 +202,20 @@ class VIEWS_EXPORT AnimatingLayoutManager : public LayoutManagerBase {
// widget).
void EnableAnimationForTesting();
+ const ProposedLayout& starting_layout_for_testing() const {
+ return starting_layout_;
+ }
+
+ const ProposedLayout& target_layout_for_testing() const {
+ return target_layout_;
+ }
+
protected:
// LayoutManagerBase:
ProposedLayout CalculateProposedLayout(
const SizeBounds& size_bounds) const override;
void OnInstalled(View* host) override;
+ bool OnViewAdded(View* host, View* view) override;
void OnLayoutChanged() override;
void LayoutImpl() override;
diff --git a/chromium/ui/views/layout/animating_layout_manager_unittest.cc b/chromium/ui/views/layout/animating_layout_manager_unittest.cc
index 8fc53d36838..efb44b184eb 100644
--- a/chromium/ui/views/layout/animating_layout_manager_unittest.cc
+++ b/chromium/ui/views/layout/animating_layout_manager_unittest.cc
@@ -4789,7 +4789,7 @@ TEST_F(AnimatingLayoutManagerRealtimeTest,
// TODO(dfried): figure out why these tests absolutely do not animate properly
// on Mac. Whatever magic makes the compositor animation runner go doesn't seem
// to want to work on Mac in non-browsertests :(
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Test fixture for testing sequences of the following four actions:
// * animating layout manager configured on host view
@@ -4984,6 +4984,6 @@ TEST_F(AnimatingLayoutManagerSequenceTest,
ExpectResetToLayout();
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
} // namespace views
diff --git a/chromium/ui/views/layout/composite_layout_tests.cc b/chromium/ui/views/layout/composite_layout_tests.cc
new file mode 100644
index 00000000000..061d0cc60be
--- /dev/null
+++ b/chromium/ui/views/layout/composite_layout_tests.cc
@@ -0,0 +1,1053 @@
+// Copyright (c) 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 <memory>
+
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/animation/animation_container.h"
+#include "ui/gfx/animation/animation_test_api.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/test/test_views.h"
+#include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
+#include "ui/views/widget/widget.h"
+
+// This test suite simulates the kind of nested layouts we find in e.g. the
+// toolbar without actually pulling in browser code. It's designed to test
+// interactions between multiple levels of Flex and Animating layouts, in a
+// situation that resembles how they are actually used.
+//
+// The test cases are designed to probe edge cases and interactions that are
+// difficult to simulate in either the FlexLayout or AnimatingLayoutManager unit
+// tests. They are not browser tests however, and uses TaskEnvironment and
+// AnimationContainerTestApi to step animations rather than running them in
+// realtime. This makes these tests as quick as unit tests, so they do not incur
+// the costs associated with browser tests.
+//
+// This suite is part of views_unittests.
+
+namespace views {
+
+namespace {
+
+constexpr base::TimeDelta kDefaultAnimationDuration =
+ base::TimeDelta::FromSeconds(1);
+constexpr int kIconDimension = 20;
+constexpr gfx::Size kIconSize(kIconDimension, kIconDimension);
+constexpr int kLabelWidth = 70;
+constexpr gfx::Size kLabelSize(kLabelWidth, kIconDimension);
+constexpr int kBarMinimumWidth = 70;
+constexpr int kBarPreferredWidth = 200;
+constexpr gfx::Size kBarMinimumSize(kBarMinimumWidth, kIconDimension);
+constexpr gfx::Size kBarPreferredSize(kBarPreferredWidth, kIconDimension);
+constexpr gfx::Size kDefaultToolbarSize(400, kIconDimension);
+
+// Base class for elements in the toolbar that animate; a stand-in for e.g.
+// ToolbarIconContainer.
+class SimulatedToolbarElement : public View {
+ public:
+ AnimatingLayoutManager* layout() {
+ return static_cast<AnimatingLayoutManager*>(GetLayoutManager());
+ }
+ const AnimatingLayoutManager* layout() const {
+ return static_cast<const AnimatingLayoutManager*>(GetLayoutManager());
+ }
+
+ protected:
+ SimulatedToolbarElement() {
+ auto* const animating_layout =
+ SetLayoutManager(std::make_unique<AnimatingLayoutManager>());
+ animating_layout
+ ->SetBoundsAnimationMode(
+ AnimatingLayoutManager::BoundsAnimationMode::kAnimateMainAxis)
+ .SetOrientation(LayoutOrientation::kHorizontal)
+ .SetAnimationDuration(kDefaultAnimationDuration);
+ animating_layout->SetTargetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal);
+ }
+
+ void SetAnimationDuration(base::TimeDelta animation_duration) {
+ layout()->SetAnimationDuration(animation_duration);
+ }
+
+ FlexLayout* target_layout() {
+ return static_cast<FlexLayout*>(layout()->target_layout_manager());
+ }
+};
+
+// Simulates an avatar button on the Chrome toolbar, with a fixed-size icon and
+// a label that can animate in and out.
+class SimulatedAvatarButton : public SimulatedToolbarElement {
+ public:
+ SimulatedAvatarButton() {
+ AddChildView(std::make_unique<StaticSizedView>(kIconSize));
+ auto* const status =
+ AddChildView(std::make_unique<StaticSizedView>(kLabelSize));
+ status->SetVisible(false);
+ layout()->SetDefaultFadeMode(
+ AnimatingLayoutManager::FadeInOutMode::kScaleFromZero);
+ }
+
+ ~SimulatedAvatarButton() override = default;
+
+ void FadeLabelIn() {
+ layout()->FadeIn(label());
+ showing_label_ = true;
+ }
+
+ void FadeLabelOut() {
+ layout()->FadeOut(label());
+ showing_label_ = false;
+ }
+
+ // Verifies that the label appears (or does not appear) directly to the right
+ // of the avatar icon, and fills available remaining space in this view. If
+ // the view is not animating, ensures that the label appears (or does not
+ // appear) at the exact size and position it should.
+ void EnsureLayout() const {
+ EXPECT_EQ(gfx::Rect(gfx::Point(), kIconSize), icon()->bounds());
+ if (layout()->is_animating()) {
+ if (label()->GetVisible()) {
+ EXPECT_EQ(0, label()->y());
+ EXPECT_EQ(kIconDimension, label()->height());
+ // TODO(dfried): eliminate potential for rounding error here. Currently
+ // it is possible for the left side of the label to round up a pixel
+ // when it is shrinking down due to how interpolation works, so we need
+ // to account for that in determining if the current state is valid.
+ EXPECT_GE(label()->x(), kIconDimension);
+ EXPECT_LE(label()->x(), kIconDimension + 1);
+ EXPECT_EQ(label()->width(), width() - label()->x());
+ }
+ } else if (showing_label_) {
+ EXPECT_TRUE(label()->GetVisible());
+ EXPECT_EQ(gfx::Rect(gfx::Point(kIconDimension, 0), kLabelSize),
+ label()->bounds());
+ } else {
+ EXPECT_FALSE(label()->GetVisible());
+ }
+ }
+
+ private:
+ // views::View:
+ const char* GetClassName() const override { return "SimulatedAvatarButton"; }
+
+ View* icon() { return children()[0]; }
+ const View* icon() const { return children()[0]; }
+ View* label() { return children()[1]; }
+ const View* label() const { return children()[1]; }
+ bool showing_label_ = false;
+};
+
+// Simulates extensions buttons in the new toolbar extensions view, with a fixed
+// button on the right and buttons to the left that can animate in and out and
+// be hidden if there is insufficient space.
+class SimulatedExtensionsContainer : public SimulatedToolbarElement {
+ public:
+ SimulatedExtensionsContainer() {
+ auto* const main_button =
+ AddChildView(std::make_unique<StaticSizedView>(kIconSize));
+ main_button->SetProperty(kFlexBehaviorKey, FlexSpecification());
+ layout()->SetDefaultFadeMode(
+ AnimatingLayoutManager::FadeInOutMode::kSlideFromTrailingEdge);
+ target_layout()
+ ->SetFlexAllocationOrder(FlexAllocationOrder::kReverse)
+ .SetDefault(
+ kFlexBehaviorKey,
+ FlexSpecification(LayoutOrientation::kHorizontal,
+ MinimumFlexSizeRule::kPreferredSnapToZero));
+ }
+
+ ~SimulatedExtensionsContainer() override = default;
+
+ void AddIcons(std::vector<bool> visibility) {
+ int insertion_point = children().size() - 1;
+ for (bool visible : visibility)
+ AddIconAt(insertion_point++, visible);
+ }
+
+ void AddIconAt(int position, bool initially_visible) {
+ DCHECK_GE(position, 0);
+ DCHECK_LT(position, int{children().size()});
+ auto new_child = std::make_unique<StaticSizedView>(kIconSize);
+ new_child->SetVisible(initially_visible);
+ if (initially_visible)
+ visible_views_.insert(new_child.get());
+ AddChildViewAt(std::move(new_child), position);
+ }
+
+ void RemoveIconAt(int position) {
+ DCHECK_GE(position, 0);
+ DCHECK_LT(position, int{children().size()} - 1);
+ visible_views_.erase(RemoveChildViewT<View>(children()[position]).get());
+ }
+
+ void SetIconVisibility(int position, bool visible) {
+ DCHECK_GE(position, 0);
+ DCHECK_LT(position, int{children().size()} - 1);
+ auto* const button = children()[position];
+ if (visible) {
+ layout()->FadeIn(button);
+ visible_views_.insert(button);
+ } else {
+ layout()->FadeOut(button);
+ visible_views_.erase(button);
+ }
+ }
+
+ void MoveIcon(int from, int to) {
+ DCHECK_GE(from, 0);
+ DCHECK_GE(to, 0);
+ DCHECK_NE(from, to);
+ DCHECK_LT(from, int{children().size()} - 1);
+ DCHECK_LT(to, int{children().size()} - 1);
+ ReorderChildView(children()[from], to);
+ }
+
+ // Ensures that the extension icons appear with the size and placement and
+ // visibility expected, and that the final "extensions menu" button always
+ // appears and is flush with the right edge of the view:
+ //
+ // |[pinned][pinned][pinned][menu]| (unpinned extensions not visible)
+ //
+ // While animating, icons need only be the correct size and in the correct
+ // region of the view, and visible if they are going to be visible in the
+ // final layout (icons which are fading out may also be visible).
+ //
+ // If |expected_num_icons| is specified:
+ // - while animating, serves as a lower bound on the number of icons displayed
+ // - while not animating, must match the number of visible icons exactly
+ void EnsureLayout(base::Optional<int> expected_num_icons) const {
+ if (layout()->is_animating()) {
+ // For animating layouts, we ensure that icons are the correct size and
+ // appear between the left edge of the container and exactly overlapping
+ // the "extensions menu" icon (the final icon in the container).
+ const int available_width = width() - kIconDimension;
+ DCHECK_GE(available_width, 0);
+ int num_visible = 0;
+ for (int i = 0; i < int{children().size()} - 1; ++i) {
+ const View* const child = children()[i];
+ if (child->GetVisible()) {
+ ++num_visible;
+ EXPECT_GE(child->x(), 0) << " icon " << i;
+ EXPECT_LE(child->x(), available_width) << " icon " << i;
+ EXPECT_EQ(kIconSize, child->size()) << " icon " << i;
+ }
+ }
+ if (expected_num_icons.has_value())
+ EXPECT_GE(num_visible, expected_num_icons.value());
+ } else {
+ // Calculate how many icons *should* be visible given the available space.
+ SizeBounds available_size = parent()->GetAvailableSize(this);
+ int num_visible = visible_views_.size();
+ if (available_size.width().has_value()) {
+ num_visible = std::min(
+ num_visible,
+ (available_size.width().value() - kIconDimension) / kIconDimension);
+ }
+ DCHECK_LT(num_visible, int{children().size()});
+ if (expected_num_icons.has_value())
+ EXPECT_EQ(expected_num_icons.value(), num_visible);
+ // Verify that the correct icons are visible and are in the correct place
+ // with the correct size.
+ int x = 0;
+ for (int i = 0; i < int{children().size()} - 1; ++i) {
+ const View* const child = children()[i];
+ if (base::Contains(visible_views_, child)) {
+ if (num_visible > 0) {
+ --num_visible;
+ EXPECT_TRUE(child->GetVisible()) << " icon " << i;
+ EXPECT_EQ(gfx::Rect(gfx::Point(x, 0), kIconSize), child->bounds())
+ << " icon " << i;
+ x += kIconDimension;
+ } else {
+ // This is a pinned extension that overflowed the available space
+ // and therefore should be hidden.
+ EXPECT_FALSE(child->GetVisible())
+ << " icon " << i
+ << " should have been hidden; available size is "
+ << available_size.ToString();
+ }
+ } else {
+ // This icon is explicitly hidden.
+ EXPECT_FALSE(child->GetVisible()) << " icon " << i;
+ }
+ }
+ }
+ EXPECT_TRUE(main_button()->GetVisible());
+ EXPECT_EQ(kIconSize, main_button()->size());
+ EXPECT_EQ(width(), main_button()->bounds().right());
+ }
+
+ private:
+ // views::View:
+ const char* GetClassName() const override {
+ return "SimulatedExtensionsContainer";
+ }
+
+ const View* main_button() const { return children()[children().size() - 1]; }
+
+ std::set<const View*> visible_views_;
+};
+
+// Simulates a toolbar with buttons on either side, a "location bar", and mock
+// versions of the extensions container and avatar button.
+class SimulatedToolbar : public View {
+ public:
+ SimulatedToolbar() {
+ AddChildView(std::make_unique<StaticSizedView>(kIconSize));
+ auto* const bar =
+ AddChildView(std::make_unique<StaticSizedView>(kBarPreferredSize));
+ bar->set_minimum_size(kBarMinimumSize);
+ extensions_ =
+ AddChildView(std::make_unique<SimulatedExtensionsContainer>());
+ avatar_ = AddChildView(std::make_unique<SimulatedAvatarButton>());
+ AddChildView(std::make_unique<StaticSizedView>(kIconSize));
+
+ SetLayoutManager(std::make_unique<FlexLayout>())
+ ->SetOrientation(LayoutOrientation::kHorizontal);
+ avatar_->SetProperty(
+ kFlexBehaviorKey,
+ FlexSpecification(avatar_->layout()->GetDefaultFlexRule())
+ .WithOrder(1));
+ bar->SetProperty(kFlexBehaviorKey,
+ FlexSpecification(LayoutOrientation::kHorizontal,
+ MinimumFlexSizeRule::kScaleToMinimum,
+ MaximumFlexSizeRule::kUnbounded)
+ .WithOrder(2));
+ extensions_->SetProperty(
+ kFlexBehaviorKey,
+ FlexSpecification(extensions_->layout()->GetDefaultFlexRule())
+ .WithOrder(3));
+ }
+
+ SimulatedExtensionsContainer* extensions() { return extensions_; }
+ const SimulatedExtensionsContainer* extensions() const { return extensions_; }
+ SimulatedAvatarButton* avatar() { return avatar_; }
+ const SimulatedAvatarButton* avatar() const { return avatar_; }
+ View* location() { return children()[1]; }
+ const View* location() const { return children()[1]; }
+
+ // Ensures the layout of the toolbar which contains:
+ // - one dummy back/forward/home type button of fixed size
+ // - a location bar with flexible width
+ // - a simulated extensions container
+ // - a simulated avatar button
+ // - one dummy "wrench menu" type button of fixed size
+ //
+ // All child views must be laid out end-to-end adjacent to each other, in the
+ // right order, and at the correct size. Furthermore, EnsureLayout() is called
+ // on the child views that support it.
+ //
+ // The parameter |expected_num_extension_icons| is passed to
+ // SimulatedExtensionsContainer::EnsureLayout().
+ void EnsureLayout(base::Optional<int> expected_num_extension_icons) const {
+ EXPECT_EQ(kIconDimension, height());
+ EXPECT_EQ(gfx::Rect(gfx::Point(), kIconSize), children()[0]->bounds());
+ EXPECT_EQ(gfx::Point(kIconDimension, 0), location()->origin());
+ EXPECT_EQ(kIconDimension, location()->height());
+ EXPECT_GE(location()->width(), kBarMinimumWidth);
+ EXPECT_EQ(gfx::Point(location()->bounds().right(), 0),
+ extensions_->origin());
+ extensions_->EnsureLayout(expected_num_extension_icons);
+ EXPECT_EQ(gfx::Point(extensions_->bounds().right(), 0), avatar_->origin());
+ avatar_->EnsureLayout();
+ EXPECT_EQ(gfx::Rect(gfx::Point(avatar_->bounds().right(), 0), kIconSize),
+ children()[4]->bounds());
+ if (location()->width() == kBarMinimumWidth)
+ EXPECT_LE(width(), children()[4]->bounds().right());
+ else
+ EXPECT_EQ(width(), children()[4]->bounds().right());
+ }
+
+ private:
+ // views::View:
+ const char* GetClassName() const override { return "SimulatedToolbar"; }
+
+ SimulatedExtensionsContainer* extensions_;
+ SimulatedAvatarButton* avatar_;
+};
+
+} // anonymous namespace
+
+// Test suite. Sets up the mock toolbar and ties animation of all child elements
+// together so they can be controlled for testing. Use the utility methods here
+// as much as possible rather than calling methods on the individual Views.
+class CompositeLayoutTest : public testing::Test {
+ public:
+ void SetUp() override {
+ toolbar_ = std::make_unique<SimulatedToolbar>();
+ toolbar_->SetSize(kDefaultToolbarSize);
+ extensions_test_api_ = std::make_unique<gfx::AnimationContainerTestApi>(
+ extensions()->layout()->GetAnimationContainerForTesting());
+ avatar_test_api_ = std::make_unique<gfx::AnimationContainerTestApi>(
+ avatar()->layout()->GetAnimationContainerForTesting());
+ }
+
+ SimulatedAvatarButton* avatar() { return toolbar_->avatar(); }
+ const SimulatedAvatarButton* avatar() const { return toolbar_->avatar(); }
+ SimulatedExtensionsContainer* extensions() { return toolbar_->extensions(); }
+ const SimulatedExtensionsContainer* extensions() const {
+ return toolbar_->extensions();
+ }
+ SimulatedToolbar* toolbar() { return toolbar_.get(); }
+ const SimulatedToolbar* toolbar() const { return toolbar_.get(); }
+
+ void SetWidth(int width) {
+ toolbar()->SetSize(gfx::Size(width, kIconDimension));
+ }
+
+ void ChangeWidth(int delta) {
+ toolbar()->SetSize(
+ gfx::Size(std::max(0, toolbar()->width() + delta), kIconDimension));
+ }
+
+ void AdvanceAnimations(int ms) {
+ const auto delta = base::TimeDelta::FromMilliseconds(ms);
+ if (avatar()->layout()->is_animating())
+ avatar_test_api_->IncrementTime(delta);
+ if (extensions()->layout()->is_animating())
+ extensions_test_api_->IncrementTime(delta);
+ toolbar_->Layout();
+ }
+
+ void ResetAnimation() {
+ avatar()->layout()->ResetLayout();
+ extensions()->layout()->ResetLayout();
+ toolbar()->Layout();
+ }
+
+ bool IsAnimating() const {
+ return avatar()->layout()->is_animating() ||
+ extensions()->layout()->is_animating();
+ }
+
+ void FinishAnimations() {
+ // Advance the animation an unreasonable amount of times and fail if that
+ // doesn't actually cause the animation to complete. It's possible that one
+ // animation could lead to another so basing our limit only on the current
+ // animation durations is not necessarily reliable.
+ for (int i = 0; i < 100; ++i) {
+ if (!IsAnimating())
+ return;
+ // Advance by a small but significant step (1/10 of a second).
+ AdvanceAnimations(100);
+ }
+ GTEST_FAIL()
+ << "Animations did not complete in a reasonable amount of time.";
+ }
+
+ // Ensures the toolbar layout and all child view layouts are as expected.
+ // If |expected_num_extension_icons| is specified, then exactly that many (or
+ // at minimum that many if animating) simulated extension icons must be
+ // visible.
+ void EnsureLayout(
+ base::Optional<int> expected_num_extension_icons = base::nullopt) {
+ toolbar_->EnsureLayout(expected_num_extension_icons);
+ }
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ std::unique_ptr<gfx::AnimationContainerTestApi> extensions_test_api_;
+ std::unique_ptr<gfx::AnimationContainerTestApi> avatar_test_api_;
+ std::unique_ptr<SimulatedToolbar> toolbar_;
+};
+
+// ------------
+// Basic tests.
+
+TEST_F(CompositeLayoutTest, InitialLayout) {
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, SmallResize) {
+ toolbar()->SetSize(gfx::Size(300, kIconDimension));
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ShrinkLocationBar) {
+ toolbar()->SetSize(
+ gfx::Size(4 * kIconDimension + kBarMinimumWidth, kIconDimension));
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ShrinkLocationBarTooSmall) {
+ toolbar()->SetSize(
+ gfx::Size(4 * kIconDimension + kBarMinimumWidth - 20, kIconDimension));
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ProfileAnimates) {
+ avatar()->FadeLabelIn();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ avatar()->FadeLabelOut();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ProfileAnimationInterrupted) {
+ avatar()->FadeLabelIn();
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(500);
+ EnsureLayout();
+ avatar()->FadeLabelOut();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ProfileAnimationInterruptedImmediately) {
+ avatar()->FadeLabelIn();
+ avatar()->FadeLabelOut();
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+}
+
+// ----------------------------------------------------------
+// Tests which add/remove extension icons from the container.
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnAdd) {
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ extensions()->AddIconAt(2, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnAddMultiple) {
+ extensions()->AddIconAt(0, true);
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->AddIconAt(1, true);
+ extensions()->AddIconAt(3, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnAddMultipleStaggered) {
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(200);
+ EnsureLayout();
+ EXPECT_TRUE(IsAnimating());
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->AddIconAt(1, true);
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(200);
+ EnsureLayout();
+ EXPECT_TRUE(IsAnimating());
+ extensions()->AddIconAt(3, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionRemovedDuringAnimation) {
+ extensions()->AddIconAt(0, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->AddIconAt(0, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->AddIconAt(2, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->RemoveIconAt(1);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->AddIconAt(0, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->RemoveIconAt(2);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->RemoveIconAt(0);
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionRemovedImmediately) {
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->AddIconAt(1, true);
+ extensions()->RemoveIconAt(1);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionRemovedImmediatelyDuringAnimation) {
+ extensions()->AddIconAt(0, true);
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(500);
+ EnsureLayout();
+ extensions()->AddIconAt(1, true);
+ extensions()->RemoveIconAt(1);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+// -----------------------------------------------
+// Tests which show/hide existing extension icons.
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnShow) {
+ extensions()->AddIcons({false, true, false});
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ extensions()->AddIconAt(2, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnShowMultiple) {
+ extensions()->AddIcons({true, false, true, false});
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, true);
+ extensions()->SetIconVisibility(3, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsAnimateOnShowMultipleStaggered) {
+ extensions()->AddIcons({false, false, true, false});
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(200);
+ EnsureLayout();
+ EXPECT_TRUE(IsAnimating());
+ extensions()->SetIconVisibility(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, true);
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(200);
+ EnsureLayout();
+ EXPECT_TRUE(IsAnimating());
+ extensions()->SetIconVisibility(3, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionHiddenDuringAnimation) {
+ extensions()->AddIcons({false, false, true, false});
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(3, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(2, false);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(0, true);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(3, false);
+ AdvanceAnimations(200);
+ EnsureLayout();
+ extensions()->SetIconVisibility(0, false);
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionHiddenImmediately) {
+ extensions()->AddIcons({true, false});
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, true);
+ extensions()->SetIconVisibility(1, false);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionHiddenImmediatelyDuringAnimation) {
+ extensions()->AddIcons({true, false});
+ EXPECT_TRUE(IsAnimating());
+ AdvanceAnimations(500);
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, true);
+ extensions()->SetIconVisibility(1, false);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+// ----------------------------------
+// Tests where child views are moved.
+
+TEST_F(CompositeLayoutTest, ExtensionOrderChanged) {
+ extensions()->AddIcons({true, true, true});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->MoveIcon(1, 2);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionHiddenAndPoppedOutImmediate) {
+ extensions()->AddIcons({true, true, true});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, false);
+ extensions()->MoveIcon(1, 2);
+ extensions()->SetIconVisibility(2, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionHiddenAndPoppedOutDelayed) {
+ extensions()->AddIcons({true, true, true});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->SetIconVisibility(1, false);
+ extensions()->MoveIcon(1, 2);
+ AdvanceAnimations(500);
+ extensions()->SetIconVisibility(2, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionPinned) {
+ extensions()->AddIcons({true, true, false, false});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->MoveIcon(3, 0);
+ extensions()->SetIconVisibility(0, true);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, VisibleExtensionMoved) {
+ extensions()->AddIcons({true, true, false, false});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->MoveIcon(1, 0);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, InvisibleExtensionMoved) {
+ extensions()->AddIcons({true, true, false, false});
+ FinishAnimations();
+ EnsureLayout();
+ extensions()->MoveIcon(2, 3);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+}
+
+// -----------------------------------------------
+// Tests combining two different animating views.
+
+TEST_F(CompositeLayoutTest, ExtensionsAndAvatarAnimateSimultaneously) {
+ // Expand both views.
+ extensions()->AddIcons({true, true});
+ avatar()->FadeLabelIn();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+ // Expand one, contract the other.
+ extensions()->AddIcons({true});
+ avatar()->FadeLabelOut();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout();
+ EXPECT_FALSE(IsAnimating());
+ // Then vice-versa.
+ extensions()->SetIconVisibility(1, false);
+ avatar()->FadeLabelIn();
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout();
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsAndAvatarAnimateStaggered) {
+ // Expand both views.
+ extensions()->AddIcons({true, true});
+ AdvanceAnimations(500);
+ EnsureLayout();
+ avatar()->FadeLabelIn();
+ FinishAnimations();
+ EnsureLayout();
+ // Expand one, contract the other.
+ extensions()->AddIcons({true});
+ AdvanceAnimations(500);
+ EnsureLayout();
+ avatar()->FadeLabelOut();
+ FinishAnimations();
+ EnsureLayout();
+ // Then vice-versa.
+ extensions()->SetIconVisibility(1, false);
+ AdvanceAnimations(500);
+ avatar()->FadeLabelIn();
+ FinishAnimations();
+ EnsureLayout();
+}
+
+// -----------------------
+// Tests in limited space.
+
+TEST_F(CompositeLayoutTest, ExtensionsNotShownWhenSpaceConstrained) {
+ // At the minimum size for the toolbar, no icons should be displayed.
+ toolbar()->SizeToPreferredSize();
+ extensions()->AddIcons({true, true});
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(0);
+
+ // Increase the size gradually, exposing pinned icons.
+ // We don't really care if the individual icons animate out or not; as long as
+ // the correct number are displayed at each step.
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(0);
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout(1);
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(1);
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout(2);
+}
+
+TEST_F(CompositeLayoutTest, SomeExtensionsNotShownWhenSpaceConstrained) {
+ // Provide room for one of two icons.
+ SetWidth(toolbar()->GetPreferredSize().width() + kIconDimension);
+ extensions()->AddIcons({true, true});
+ FinishAnimations();
+ EnsureLayout(1);
+
+ // Increase the size gradually, exposing pinned icons.
+ // We don't really care if the individual icons animate out or not; as long as
+ // the correct number are displayed at each step.
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(1);
+ ChangeWidth(kIconDimension / 2);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout(2);
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsShownSnapsWhenSpaceShrinks) {
+ // Provide room for both icons.
+ SetWidth(toolbar()->GetPreferredSize().width() + 2 * kIconDimension);
+ extensions()->AddIcons({true, true});
+ FinishAnimations();
+ EnsureLayout(2);
+
+ ChangeWidth(-kIconDimension);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(1);
+
+ ChangeWidth(-kIconDimension);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(0);
+}
+
+TEST_F(CompositeLayoutTest,
+ ExtensionsShowingAnimationRedirectsDueToSmallerAvailableSpace) {
+ // Provide room for both icons.
+ SetWidth(toolbar()->GetPreferredSize().width() + 2 * kIconDimension);
+ extensions()->AddIcons({true, true});
+ AdvanceAnimations(400);
+
+ // The icons are fading in, but not enough that the animation would be reset
+ // by changing the toolbar width by one icon width.
+ ChangeWidth(-kIconDimension);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout(1);
+}
+
+TEST_F(CompositeLayoutTest,
+ ExtensionsShowingAnimationCancelsDueToSmallerAvailableSpace) {
+ // Provide room for both icons.
+ SetWidth(toolbar()->GetPreferredSize().width() + 2 * kIconDimension);
+ extensions()->AddIcons({true, true});
+ AdvanceAnimations(800);
+
+ // The icons are fading in, far enough that the animation is reset by changing
+ // the toolbar width by one icon width.
+ ChangeWidth(-kIconDimension);
+ EXPECT_FALSE(IsAnimating());
+ EnsureLayout(1);
+}
+
+TEST_F(CompositeLayoutTest,
+ ExtensionsShowingAnimationRedirectsDueToLargerAvailableSpace) {
+ // Provide room for one of two icons.
+ SetWidth(toolbar()->GetPreferredSize().width() + kIconDimension);
+ extensions()->AddIcons({true, true});
+ AdvanceAnimations(400);
+
+ // Make room for the second icon; the animation should continue.
+ ChangeWidth(kIconDimension);
+ EXPECT_TRUE(IsAnimating());
+ FinishAnimations();
+ EnsureLayout(2);
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsHiddenWhenAvatarExpands) {
+ extensions()->AddIcons({true, true, true, true, true});
+ ResetAnimation();
+ EnsureLayout(5);
+ toolbar()->SizeToPreferredSize();
+ EXPECT_FALSE(IsAnimating());
+
+ avatar()->FadeLabelIn();
+
+ // Halfway through, the label will have displaced 35 pixels, or two icons.
+ AdvanceAnimations(500);
+ EnsureLayout(3);
+
+ // At its largest, 70 pixels and four icons.
+ FinishAnimations();
+ EnsureLayout(1);
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsShownWhenAvatarCollapses) {
+ extensions()->AddIcons({true});
+ avatar()->FadeLabelIn();
+ ResetAnimation();
+ toolbar()->SizeToPreferredSize();
+ // These should all be hidden.
+ extensions()->AddIcons({true, true, true, true});
+ EnsureLayout(1);
+
+ avatar()->FadeLabelOut();
+
+ // Halfway through, the label will cede back 35 pixels - enough to display an
+ // additional icon.
+ AdvanceAnimations(500);
+ EnsureLayout(2);
+
+ // Finish everything - this will include icons revealed at the very end. Since
+ // 70 pixels total are ceded back, three of the four newly-added icons can be
+ // shown.
+ FinishAnimations();
+ EnsureLayout(4);
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsHideAndShowWhenAvatarAnimates) {
+ extensions()->AddIcons({true, true, true, true, true});
+ ResetAnimation();
+ EnsureLayout(5);
+ toolbar()->SizeToPreferredSize();
+ EXPECT_FALSE(IsAnimating());
+
+ avatar()->FadeLabelIn();
+
+ // Halfway through, the label will have displaced 35 pixels, or two icons.
+ AdvanceAnimations(500);
+ EnsureLayout(3);
+
+ // Interrupt most of the way through.
+ AdvanceAnimations(200);
+ EnsureLayout(2);
+
+ // Fade the label out and make sure all of the extensions reappeared.
+ avatar()->FadeLabelOut();
+ FinishAnimations();
+ EnsureLayout(5);
+}
+
+TEST_F(CompositeLayoutTest, ExtensionsShowAndHideWhenAvatarAnimates) {
+ extensions()->AddIcons({true});
+ avatar()->FadeLabelIn();
+ ResetAnimation();
+ toolbar()->SizeToPreferredSize();
+ // These should all be hidden.
+ extensions()->AddIcons({true, true, true, true});
+ ResetAnimation();
+
+ // Halfway through, the label will have ceded 35 pixels, or one icon.
+ avatar()->FadeLabelOut();
+ AdvanceAnimations(500);
+ EnsureLayout(2);
+
+ // Interrupt most of the way through.
+ AdvanceAnimations(200);
+ EnsureLayout(3);
+
+ // Fade the label back in and make sure all of the extensions re-hide.
+ avatar()->FadeLabelIn();
+ FinishAnimations();
+ EnsureLayout(1);
+}
+
+TEST_F(CompositeLayoutTest, MultipleAnimationAndLayoutChanges) {
+ extensions()->AddIcons({true});
+ avatar()->FadeLabelIn();
+ ResetAnimation();
+ toolbar()->SizeToPreferredSize();
+ // These should all be hidden.
+ extensions()->AddIcons({true, true, true});
+ ResetAnimation();
+
+ // Halfway through, the label will have ceded 35 pixels, or one icon.
+ avatar()->FadeLabelOut();
+ AdvanceAnimations(500);
+ EnsureLayout(2);
+
+ // Interrupt most of the way through to add a random icon.
+ AdvanceAnimations(100);
+ extensions()->AddIconAt(2, true);
+ AdvanceAnimations(100);
+ EnsureLayout(3);
+
+ extensions()->SetIconVisibility(1, false);
+ extensions()->SetIconVisibility(3, false);
+ FinishAnimations();
+ EnsureLayout(3);
+
+ // Fade the label back in and make sure all of the extensions re-hide.
+ avatar()->FadeLabelIn();
+ FinishAnimations();
+ EnsureLayout(1);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/layout/flex_layout.cc b/chromium/ui/views/layout/flex_layout.cc
index 18131f8c984..bd4e32e0e6f 100644
--- a/chromium/ui/views/layout/flex_layout.cc
+++ b/chromium/ui/views/layout/flex_layout.cc
@@ -835,7 +835,7 @@ void FlexLayout::AllocateFlexSpace(
if (flex_weight > 0) {
// Round up so we give slightly greater weight to earlier views.
flex_amount =
- gfx::ToCeiledInt(remaining * flex_weight / float{flex_total});
+ base::ClampCeil(remaining * flex_weight / float{flex_total});
}
flex_total -= flex_weight;
diff --git a/chromium/ui/views/layout/grid_layout.cc b/chromium/ui/views/layout/grid_layout.cc
index 58004fb9ee0..1f5bb2d12b6 100644
--- a/chromium/ui/views/layout/grid_layout.cc
+++ b/chromium/ui/views/layout/grid_layout.cc
@@ -342,7 +342,7 @@ struct ViewState {
const bool pref_height_fixed;
// The preferred size, only set during the preferred size pass
- // (SizeCalculationType::PREFERRED).
+ // (SizeCalculationType::kPreferred).
gfx::Size pref_size;
// The width/height. This is one of possible three values:
@@ -588,17 +588,17 @@ void ColumnSet::ResetColumnXCoordinates() {
void ColumnSet::CalculateSize(SizeCalculationType type) {
#if DCHECK_IS_ON()
- // SizeCalculationType::MINIMUM must be preceeded by a request for
- // SizeCalculationType::PREFERRED.
- DCHECK(type == SizeCalculationType::PREFERRED ||
- last_calculation_type_ == PREFERRED);
+ // SizeCalculationType::kMinimum must be preceded by a request for
+ // SizeCalculationType::kPreferred.
+ DCHECK(type == SizeCalculationType::kPreferred ||
+ last_calculation_type_ == SizeCalculationType::kPreferred);
last_calculation_type_ = type;
#endif
// Reset the size and remaining sizes.
for (auto* view_state : view_states_) {
if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) {
gfx::Size size;
- if (type == SizeCalculationType::MINIMUM && CanUseMinimum(*view_state)) {
+ if (type == SizeCalculationType::kMinimum && CanUseMinimum(*view_state)) {
// If the min size is bigger than the preferred, use the preferred.
// This relies on MINIMUM being calculated immediately after PREFERRED,
// which the rest of this code relies on as well.
@@ -676,7 +676,7 @@ void ColumnSet::ResizeUsingMin(int total_delta) {
preferred_column_sizes[i] = columns_[i]->Size();
// Recalculate the sizes using the min.
- CalculateSize(ColumnSet::SizeCalculationType::MINIMUM);
+ CalculateSize(ColumnSet::SizeCalculationType::kMinimum);
// Build up the set of columns that can be shrunk in |resize_data|, this
// iteration also resets the size of the column back to the preferred size.
@@ -963,7 +963,7 @@ void GridLayout::SizeRowsAndColumns(bool layout,
// preferred heights are derived from their width, as such we need to
// calculate the size of the columns first.
for (const auto& column_set : column_sets_) {
- column_set->CalculateSize(ColumnSet::SizeCalculationType::PREFERRED);
+ column_set->CalculateSize(ColumnSet::SizeCalculationType::kPreferred);
pref->set_width(std::max(pref->width(), column_set->LayoutWidth()));
}
const gfx::Insets& insets = host_->GetInsets();
diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h
index 9c6a82cd127..073b313a5b4 100644
--- a/chromium/ui/views/layout/grid_layout.h
+++ b/chromium/ui/views/layout/grid_layout.h
@@ -392,9 +392,9 @@ class VIEWS_EXPORT ColumnSet {
// NOTE: this doesn't include the insets.
void ResetColumnXCoordinates();
- enum SizeCalculationType {
- PREFERRED,
- MINIMUM,
+ enum class SizeCalculationType {
+ kPreferred,
+ kMinimum,
};
// Calculate the preferred width of each view in this column set, as well
@@ -432,7 +432,7 @@ class VIEWS_EXPORT ColumnSet {
std::vector<Column*> master_columns_;
#if DCHECK_IS_ON()
- SizeCalculationType last_calculation_type_ = SizeCalculationType::PREFERRED;
+ SizeCalculationType last_calculation_type_ = SizeCalculationType::kPreferred;
#endif
DISALLOW_COPY_AND_ASSIGN(ColumnSet);
diff --git a/chromium/ui/views/layout/layout_manager_base.cc b/chromium/ui/views/layout/layout_manager_base.cc
index 5bc27081215..cdab637584a 100644
--- a/chromium/ui/views/layout/layout_manager_base.cc
+++ b/chromium/ui/views/layout/layout_manager_base.cc
@@ -175,6 +175,8 @@ void LayoutManagerBase::ApplyLayout(const ProposedLayout& layout) {
// events that wouldn't do anything useful).
if (new_available_size != cached_available_size_ || child_layout.visible ||
!child_layout.bounds.IsEmpty()) {
+ const bool size_changed =
+ child_view->bounds().size() != child_layout.bounds.size();
if (child_view->bounds() != child_layout.bounds)
child_view->SetBoundsRect(child_layout.bounds);
// Child layouts which are not invalid will not be laid out by the default
@@ -182,7 +184,7 @@ void LayoutManagerBase::ApplyLayout(const ProposedLayout& layout) {
// constraint it's important that the child view be laid out. So we'll do
// it here.
// TODO(dfried): figure out a better way to handle this.
- else if (child_layout.available_size != SizeBounds())
+ if (!size_changed && child_layout.available_size != SizeBounds())
child_view->Layout();
}
}
diff --git a/chromium/ui/views/linux_ui/linux_ui.cc b/chromium/ui/views/linux_ui/linux_ui.cc
index 585fea624f8..51459bd6cb5 100644
--- a/chromium/ui/views/linux_ui/linux_ui.cc
+++ b/chromium/ui/views/linux_ui/linux_ui.cc
@@ -5,6 +5,7 @@
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/base/ime/linux/linux_input_method_context_factory.h"
+#include "ui/base/ui_base_features.h"
#include "ui/gfx/skia_font_delegate.h"
#include "ui/shell_dialogs/shell_dialog_linux.h"
@@ -19,12 +20,11 @@ namespace views {
void LinuxUI::SetInstance(LinuxUI* instance) {
delete g_linux_ui;
g_linux_ui = instance;
-// Do not set IME instance for ozone as we delegate creating the input method to
-// OzonePlatforms instead. If this is set, OzonePlatform never sets a context
-// factory.
-#if !defined(USE_OZONE)
- LinuxInputMethodContextFactory::SetInstance(instance);
-#endif
+ // Do not set IME instance for ozone as we delegate creating the input method
+ // to OzonePlatforms instead. If this is set, OzonePlatform never sets a
+ // context factory.
+ if (!features::IsUsingOzonePlatform())
+ LinuxInputMethodContextFactory::SetInstance(instance);
SkiaFontDelegate::SetInstance(instance);
ShellDialogLinux::SetInstance(instance);
ui::SetTextEditKeyBindingsDelegate(instance);
diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h
index 7641a91abf0..ebcc4bea2ce 100644
--- a/chromium/ui/views/linux_ui/linux_ui.h
+++ b/chromium/ui/views/linux_ui/linux_ui.h
@@ -9,7 +9,6 @@
#include <string>
#include "base/callback.h"
-#include "base/macros.h"
#include "build/buildflag.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/cursor/cursor_theme_manager.h"
@@ -78,6 +77,8 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
kRightClick,
};
+ LinuxUI(const LinuxUI&) = delete;
+ LinuxUI& operator=(const LinuxUI&) = delete;
~LinuxUI() override;
// Sets the dynamically loaded singleton that draws the desktop native UI.
@@ -178,9 +179,6 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
protected:
LinuxUI();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LinuxUI);
};
} // namespace views
diff --git a/chromium/ui/views/linux_ui/window_button_order_provider.cc b/chromium/ui/views/linux_ui/window_button_order_provider.cc
index be230eabcd5..520e128a267 100644
--- a/chromium/ui/views/linux_ui/window_button_order_provider.cc
+++ b/chromium/ui/views/linux_ui/window_button_order_provider.cc
@@ -4,7 +4,6 @@
#include "ui/views/window/window_button_order_provider.h"
-#include "base/macros.h"
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/views/linux_ui/window_button_order_observer.h"
@@ -16,15 +15,17 @@ class WindowButtonOrderObserverDelegate : public WindowButtonOrderProvider,
public WindowButtonOrderObserver {
public:
WindowButtonOrderObserverDelegate();
+
+ WindowButtonOrderObserverDelegate(const WindowButtonOrderObserverDelegate&) =
+ delete;
+ WindowButtonOrderObserverDelegate& operator=(
+ const WindowButtonOrderObserverDelegate&) = delete;
~WindowButtonOrderObserverDelegate() override;
// WindowButtonOrderObserver:
void OnWindowButtonOrderingChange(
const std::vector<views::FrameButton>& leading_buttons,
const std::vector<views::FrameButton>& trailing_buttons) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(WindowButtonOrderObserverDelegate);
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/ui/views/metadata/metadata_unittest.cc b/chromium/ui/views/metadata/metadata_unittest.cc
index d76a428d8e3..7dca9369f5a 100644
--- a/chromium/ui/views/metadata/metadata_unittest.cc
+++ b/chromium/ui/views/metadata/metadata_unittest.cc
@@ -56,7 +56,7 @@ class MetadataTestBaseView : public views::View {
}
int GetIntProperty() const { return int_property_; }
views::PropertyChangedSubscription AddIntPropertyChangedCallback(
- views::PropertyChangedCallback callback) {
+ views::PropertyChangedCallback callback) WARN_UNUSED_RESULT {
return AddPropertyChangedCallback(&int_property_, std::move(callback));
}
@@ -86,7 +86,7 @@ class MetadataTestView : public MetadataTestBaseView {
}
float GetFloatProperty() const { return float_property_; }
views::PropertyChangedSubscription AddFloatPropertyChangedCallback(
- views::PropertyChangedCallback callback) {
+ views::PropertyChangedCallback callback) WARN_UNUSED_RESULT {
return AddPropertyChangedCallback(&float_property_, std::move(callback));
}
diff --git a/chromium/ui/views/metadata/type_conversion.cc b/chromium/ui/views/metadata/type_conversion.cc
index d953161eff3..1a0223dd1b5 100644
--- a/chromium/ui/views/metadata/type_conversion.cc
+++ b/chromium/ui/views/metadata/type_conversion.cc
@@ -317,6 +317,22 @@ DEFINE_ENUM_CONVERTERS(ui::TextInputType,
{ui::TextInputType::TEXT_INPUT_TYPE_MAX,
base::ASCIIToUTF16("TEXT_INPUT_TYPE_MAX")})
+DEFINE_ENUM_CONVERTERS(ui::MenuSeparatorType,
+ {ui::MenuSeparatorType::NORMAL_SEPARATOR,
+ base::ASCIIToUTF16("NORMAL_SEPARATOR")},
+ {ui::MenuSeparatorType::DOUBLE_SEPARATOR,
+ base::ASCIIToUTF16("DOUBLE_SEPARATOR")},
+ {ui::MenuSeparatorType::UPPER_SEPARATOR,
+ base::ASCIIToUTF16("UPPER_SEPARATOR")},
+ {ui::MenuSeparatorType::LOWER_SEPARATOR,
+ base::ASCIIToUTF16("LOWER_SEPARATOR")},
+ {ui::MenuSeparatorType::SPACING_SEPARATOR,
+ base::ASCIIToUTF16("SPACING_SEPARATOR")},
+ {ui::MenuSeparatorType::VERTICAL_SEPARATOR,
+ base::ASCIIToUTF16("VERTICAL_SEPARATOR")},
+ {ui::MenuSeparatorType::PADDED_SEPARATOR,
+ base::ASCIIToUTF16("PADDED_SEPARATOR")})
+
#define OP(enum_name) \
{ ui::NativeTheme::enum_name, base::ASCIIToUTF16(#enum_name) }
DEFINE_ENUM_CONVERTERS(ui::NativeTheme::ColorId, NATIVE_THEME_COLOR_IDS)
diff --git a/chromium/ui/views/repeat_controller.cc b/chromium/ui/views/repeat_controller.cc
index 5cabeac2a2d..5df70bdd6b8 100644
--- a/chromium/ui/views/repeat_controller.cc
+++ b/chromium/ui/views/repeat_controller.cc
@@ -6,22 +6,20 @@
#include <utility>
-using base::TimeDelta;
-
namespace views {
///////////////////////////////////////////////////////////////////////////////
// RepeatController, public:
-RepeatController::RepeatController(base::RepeatingClosure callback)
- : callback_(std::move(callback)) {}
+RepeatController::RepeatController(base::RepeatingClosure callback,
+ const base::TickClock* tick_clock)
+ : timer_(tick_clock), callback_(std::move(callback)) {}
RepeatController::~RepeatController() = default;
void RepeatController::Start() {
// The first timer is slightly longer than subsequent repeats.
- timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(250), this,
- &RepeatController::Run);
+ timer_.Start(FROM_HERE, kInitialWait, this, &RepeatController::Run);
}
void RepeatController::Stop() {
@@ -31,9 +29,14 @@ void RepeatController::Stop() {
///////////////////////////////////////////////////////////////////////////////
// RepeatController, private:
+// static
+constexpr base::TimeDelta RepeatController::kInitialWait;
+
+// static
+constexpr base::TimeDelta RepeatController::kRepeatingWait;
+
void RepeatController::Run() {
- timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(50), this,
- &RepeatController::Run);
+ timer_.Start(FROM_HERE, kRepeatingWait, this, &RepeatController::Run);
callback_.Run();
}
diff --git a/chromium/ui/views/repeat_controller.h b/chromium/ui/views/repeat_controller.h
index b2afddfc9ce..00878f2af79 100644
--- a/chromium/ui/views/repeat_controller.h
+++ b/chromium/ui/views/repeat_controller.h
@@ -8,6 +8,11 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/timer/timer.h"
+#include "ui/views/views_export.h"
+
+namespace base {
+class TickClock;
+}
namespace views {
@@ -21,9 +26,10 @@ namespace views {
// associated action.
//
///////////////////////////////////////////////////////////////////////////////
-class RepeatController {
+class VIEWS_EXPORT RepeatController {
public:
- explicit RepeatController(base::RepeatingClosure callback);
+ explicit RepeatController(base::RepeatingClosure callback,
+ const base::TickClock* tick_clock = nullptr);
virtual ~RepeatController();
// Start repeating.
@@ -32,9 +38,24 @@ class RepeatController {
// Stop repeating.
void Stop();
+ static constexpr base::TimeDelta GetInitialWaitForTesting() {
+ return kInitialWait;
+ }
+ static constexpr base::TimeDelta GetRepeatingWaitForTesting() {
+ return kRepeatingWait;
+ }
+
const base::OneShotTimer& timer_for_testing() const { return timer_; }
private:
+ // Initial time required before the first callback occurs.
+ static constexpr base::TimeDelta kInitialWait =
+ base::TimeDelta::FromMilliseconds(250);
+
+ // Period of callbacks after the first callback.
+ static constexpr base::TimeDelta kRepeatingWait =
+ base::TimeDelta::FromMilliseconds(50);
+
// Called when the timer expires.
void Run();
diff --git a/chromium/ui/views/repeat_controller_unittest.cc b/chromium/ui/views/repeat_controller_unittest.cc
new file mode 100644
index 00000000000..abab3527737
--- /dev/null
+++ b/chromium/ui/views/repeat_controller_unittest.cc
@@ -0,0 +1,107 @@
+// 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 "ui/views/repeat_controller.h"
+
+#include "base/bind.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace views {
+
+namespace {
+
+class RepeatControllerTest : public testing::Test {
+ public:
+ RepeatControllerTest() = default;
+ ~RepeatControllerTest() override = default;
+
+ void SetUp() override {
+ // Ensures that the callback hasn't fired at initialization.
+ ASSERT_EQ(0, times_called_);
+ }
+
+ protected:
+ // Short wait that must be below both
+ // RepeatController::GetInitialWaitForTesting() and
+ // RepeatController::GetRepeatingWaitForTesting().
+ static constexpr base::TimeDelta kShortWait =
+ base::TimeDelta::FromMilliseconds(10);
+ static_assert(
+ kShortWait < RepeatController::GetInitialWaitForTesting(),
+ "kShortWait must be shorter than the RepeatController initial wait.");
+ static_assert(
+ kShortWait < RepeatController::GetRepeatingWaitForTesting(),
+ "kShortWait must be shorter than the RepeatController repeating wait.");
+
+ int times_called() const { return times_called_; }
+ RepeatController& repeat_controller() { return repeat_controller_; }
+
+ void AdvanceTime(base::TimeDelta time_delta) {
+ task_environment_.FastForwardBy(time_delta);
+ }
+
+ private:
+ void IncrementTimesCalled() { ++times_called_; }
+
+ int times_called_ = 0;
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ RepeatController repeat_controller_{
+ base::BindRepeating(&RepeatControllerTest::IncrementTimesCalled,
+ base::Unretained(this)),
+ task_environment_.GetMockTickClock()};
+};
+
+// static
+constexpr base::TimeDelta RepeatControllerTest::kShortWait;
+
+} // namespace
+
+TEST_F(RepeatControllerTest, StartStop) {
+ repeat_controller().Start();
+ repeat_controller().Stop();
+ EXPECT_EQ(0, times_called());
+}
+
+TEST_F(RepeatControllerTest, StartShortWait) {
+ repeat_controller().Start();
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() - kShortWait);
+ EXPECT_EQ(0, times_called());
+ repeat_controller().Stop();
+}
+
+TEST_F(RepeatControllerTest, StartInitialWait) {
+ repeat_controller().Start();
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() +
+ RepeatController::GetRepeatingWaitForTesting() - kShortWait);
+ EXPECT_EQ(1, times_called());
+ repeat_controller().Stop();
+}
+
+TEST_F(RepeatControllerTest, StartLongerWait) {
+ constexpr int kExpectedCallbacks = 34;
+ repeat_controller().Start();
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() +
+ (RepeatController::GetRepeatingWaitForTesting() *
+ (kExpectedCallbacks - 1)) +
+ kShortWait);
+ EXPECT_EQ(kExpectedCallbacks, times_called());
+ repeat_controller().Stop();
+}
+
+TEST_F(RepeatControllerTest, NoCallbacksAfterStop) {
+ constexpr int kExpectedCallbacks = 34;
+ repeat_controller().Start();
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() +
+ (RepeatController::GetRepeatingWaitForTesting() *
+ (kExpectedCallbacks - 1)) +
+ kShortWait);
+ repeat_controller().Stop();
+ AdvanceTime(RepeatController::GetInitialWaitForTesting() +
+ RepeatController::GetRepeatingWaitForTesting());
+ EXPECT_EQ(kExpectedCallbacks, times_called());
+}
+
+} // namespace views
diff --git a/chromium/ui/views/selection_controller.cc b/chromium/ui/views/selection_controller.cc
index 13c005d4be9..4f8f035a342 100644
--- a/chromium/ui/views/selection_controller.cc
+++ b/chromium/ui/views/selection_controller.cc
@@ -76,7 +76,7 @@ bool SelectionController::OnMousePressed(
if (event.IsOnlyRightMouseButton()) {
if (PlatformStyle::kSelectAllOnRightClickWhenUnfocused &&
- initial_focus_state == InitialFocusStateOnMousePress::UNFOCUSED) {
+ initial_focus_state == InitialFocusStateOnMousePress::kUnFocused) {
SelectAll();
} else if (PlatformStyle::kSelectWordOnRightClick &&
!render_text->IsPointInSelection(event.location()) &&
diff --git a/chromium/ui/views/selection_controller.h b/chromium/ui/views/selection_controller.h
index 4603542d01e..31ec34864ec 100644
--- a/chromium/ui/views/selection_controller.h
+++ b/chromium/ui/views/selection_controller.h
@@ -30,9 +30,9 @@ class VIEWS_EXPORT SelectionController {
public:
// Describes whether the view managing the delegate was initially focused when
// the mouse press was received.
- enum InitialFocusStateOnMousePress {
- FOCUSED,
- UNFOCUSED,
+ enum class InitialFocusStateOnMousePress {
+ kFocused,
+ kUnFocused,
};
// |delegate| must be non-null.
diff --git a/chromium/ui/views/selection_controller_unittest.cc b/chromium/ui/views/selection_controller_unittest.cc
index b14035c6bf3..1c5fad7fbb9 100644
--- a/chromium/ui/views/selection_controller_unittest.cc
+++ b/chromium/ui/views/selection_controller_unittest.cc
@@ -123,8 +123,9 @@ class SelectionControllerTest : public ::testing::Test {
ui::MouseEvent(ui::ET_MOUSE_PRESSED, location, location,
last_event_time_, mouse_flags_, button),
false,
- focused ? SelectionController::FOCUSED
- : SelectionController::UNFOCUSED);
+ focused
+ ? SelectionController::InitialFocusStateOnMousePress::kFocused
+ : SelectionController::InitialFocusStateOnMousePress::kUnFocused);
}
void ReleaseMouseButton(int button) {
diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc
index a5f91b0da94..155fb12b920 100644
--- a/chromium/ui/views/style/platform_style.cc
+++ b/chromium/ui/views/style/platform_style.cc
@@ -33,7 +33,7 @@ const bool PlatformStyle::kIsOkButtonLeading = false;
const float PlatformStyle::kFocusHaloThickness = 2.f;
const float PlatformStyle::kFocusHaloInset = -1.f;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
const int PlatformStyle::kMinLabelButtonWidth = 70;
const int PlatformStyle::kMinLabelButtonHeight = 33;
@@ -72,9 +72,10 @@ gfx::Range PlatformStyle::RangeToDeleteBackwards(const base::string16& text,
return gfx::Range(cursor_position, previous_grapheme_index);
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
-#if !BUILDFLAG(ENABLE_DESKTOP_AURA) || !defined(OS_LINUX)
+#if !BUILDFLAG(ENABLE_DESKTOP_AURA) || \
+ (!defined(OS_LINUX) && !defined(OS_CHROMEOS))
// static
std::unique_ptr<Border> PlatformStyle::CreateThemedLabelButtonBorder(
LabelButton* button) {
diff --git a/chromium/ui/views/style/typography_provider.cc b/chromium/ui/views/style/typography_provider.cc
index 419f5c1ba4e..44f3d4f5359 100644
--- a/chromium/ui/views/style/typography_provider.cc
+++ b/chromium/ui/views/style/typography_provider.cc
@@ -12,7 +12,7 @@
#include "ui/views/style/typography.h"
#include "ui/views/view.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "base/mac/mac_util.h"
#endif
@@ -143,12 +143,12 @@ SkColor TypographyProvider::GetColor(const View& view,
}
int TypographyProvider::GetLineHeight(int context, int style) const {
- return 0;
+ return GetFont(context, style).GetHeight();
}
// static
gfx::Font::Weight TypographyProvider::MediumWeightForUI() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// System fonts are not user-configurable on Mac, so there's a simpler check.
// However, 10.11 do not ship with a MEDIUM weight system font. In that
// case, trying to use MEDIUM there will give a bold font, which will look
diff --git a/chromium/ui/views/style/typography_provider.h b/chromium/ui/views/style/typography_provider.h
index e8e9c8255ee..3184eae2e5e 100644
--- a/chromium/ui/views/style/typography_provider.h
+++ b/chromium/ui/views/style/typography_provider.h
@@ -33,7 +33,7 @@ class VIEWS_EXPORT TypographyProvider {
int context,
int style) const;
- // Gets the line spacing, or 0 if it should be provided by gfx::FontList.
+ // Gets the line spacing. By default this is the font height.
virtual int GetLineHeight(int context, int style) const;
// Returns the weight that will result in the ResourceBundle returning an
diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc
index 186b6c5f5cd..174013d87e9 100644
--- a/chromium/ui/views/view.cc
+++ b/chromium/ui/views/view.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
+#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/notreached.h"
#include "base/scoped_observer.h"
@@ -25,6 +26,7 @@
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/ime/input_method.h"
#include "ui/compositor/clip_recorder.h"
#include "ui/compositor/compositor.h"
@@ -341,7 +343,7 @@ void View::SetBoundsRect(const gfx::Rect& bounds) {
// In RTL mode, if our width has changed, our children's mirrored bounds
// will have changed. Update the child's layer bounds, or if it is not a
// layer, the bounds of any layers inside the child.
- if (base::i18n::IsRTL() && bounds_.width() != prev.width()) {
+ if (GetMirrored() && bounds_.width() != prev.width()) {
for (View* child : children_) {
child->UpdateChildLayerBounds(
LayerOffsetData(layer()->device_scale_factor(),
@@ -586,7 +588,7 @@ gfx::Transform View::GetTransform() const {
gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset();
// Offsets for layer-based scrolling are never negative, but the horizontal
// scroll direction is reversed in RTL via canvas flipping.
- transform.Translate((base::i18n::IsRTL() ? 1 : -1) * scroll_offset.x(),
+ transform.Translate((GetMirrored() ? 1 : -1) * scroll_offset.x(),
-scroll_offset.y());
return transform;
}
@@ -734,7 +736,7 @@ int View::GetMirroredX() const {
}
int View::GetMirroredXForRect(const gfx::Rect& rect) const {
- return base::i18n::IsRTL() ? (width() - rect.x() - rect.width()) : rect.x();
+ return GetMirrored() ? (width() - rect.x() - rect.width()) : rect.x();
}
gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
@@ -744,11 +746,11 @@ gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
}
int View::GetMirroredXInView(int x) const {
- return base::i18n::IsRTL() ? width() - x : x;
+ return GetMirrored() ? width() - x : x;
}
int View::GetMirroredXWithWidthInView(int x, int w) const {
- return base::i18n::IsRTL() ? width() - x - w : x;
+ return GetMirrored() ? width() - x - w : x;
}
// Layout ----------------------------------------------------------------------
@@ -1145,6 +1147,10 @@ void View::EnableCanvasFlippingForRTLUI(bool enable) {
flip_canvas_on_paint_for_rtl_ui_ = enable;
}
+bool View::GetMirrored() const {
+ return is_mirrored_.value_or(base::i18n::IsRTL());
+}
+
// Input -----------------------------------------------------------------------
View* View::GetEventHandlerForPoint(const gfx::Point& point) {
@@ -2076,7 +2082,6 @@ void View::HandlePropertyChangeEffects(PropertyEffects effects) {
InvalidateLayout();
if (effects & kPropertyEffectsPaint)
SchedulePaint();
- OnHandlePropertyChangeEffects(effects);
}
PropertyChangedSubscription View::AddPropertyChangedCallback(
@@ -2775,7 +2780,7 @@ void View::ProcessMouseDragged(ui::MouseEvent* event) {
drag_controller_->CanStartDragForView(this, GetDragInfo()->start_pt,
event->location()))) {
if (DoDrag(*event, GetDragInfo()->start_pt,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE)) {
+ ui::mojom::DragEventSource::kMouse)) {
event->StopPropagation();
return;
}
@@ -2961,7 +2966,7 @@ void View::UpdateTooltip() {
bool View::DoDrag(const ui::LocatedEvent& event,
const gfx::Point& press_pt,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
int drag_operations = GetDragOperations(press_pt);
if (drag_operations == ui::DragDropTypes::DRAG_NONE)
return false;
@@ -3007,6 +3012,7 @@ ADD_PROPERTY_METADATA(View, int, Group)
ADD_PROPERTY_METADATA(View, int, ID)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MaximumSize)
ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MinimumSize)
+ADD_PROPERTY_METADATA(View, bool, Mirrored)
ADD_PROPERTY_METADATA(View, bool, Visible)
END_METADATA()
diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h
index 23b5d3ffd8b..1990d29fcae 100644
--- a/chromium/ui/views/view.h
+++ b/chromium/ui/views/view.h
@@ -23,14 +23,15 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/optional.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/class_property.h"
#include "ui/base/clipboard/clipboard_format_type.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drop_target_event.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/layer_delegate.h"
@@ -43,6 +44,7 @@
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/metadata/metadata_header_macros.h"
@@ -125,7 +127,7 @@ struct VIEWS_EXPORT ViewHierarchyChangedDetails {
// Used to identify the CallbackList<> within the PropertyChangedVectors map.
using PropertyKey = const void*;
-using PropertyChangedCallbacks = base::CallbackList<void()>;
+using PropertyChangedCallbacks = base::RepeatingClosureList;
using PropertyChangedCallback = PropertyChangedCallbacks::CallbackType;
using PropertyChangedSubscription =
std::unique_ptr<PropertyChangedCallbacks::Subscription>;
@@ -358,9 +360,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Since pixels cannot be fractional, we need to round the offset to get
// the correct physical pixel coordinate.
- gfx::Vector2dF integral_pixel_offset(
- gfx::ToRoundedInt(fractional_pixel_offset.x()),
- gfx::ToRoundedInt(fractional_pixel_offset.y()));
+ gfx::Vector2d integral_pixel_offset =
+ gfx::ToRoundedVector2d(fractional_pixel_offset);
// |integral_pixel_offset - fractional_pixel_offset| gives the subpixel
// offset amount for |offset_to_parent|. This is added to
@@ -907,7 +908,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Enables or disables flipping of the gfx::Canvas during Paint(). Note that
// if canvas flipping is enabled, the canvas will be flipped only if the UI
// layout is right-to-left; that is, the canvas will be flipped only if
- // base::i18n::IsRTL() returns true.
+ // GetMirrored() is true.
//
// Enabling canvas flipping is useful for leaf views that draw an image that
// needs to be flipped horizontally when the UI layout is right-to-left
@@ -916,6 +917,19 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// the UI directionality.
virtual void EnableCanvasFlippingForRTLUI(bool enable);
+ // When set, this view will ignore base::l18n::IsRTL() and instead be drawn
+ // according to |is_mirrored|.
+ //
+ // This is useful for views that should be displayed the same regardless of UI
+ // direction. Unlike EnableCanvasFlippingForRTLUI this setting has an effect
+ // on the visual order of child views.
+ //
+ // This setting does not propagate to child views. So while the visual order
+ // of this view's children may change, the visual order of this view's
+ // grandchildren in relation to their parents are unchanged.
+ void SetMirrored(bool is_mirrored) { is_mirrored_ = is_mirrored; }
+ bool GetMirrored() const;
+
// Input ---------------------------------------------------------------------
// The points, rects, mouse locations, and touch locations in the following
// functions are in the view's coordinates, except for a RootView.
@@ -1574,15 +1588,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
PropertyChangedSubscription AddPropertyChangedCallback(
PropertyKey property,
- PropertyChangedCallback callback);
+ PropertyChangedCallback callback) WARN_UNUSED_RESULT;
void OnPropertyChanged(PropertyKey property,
PropertyEffects property_effects);
- // Empty function called in HandlePropertyChangeEffects to be overridden in
- // subclasses if they have custom functions for property changes.
- virtual void OnHandlePropertyChangeEffects(PropertyEffects property_effects) {
- }
-
private:
friend class internal::PreEventDispatchHandler;
friend class internal::PostEventDispatchHandler;
@@ -1859,7 +1868,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// Returns true if a drag was started.
bool DoDrag(const ui::LocatedEvent& event,
const gfx::Point& press_pt,
- ui::DragDropTypes::DragEventSource source);
+ ui::mojom::DragEventSource source);
// Property support ----------------------------------------------------------
@@ -1981,6 +1990,13 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
// right-to-left locales for this View.
bool flip_canvas_on_paint_for_rtl_ui_ = false;
+ // Controls whether GetTransform(), the mirroring functions, and the like
+ // horizontally mirror. This controls how child views are physically
+ // positioned onscreen. The default behavior should be correct in most cases,
+ // but can be overridden if a particular view must always be laid out in some
+ // direction regardless of the application's default UI direction.
+ base::Optional<bool> is_mirrored_;
+
// Accelerated painting ------------------------------------------------------
// Whether layer painting was explicitly set by a call to |SetPaintToLayer()|.
diff --git a/chromium/ui/views/view_targeter_delegate.cc b/chromium/ui/views/view_targeter_delegate.cc
index 82f1424b4cf..c64e6eabe1a 100644
--- a/chromium/ui/views/view_targeter_delegate.cc
+++ b/chromium/ui/views/view_targeter_delegate.cc
@@ -89,17 +89,6 @@ View* ViewTargeterDelegate::TargetForRect(View* root, const gfx::Rect& rect) {
if (views::UsePointBasedTargeting(rect) || (!rect_view && !point_view))
return root;
- // If |root| is a suitable candidate for rect-based targeting, check to
- // see if it is closer than the current best suitable candidate so far.
- gfx::Rect local_bounds(root->GetLocalBounds());
- if (views::PercentCoveredBy(local_bounds, rect) >= kRectTargetOverlap) {
- gfx::Point touch_center(rect.CenterPoint());
- int cur_dist =
- views::DistanceSquaredFromCenterToPoint(touch_center, local_bounds);
- if (!rect_view || cur_dist < rect_view_distance)
- rect_view = root;
- }
-
return rect_view ? rect_view : point_view;
}
diff --git a/chromium/ui/views/view_targeter_unittest.cc b/chromium/ui/views/view_targeter_unittest.cc
index f2994f9a157..a14e3637174 100644
--- a/chromium/ui/views/view_targeter_unittest.cc
+++ b/chromium/ui/views/view_targeter_unittest.cc
@@ -4,6 +4,7 @@
#include "ui/views/view_targeter.h"
+#include <memory>
#include <utility>
#include "base/macros.h"
@@ -651,5 +652,29 @@ TEST_F(ViewTargeterTest, HitTestCallsOnView) {
widget->CloseNow();
}
+TEST_F(ViewTargeterTest, FavorChildContainingHitBounds) {
+ Widget widget;
+ Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ init_params.bounds = gfx::Rect(0, 0, 200, 200);
+ widget.Init(std::move(init_params));
+
+ View* content = widget.SetContentsView(std::make_unique<View>());
+ content->SetBounds(0, 0, 50, 50);
+ View* child = content->AddChildView(std::make_unique<View>());
+ child->SetBounds(2, 2, 50, 50);
+
+ internal::RootView* root_view =
+ static_cast<internal::RootView*>(widget.GetRootView());
+ ui::EventTargeter* targeter = root_view->targeter();
+
+ gfx::RectF bounding_box(gfx::PointF(4.f, 4.f), gfx::SizeF(42.f, 42.f));
+ ui::GestureEventDetails details(ui::ET_GESTURE_TAP);
+ details.set_bounding_box(bounding_box);
+ GestureEventForTest tap(details);
+
+ EXPECT_EQ(child, targeter->FindTargetForEvent(root_view, &tap));
+}
+
} // namespace test
} // namespace views
diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc
index 7613c1939c8..68f7f6e8492 100644
--- a/chromium/ui/views/view_unittest.cc
+++ b/chromium/ui/views/view_unittest.cc
@@ -51,6 +51,7 @@
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/box_layout.h"
#include "ui/views/metadata/metadata_types.h"
#include "ui/views/paint_info.h"
#include "ui/views/test/view_metadata_test_utils.h"
@@ -1531,7 +1532,8 @@ TEST_F(ViewTest, GetEventHandlerForRect) {
// the center points of |v4| and |v41|.
touch_rect.SetRect(310, 210, 80, 80);
result_view = root_view->GetEventHandlerForRect(touch_rect);
- EXPECT_EQ(v41, result_view);
+ // |v411| is the deepest view that is completely contained by |touch_rect|.
+ EXPECT_EQ(v411, result_view);
result_view = nullptr;
// Intersects all of |v4|, |v41|, and |v411| but only covers
@@ -1602,21 +1604,19 @@ TEST_F(ViewTest, GetEventHandlerForRect) {
// Intersects all of |v2|, |v3|, |v32|, |v4|, |v41|, and |v411|.
// Covers |v2|, |v32|, |v4|, |v41|, and |v411| by at least 60%.
- // The center point of |touch_rect| is closest to the center
- // point of |root_view|.
touch_rect.SetRect(110, 15, 375, 450);
result_view = root_view->GetEventHandlerForRect(touch_rect);
- EXPECT_EQ(root_view, result_view);
+ // Target is |v411| as it is the deepest view touched by at least 60% of the
+ // rect.
+ EXPECT_EQ(v411, result_view);
result_view = nullptr;
// Covers all views (except |v5| and |v51|) by at least 60%. The
// center point of |touch_rect| is equally close to the center
- // points of |v2| and |v32|. One is not a descendant of the other,
- // so in this case the view selected is arbitrary (i.e.,
- // it depends only on the ordering of nodes in the views
- // hierarchy).
+ // points of |v2| and |v32|.
touch_rect.SetRect(0, 0, 400, 300);
result_view = root_view->GetEventHandlerForRect(touch_rect);
+ // |v32| is the deepest view that is contained by the rest.
EXPECT_EQ(v32, result_view);
result_view = nullptr;
@@ -2040,21 +2040,24 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) {
normal->SelectAll(false);
normal->ExecuteCommand(Textfield::kCut, 0);
base::string16 result;
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
EXPECT_EQ(kNormalText, result);
normal->SetText(kNormalText); // Let's revert to the original content.
read_only->SelectAll(false);
read_only->ExecuteCommand(Textfield::kCut, 0);
result.clear();
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
// Cut should have failed, so the clipboard content should not have changed.
EXPECT_EQ(kNormalText, result);
password->SelectAll(false);
password->ExecuteCommand(Textfield::kCut, 0);
result.clear();
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
// Cut should have failed, so the clipboard content should not have changed.
EXPECT_EQ(kNormalText, result);
@@ -2066,19 +2069,22 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) {
read_only->SelectAll(false);
read_only->ExecuteCommand(Textfield::kCopy, 0);
result.clear();
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
EXPECT_EQ(kReadOnlyText, result);
normal->SelectAll(false);
normal->ExecuteCommand(Textfield::kCopy, 0);
result.clear();
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
EXPECT_EQ(kNormalText, result);
password->SelectAll(false);
password->ExecuteCommand(Textfield::kCopy, 0);
result.clear();
- clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result);
+ clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
+ &result);
// Text cannot be copied from an obscured field; the clipboard won't change.
EXPECT_EQ(kNormalText, result);
@@ -2288,7 +2294,7 @@ TEST_F(ViewTest, HandleAccelerator) {
// TODO(themblsha): Bring this up on non-Mac platforms. It currently fails
// because TestView::AcceleratorPressed() is not called. See
// http://crbug.com/667757.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Test that BridgedContentView correctly handles Accelerator key events when
// subject to OS event dispatch.
TEST_F(ViewTest, ActivateAcceleratorOnMac) {
@@ -2331,11 +2337,11 @@ TEST_F(ViewTest, ActivateAcceleratorOnMac) {
key_down_accelerator.modifiers());
EXPECT_EQ(view->accelerator_count_map_[key_down_accelerator], 1);
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
// TODO(crbug.com/667757): these tests were initially commented out when getting
// aura to run. Figure out if still valuable and either nuke or fix.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
TEST_F(ViewTest, ActivateAccelerator) {
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
TestViewWidget test_widget(CreateParams(Widget::InitParams::TYPE_POPUP),
@@ -2417,7 +2423,7 @@ TEST_F(ViewTest, ViewInHiddenWidgetWithAccelerator) {
EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(1, view->accelerator_count_map_[return_accelerator]);
}
-#endif // OS_MACOSX
+#endif // OS_APPLE
// TODO(crbug.com/667757): these tests were initially commented out when getting
// aura to run. Figure out if still valuable and either nuke or fix.
@@ -3776,6 +3782,62 @@ TEST_F(ViewTest, AddExistingChild) {
EXPECT_EQ(1, v1.GetIndexOf(&v3));
}
+TEST_F(ViewTest, UseMirroredLayoutDisableMirroring) {
+ base::i18n::SetICUDefaultLocale("ar");
+ ASSERT_TRUE(base::i18n::IsRTL());
+
+ View parent, child1, child2;
+ parent.SetLayoutManager(
+ std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
+
+ child1.SetPreferredSize(gfx::Size(10, 10));
+ child2.SetPreferredSize(gfx::Size(10, 10));
+
+ parent.AddChildView(&child1);
+ parent.AddChildView(&child2);
+ parent.SizeToPreferredSize();
+
+ EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+ EXPECT_GT(child1.GetMirroredX(), child2.GetMirroredX());
+ EXPECT_LT(child1.x(), child2.x());
+ EXPECT_NE(parent.GetMirroredXInView(5), 5);
+
+ parent.SetMirrored(false);
+
+ EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+ EXPECT_GT(child2.GetMirroredX(), child1.GetMirroredX());
+ EXPECT_LT(child1.x(), child2.x());
+ EXPECT_EQ(parent.GetMirroredXInView(5), 5);
+}
+
+TEST_F(ViewTest, UseMirroredLayoutEnableMirroring) {
+ base::i18n::SetICUDefaultLocale("en");
+ ASSERT_FALSE(base::i18n::IsRTL());
+
+ View parent, child1, child2;
+ parent.SetLayoutManager(
+ std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
+
+ child1.SetPreferredSize(gfx::Size(10, 10));
+ child2.SetPreferredSize(gfx::Size(10, 10));
+
+ parent.AddChildView(&child1);
+ parent.AddChildView(&child2);
+ parent.SizeToPreferredSize();
+
+ EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+ EXPECT_LT(child1.GetMirroredX(), child2.GetMirroredX());
+ EXPECT_LT(child1.x(), child2.x());
+ EXPECT_NE(parent.GetMirroredXInView(5), 15);
+
+ parent.SetMirrored(true);
+
+ EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+ EXPECT_LT(child2.GetMirroredX(), child1.GetMirroredX());
+ EXPECT_LT(child1.x(), child2.x());
+ EXPECT_EQ(parent.GetMirroredXInView(5), 15);
+}
+
////////////////////////////////////////////////////////////////////////////////
// FocusManager
////////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc
index e0007503954..f856122274b 100644
--- a/chromium/ui/views/views_delegate.cc
+++ b/chromium/ui/views/views_delegate.cc
@@ -85,14 +85,15 @@ HICON ViewsDelegate::GetSmallWindowIcon() const {
bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const {
return false;
}
-#elif defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#elif BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_LINUX) || defined(OS_CHROMEOS))
gfx::ImageSkia* ViewsDelegate::GetDefaultWindowIcon() const {
return nullptr;
}
#endif
-NonClientFrameView* ViewsDelegate::CreateDefaultNonClientFrameView(
- Widget* widget) {
+std::unique_ptr<NonClientFrameView>
+ViewsDelegate::CreateDefaultNonClientFrameView(Widget* widget) {
return nullptr;
}
@@ -112,7 +113,7 @@ bool ViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) {
return false;
}
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
ui::ContextFactory* ViewsDelegate::GetContextFactory() {
return nullptr;
}
diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h
index 23b538ab496..177271c66b2 100644
--- a/chromium/ui/views/views_delegate.h
+++ b/chromium/ui/views/views_delegate.h
@@ -30,7 +30,7 @@ class Rect;
} // namespace gfx
namespace ui {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
class ContextFactory;
#endif
class TouchEditingControllerFactory;
@@ -133,14 +133,16 @@ class VIEWS_EXPORT ViewsDelegate {
// Returns true if the window passed in is in the Windows 8 metro
// environment.
virtual bool IsWindowInMetro(gfx::NativeWindow window) const;
-#elif defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#elif BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_LINUX) || defined(OS_CHROMEOS))
virtual gfx::ImageSkia* GetDefaultWindowIcon() const;
#endif
// Creates a default NonClientFrameView to be used for windows that don't
// specify their own. If this function returns NULL, the
// views::CustomFrameView type will be used.
- virtual NonClientFrameView* CreateDefaultNonClientFrameView(Widget* widget);
+ virtual std::unique_ptr<NonClientFrameView> CreateDefaultNonClientFrameView(
+ Widget* widget);
// AddRef/ReleaseRef are invoked while a menu is visible. They are used to
// ensure we don't attempt to exit while a menu is showing.
@@ -160,7 +162,7 @@ class VIEWS_EXPORT ViewsDelegate {
// maximized windows; otherwise to restored windows.
virtual bool WindowManagerProvidesTitleBar(bool maximized);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Returns the context factory for new windows.
virtual ui::ContextFactory* GetContextFactory();
#endif
diff --git a/chromium/ui/views/views_features.cc b/chromium/ui/views/views_features.cc
index b3702c04028..3c30f2f1fe6 100644
--- a/chromium/ui/views/views_features.cc
+++ b/chromium/ui/views/views_features.cc
@@ -5,6 +5,7 @@
#include "ui/views/views_features.h"
#include "base/feature_list.h"
+#include "build/build_config.h"
namespace views {
namespace features {
@@ -31,8 +32,14 @@ const base::Feature kEnableViewPaintOptimization{
// Change views::Textfield to take focus on a completed tap, rather than
// immediately on tap down. This only affects touch input. See
// https://crbug.com/1069634.
-const base::Feature kTextfieldFocusOnTapUp{"TextfieldFocusOnTapUp",
- base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTextfieldFocusOnTapUp {
+ "TextfieldFocusOnTapUp",
+#if defined(OS_CHROMEOS)
+ base::FEATURE_DISABLED_BY_DEFAULT
+#else
+ base::FEATURE_ENABLED_BY_DEFAULT
+#endif // defined(OS_CHROMEOS)
+};
// Allows a "New" badge to be displayed on menu items that provide access to new
// features.
diff --git a/chromium/ui/views/views_test_suite.cc b/chromium/ui/views/views_test_suite.cc
index dbdb4c18b28..0c0796dd0a3 100644
--- a/chromium/ui/views/views_test_suite.cc
+++ b/chromium/ui/views/views_test_suite.cc
@@ -63,20 +63,20 @@ void ViewsTestSuite::Initialize() {
base::FilePath ui_test_pak_path;
ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#if defined(USE_AURA)
InitializeEnv();
#endif
}
void ViewsTestSuite::Shutdown() {
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#if defined(USE_AURA)
DestroyEnv();
#endif
ui::ResourceBundle::CleanupSharedInstance();
base::TestSuite::Shutdown();
}
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#if defined(USE_AURA)
void ViewsTestSuite::InitializeEnv() {
env_ = aura::Env::CreateInstance();
}
diff --git a/chromium/ui/views/views_test_suite.h b/chromium/ui/views/views_test_suite.h
index 265de530e0a..c9ab3d62545 100644
--- a/chromium/ui/views/views_test_suite.h
+++ b/chromium/ui/views/views_test_suite.h
@@ -9,7 +9,7 @@
#include "build/build_config.h"
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#if defined(USE_AURA)
#include <memory>
namespace aura {
@@ -32,16 +32,14 @@ class ViewsTestSuite : public base::TestSuite {
void Initialize() override;
void Shutdown() override;
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#if defined(USE_AURA)
// Different test suites may wish to create Env differently.
virtual void InitializeEnv();
virtual void DestroyEnv();
#endif
private:
-#if defined(USE_AURA) && !defined(OS_CHROMEOS)
- // On Chrome OS, aura::Env is set up in individual test fixtures, most notably
- // ViewsTestBase.
+#if defined(USE_AURA)
std::unique_ptr<aura::Env> env_;
#endif
diff --git a/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm
index ca7575056b0..865341f7c47 100644
--- a/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm
+++ b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm
@@ -81,7 +81,7 @@ class FlexibleRoleTestView : public View {
class TestLabelButton : public LabelButton {
public:
- TestLabelButton() : LabelButton(nullptr, base::string16()) {
+ TestLabelButton() {
// Make sure the label doesn't cover the hit test co-ordinates.
label()->SetSize(gfx::Size(1, 1));
}
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
deleted file mode 100644
index 80090c42e7d..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
+++ /dev/null
@@ -1,420 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/aura/client/capture_client.h"
-#include "ui/aura/client/drag_drop_client.h"
-#include "ui/aura/client/drag_drop_delegate.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/base/dragdrop/drop_target_event.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_x11.h"
-#include "ui/base/layout.h"
-#include "ui/base/x/selection_utils.h"
-#include "ui/base/x/x11_cursor.h"
-#include "ui/base/x/x11_drag_context.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_whole_screen_move_loop.h"
-#include "ui/display/screen.h"
-#include "ui/events/event.h"
-#include "ui/events/event_utils.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/xproto.h"
-#include "ui/platform_window/x11/x11_topmost_window_finder.h"
-#include "ui/views/controls/image_view.h"
-#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
-#include "ui/views/widget/widget.h"
-
-using aura::client::DragDropDelegate;
-using ui::OSExchangeData;
-
-namespace {
-
-// The minimum alpha before we declare a pixel transparent when searching in
-// our source image.
-constexpr uint32_t kMinAlpha = 32;
-
-// |drag_widget_|'s opacity.
-constexpr float kDragWidgetOpacity = .75f;
-
-// Returns true if |image| has any visible regions (defined as having a pixel
-// with alpha > 32).
-bool IsValidDragImage(const gfx::ImageSkia& image) {
- if (image.isNull())
- return false;
-
- // Because we need a GL context per window, we do a quick check so that we
- // don't make another context if the window would just be displaying a mostly
- // transparent image.
- const SkBitmap* in_bitmap = image.bitmap();
- for (int y = 0; y < in_bitmap->height(); ++y) {
- uint32_t* in_row = in_bitmap->getAddr32(0, y);
-
- for (int x = 0; x < in_bitmap->width(); ++x) {
- if (SkColorGetA(in_row[x]) > kMinAlpha)
- return true;
- }
- }
-
- return false;
-}
-
-std::unique_ptr<views::Widget> CreateDragWidget(
- const gfx::ImageSkia& image,
- const gfx::Vector2d& drag_widget_offset) {
- auto widget = std::make_unique<views::Widget>();
- views::Widget::InitParams params(views::Widget::InitParams::TYPE_DRAG);
- if (ui::IsCompositingManagerPresent())
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- else
- params.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.accept_events = false;
-
- gfx::Point location =
- display::Screen::GetScreen()->GetCursorScreenPoint() - drag_widget_offset;
- params.bounds = gfx::Rect(location, image.size());
- widget->set_focus_on_creation(false);
- widget->set_frame_type(views::Widget::FrameType::kForceNative);
- widget->Init(std::move(params));
- if (params.opacity == views::Widget::InitParams::WindowOpacity::kTranslucent)
- widget->SetOpacity(kDragWidgetOpacity);
- widget->GetNativeWindow()->SetName("DragWindow");
-
- views::ImageView* image_view = new views::ImageView();
- image_view->SetImage(image);
- image_view->SetBoundsRect(gfx::Rect(image.size()));
- widget->SetContentsView(image_view);
- widget->Show();
- widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false);
-
- return widget;
-}
-
-} // namespace
-
-namespace views {
-
-DesktopDragDropClientAuraX11*
- DesktopDragDropClientAuraX11::g_current_drag_drop_client = nullptr;
-
-///////////////////////////////////////////////////////////////////////////////
-
-DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
- aura::Window* root_window,
- views::DesktopNativeCursorManager* cursor_manager,
- x11::Window window)
- : XDragDropClient(this, window),
- root_window_(root_window),
- cursor_manager_(cursor_manager) {}
-
-DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
- // This is necessary when the parent native widget gets destroyed while a drag
- // operation is in progress.
- move_loop_->EndMoveLoop();
- NotifyDragLeave();
- ResetDragContext();
-}
-
-void DesktopDragDropClientAuraX11::Init() {
- move_loop_ = CreateMoveLoop(this);
-}
-
-int DesktopDragDropClientAuraX11::StartDragAndDrop(
- std::unique_ptr<ui::OSExchangeData> data,
- aura::Window* root_window,
- aura::Window* source_window,
- const gfx::Point& /*screen_location*/,
- int operation,
- ui::DragDropTypes::DragEventSource source) {
- DCHECK(!g_current_drag_drop_client);
- g_current_drag_drop_client = this;
-
- InitDrag(operation, data.get());
-
- DCHECK(source_provider());
- gfx::ImageSkia drag_image = source_provider()->GetDragImage();
- if (IsValidDragImage(drag_image)) {
- drag_image_size_ = drag_image.size();
- drag_widget_offset_ = source_provider()->GetDragImageOffset();
- drag_widget_ = CreateDragWidget(drag_image, drag_widget_offset_);
- }
-
- // Chrome expects starting drag and drop to release capture.
- aura::Window* capture_window =
- aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
- if (capture_window)
- capture_window->ReleaseCapture();
-
- // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
- // move loop, which would also destroy this drag-client. So keep track of
- // whether it is alive after the drag ends.
- base::WeakPtr<DesktopDragDropClientAuraX11> alive(
- weak_ptr_factory_.GetWeakPtr());
-
- // Windows has a specific method, DoDragDrop(), which performs the entire
- // drag. We have to emulate this, so we spin off a nested runloop which will
- // track all cursor movement and reroute events to a specific handler.
- auto* last_cursor = static_cast<ui::X11Cursor*>(
- source_window->GetHost()->last_cursor().platform());
- move_loop_->RunMoveLoop(
- !source_window->HasCapture(),
- last_cursor ? last_cursor->xcursor() : x11::None,
- static_cast<ui::X11Cursor*>(
- cursor_manager_
- ->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)
- .platform())
- ->xcursor());
-
- if (alive) {
- auto resulting_operation = negotiated_operation();
- drag_widget_.reset();
- g_current_drag_drop_client = nullptr;
- CleanupDrag();
- return resulting_operation;
- }
- return ui::DragDropTypes::DRAG_NONE;
-}
-
-void DesktopDragDropClientAuraX11::DragCancel() {
- move_loop_->EndMoveLoop();
-}
-
-bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
- return !!g_current_drag_drop_client;
-}
-
-void DesktopDragDropClientAuraX11::AddObserver(
- aura::client::DragDropClientObserver* observer) {
- NOTIMPLEMENTED();
-}
-
-void DesktopDragDropClientAuraX11::RemoveObserver(
- aura::client::DragDropClientObserver* observer) {
- NOTIMPLEMENTED();
-}
-
-bool DesktopDragDropClientAuraX11::DispatchXEvent(x11::Event* event) {
- auto* prop = event->As<x11::PropertyNotifyEvent>();
- if (!target_current_context() || !prop ||
- prop->window != target_current_context()->source_window()) {
- return false;
- }
- return target_current_context()->DispatchPropertyNotifyEvent(*prop);
-}
-
-void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
- DCHECK_EQ(target_window_, window);
- target_window_ = nullptr;
-}
-
-void DesktopDragDropClientAuraX11::OnMouseMovement(
- const gfx::Point& screen_point,
- int flags,
- base::TimeTicks event_time) {
- if (drag_widget_.get()) {
- float scale_factor =
- ui::GetScaleFactorForNativeView(drag_widget_->GetNativeWindow());
- gfx::Point scaled_point =
- gfx::ScaleToRoundedPoint(screen_point, 1.f / scale_factor);
- drag_widget_->SetBounds(
- gfx::Rect(scaled_point - drag_widget_offset_, drag_image_size_));
- drag_widget_->StackAtTop();
- }
- HandleMouseMovement(screen_point, flags, event_time);
-}
-
-void DesktopDragDropClientAuraX11::OnMouseReleased() {
- XDragDropClient::HandleMouseReleased();
-}
-
-void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
- XDragDropClient::HandleMoveLoopEnded();
-}
-
-std::unique_ptr<ui::X11MoveLoop> DesktopDragDropClientAuraX11::CreateMoveLoop(
- X11MoveLoopDelegate* delegate) {
- return base::WrapUnique(new ui::X11WholeScreenMoveLoop(this));
-}
-
-void DesktopDragDropClientAuraX11::DragTranslate(
- const gfx::Point& root_window_location,
- std::unique_ptr<ui::OSExchangeData>* data,
- std::unique_ptr<ui::DropTargetEvent>* event,
- aura::client::DragDropDelegate** delegate) {
- gfx::Point root_location = root_window_location;
- root_window_->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
- aura::Window* target_window =
- root_window_->GetEventHandlerForPoint(root_location);
- bool target_window_changed = false;
- if (target_window != target_window_) {
- if (target_window_)
- NotifyDragLeave();
- target_window_ = target_window;
- if (target_window_)
- target_window_->AddObserver(this);
- target_window_changed = true;
- }
- *delegate = nullptr;
- if (!target_window_)
- return;
- *delegate = aura::client::GetDragDropDelegate(target_window_);
- if (!*delegate)
- return;
-
- DCHECK(target_current_context());
- *data = std::make_unique<OSExchangeData>(
- std::make_unique<ui::OSExchangeDataProviderX11>(
- xwindow(), target_current_context()->fetched_targets()));
- gfx::Point location = root_location;
- aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
-
- target_window_location_ = location;
- target_window_root_location_ = root_location;
-
- int drag_op = target_current_context()->GetDragOperation();
- // KDE-based file browsers such as Dolphin change the drag operation depending
- // on whether alt/ctrl/shift was pressed. However once Chromium gets control
- // over the X11 events, the source application does no longer receive X11
- // events for key modifier changes, so the dnd operation gets stuck in an
- // incorrect state. Blink can only dnd-open files of type DRAG_COPY, so the
- // DRAG_COPY mask is added if the dnd object is a file.
- if (drag_op & (ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_LINK) &&
- data->get()->HasFile()) {
- drag_op |= ui::DragDropTypes::DRAG_COPY;
- }
-
- *event = std::make_unique<ui::DropTargetEvent>(
- *(data->get()), gfx::PointF(location), gfx::PointF(root_location),
- drag_op);
- if (target_current_context()->source_client()) {
- (*event)->set_flags(
- target_current_context()->source_client()->current_modifier_state());
- } else {
- (*event)->set_flags(ui::XGetMaskAsEventFlags());
- }
- if (target_window_changed)
- (*delegate)->OnDragEntered(*event->get());
-}
-
-void DesktopDragDropClientAuraX11::NotifyDragLeave() {
- if (!target_window_)
- return;
- DragDropDelegate* delegate =
- aura::client::GetDragDropDelegate(target_window_);
- if (delegate)
- delegate->OnDragExited();
- target_window_->RemoveObserver(this);
- target_window_ = nullptr;
-}
-
-std::unique_ptr<ui::XTopmostWindowFinder>
-DesktopDragDropClientAuraX11::CreateWindowFinder() {
- return std::make_unique<ui::X11TopmostWindowFinder>();
-}
-
-int DesktopDragDropClientAuraX11::UpdateDrag(const gfx::Point& screen_point) {
- // The drop target event holds a reference to data, that's why we have to hold
- // the data until the event is handled.
- std::unique_ptr<ui::OSExchangeData> data;
- std::unique_ptr<ui::DropTargetEvent> drop_target_event;
- DragDropDelegate* delegate = nullptr;
- DragTranslate(screen_point, &data, &drop_target_event, &delegate);
- return delegate ? delegate->OnDragUpdated(*drop_target_event)
- : ui::DragDropTypes::DRAG_NONE;
-}
-
-void DesktopDragDropClientAuraX11::UpdateCursor(
- ui::DragDropTypes::DragOperation negotiated_operation) {
- ui::mojom::CursorType cursor_type = ui::mojom::CursorType::kNull;
- switch (negotiated_operation) {
- case ui::DragDropTypes::DRAG_NONE:
- cursor_type = ui::mojom::CursorType::kDndNone;
- break;
- case ui::DragDropTypes::DRAG_MOVE:
- cursor_type = ui::mojom::CursorType::kDndMove;
- break;
- case ui::DragDropTypes::DRAG_COPY:
- cursor_type = ui::mojom::CursorType::kDndCopy;
- break;
- case ui::DragDropTypes::DRAG_LINK:
- cursor_type = ui::mojom::CursorType::kDndLink;
- break;
- }
- move_loop_->UpdateCursor(
- static_cast<ui::X11Cursor*>(
- cursor_manager_->GetInitializedCursor(cursor_type).platform())
- ->xcursor());
-}
-
-void DesktopDragDropClientAuraX11::OnBeginForeignDrag(x11::Window window) {
- DCHECK(target_current_context());
- DCHECK(!target_current_context()->source_client());
-
- ui::X11EventSource::GetInstance()->AddXEventDispatcher(this);
- source_window_events_ =
- std::make_unique<ui::XScopedEventSelector>(window, PropertyChangeMask);
-}
-
-void DesktopDragDropClientAuraX11::OnEndForeignDrag() {
- DCHECK(target_current_context());
- DCHECK(!target_current_context()->source_client());
-
- ui::X11EventSource::GetInstance()->RemoveXEventDispatcher(this);
-}
-
-void DesktopDragDropClientAuraX11::OnBeforeDragLeave() {
- NotifyDragLeave();
-}
-
-int DesktopDragDropClientAuraX11::PerformDrop() {
- DCHECK(target_current_context());
-
- int drag_operation = ui::DragDropTypes::DRAG_NONE;
- if (target_window_) {
- aura::client::DragDropDelegate* delegate =
- aura::client::GetDragDropDelegate(target_window_);
- if (delegate) {
- auto data(std::make_unique<ui::OSExchangeData>(
- std::make_unique<ui::OSExchangeDataProviderX11>(
- xwindow(), target_current_context()->fetched_targets())));
-
- ui::DropTargetEvent drop_event(
- *data.get(), gfx::PointF(target_window_location_),
- gfx::PointF(target_window_root_location_),
- target_current_context()->GetDragOperation());
- if (target_current_context()->source_client()) {
- drop_event.set_flags(target_current_context()
- ->source_client()
- ->current_modifier_state());
- } else {
- drop_event.set_flags(ui::XGetMaskAsEventFlags());
- }
-
- drag_operation = delegate->OnPerformDrop(drop_event, std::move(data));
- }
-
- target_window_->RemoveObserver(this);
- target_window_ = nullptr;
- }
- return drag_operation;
-}
-
-void DesktopDragDropClientAuraX11::EndDragLoop() {
- move_loop_->EndMoveLoop();
-}
-
-} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h
deleted file mode 100644
index b8f6cceafbf..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_
-#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_
-
-#include <memory>
-#include <set>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "ui/aura/client/drag_drop_client.h"
-#include "ui/aura/window_observer.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/x/x11_drag_drop_client.h"
-#include "ui/base/x/x11_move_loop_delegate.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/platform/x11/x11_event_source.h"
-#include "ui/events/x/x11_window_event_manager.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/x/event.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/views/views_export.h"
-
-namespace aura {
-namespace client {
-class DragDropClientObserver;
-class DragDropDelegate;
-} // namespace client
-} // namespace aura
-
-namespace gfx {
-class Point;
-}
-
-namespace ui {
-class DropTargetEvent;
-class OSExchangeData;
-class XTopmostWindowFinder;
-class X11MoveLoop;
-} // namespace ui
-
-namespace views {
-class DesktopNativeCursorManager;
-class Widget;
-
-// Implements drag and drop on X11 for aura. On one side, this class takes raw
-// X11 events forwarded from DesktopWindowTreeHostLinux, while on the other, it
-// handles the views drag events.
-class VIEWS_EXPORT DesktopDragDropClientAuraX11
- : public ui::XDragDropClient,
- public ui::XDragDropClient::Delegate,
- public aura::client::DragDropClient,
- public ui::XEventDispatcher,
- public aura::WindowObserver,
- public ui::X11MoveLoopDelegate {
- public:
- DesktopDragDropClientAuraX11(
- aura::Window* root_window,
- views::DesktopNativeCursorManager* cursor_manager,
- x11::Window xwindow);
- ~DesktopDragDropClientAuraX11() override;
-
- void Init();
-
- // aura::client::DragDropClient:
- int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data,
- aura::Window* root_window,
- aura::Window* source_window,
- const gfx::Point& screen_location,
- int operation,
- ui::DragDropTypes::DragEventSource source) override;
- void DragCancel() override;
- bool IsDragDropInProgress() override;
- void AddObserver(aura::client::DragDropClientObserver* observer) override;
- void RemoveObserver(aura::client::DragDropClientObserver* observer) override;
-
- // XEventDispatcher:
- bool DispatchXEvent(x11::Event* event) override;
-
- // aura::WindowObserver:
- void OnWindowDestroyed(aura::Window* window) override;
-
- // ui::X11MoveLoopDelegate:
- void OnMouseMovement(const gfx::Point& screen_point,
- int flags,
- base::TimeTicks event_time) override;
- void OnMouseReleased() override;
- void OnMoveLoopEnded() override;
-
- protected:
- // Getter for tests.
- Widget* drag_widget() { return drag_widget_.get(); }
-
- // Creates a move loop. Virtual for testing.
- virtual std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop(
- X11MoveLoopDelegate* delegate);
-
- private:
- // When we receive a position X11 message, we need to translate that into
- // the underlying aura::Window representation, as moves internal to the X11
- // window can cause internal drag leave and enter messages.
- void DragTranslate(const gfx::Point& root_window_location,
- std::unique_ptr<ui::OSExchangeData>* data,
- std::unique_ptr<ui::DropTargetEvent>* event,
- aura::client::DragDropDelegate** delegate);
-
- // Notifies |target_window_|'s drag delegate that we're no longer dragging,
- // then unsubscribes |target_window_| from ourselves and forgets it.
- void NotifyDragLeave();
-
- // ui::XDragDropClient::Delegate
- std::unique_ptr<ui::XTopmostWindowFinder> CreateWindowFinder() override;
- int UpdateDrag(const gfx::Point& screen_point) override;
- void UpdateCursor(
- ui::DragDropTypes::DragOperation negotiated_operation) override;
- void OnBeginForeignDrag(x11::Window window) override;
- void OnEndForeignDrag() override;
- void OnBeforeDragLeave() override;
- int PerformDrop() override;
- void EndDragLoop() override;
-
- // A nested run loop that notifies this object of events through the
- // ui::X11MoveLoopDelegate interface.
- std::unique_ptr<ui::X11MoveLoop> move_loop_;
-
- aura::Window* root_window_;
-
- DesktopNativeCursorManager* cursor_manager_;
-
- // Events that we have selected on |source_window_|.
- std::unique_ptr<ui::XScopedEventSelector> source_window_events_;
-
- // The Aura window that is currently under the cursor. We need to manually
- // keep track of this because Windows will only call our drag enter method
- // once when the user enters the associated X Window. But inside that X
- // Window there could be multiple aura windows, so we need to generate drag
- // enter events for them.
- aura::Window* target_window_ = nullptr;
-
- // Because Xdnd messages don't contain the position in messages other than
- // the XdndPosition message, we must manually keep track of the last position
- // change.
- gfx::Point target_window_location_;
- gfx::Point target_window_root_location_;
-
- // The current drag-drop client that has an active operation. Since we have
- // multiple root windows and multiple DesktopDragDropClientAuraX11 instances
- // it is important to maintain only one drag and drop operation at any time.
- static DesktopDragDropClientAuraX11* g_current_drag_drop_client;
-
- // Widget that the user drags around. May be nullptr.
- std::unique_ptr<Widget> drag_widget_;
-
- // The size of drag image.
- gfx::Size drag_image_size_;
-
- // The offset of |drag_widget_| relative to the mouse position.
- gfx::Vector2d drag_widget_offset_;
-
- base::WeakPtrFactory<DesktopDragDropClientAuraX11> weak_ptr_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
deleted file mode 100644
index 1ab88c73c14..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
+++ /dev/null
@@ -1,663 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
-
-#include <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "ui/aura/client/drag_drop_delegate.h"
-#include "ui/aura/test/test_screen.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/x/x11_move_loop.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/events/event_utils.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_atom_cache.h"
-#include "ui/gfx/x/x11_types.h"
-#include "ui/gfx/x/xproto.h"
-#include "ui/views/test/views_test_base.h"
-#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
-#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
-#include "ui/views/widget/widget.h"
-
-// TODO(crbug.com/990756): Transfer all tests from this file to better places
-// when DDDClientAuraX11 goes away.
-
-namespace views {
-
-namespace {
-
-class TestDragDropClient;
-
-// Collects messages which would otherwise be sent to |window_| via
-// SendXClientEvent().
-class ClientMessageEventCollector {
- public:
- ClientMessageEventCollector(x11::Window window, TestDragDropClient* client);
- virtual ~ClientMessageEventCollector();
-
- // Returns true if |events_| is non-empty.
- bool HasEvents() const { return !events_.empty(); }
-
- // Pops all of |events_| and returns the popped events in the order that they
- // were on the stack
- std::vector<x11::ClientMessageEvent> PopAllEvents();
-
- // Adds |event| to the stack.
- void RecordEvent(const x11::ClientMessageEvent& event);
-
- private:
- x11::Window window_;
-
- // Not owned.
- TestDragDropClient* client_;
-
- std::vector<x11::ClientMessageEvent> events_;
-
- DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector);
-};
-
-// An implementation of ui::X11MoveLoop where RunMoveLoop() always starts the
-// move loop.
-class TestMoveLoop : public ui::X11MoveLoop {
- public:
- explicit TestMoveLoop(ui::X11MoveLoopDelegate* delegate);
- ~TestMoveLoop() override;
-
- // Returns true if the move loop is running.
- bool IsRunning() const;
-
- // ui::X11MoveLoop:
- bool RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) override;
- void UpdateCursor(::Cursor cursor) override;
- void EndMoveLoop() override;
-
- private:
- // Not owned.
- ui::X11MoveLoopDelegate* delegate_;
-
- // Ends the move loop.
- base::OnceClosure quit_closure_;
-
- bool is_running_ = false;
-};
-
-// Implementation of DesktopDragDropClientAuraX11 which short circuits
-// FindWindowFor().
-class SimpleTestDragDropClient : public DesktopDragDropClientAuraX11 {
- public:
- SimpleTestDragDropClient(aura::Window*,
- DesktopNativeCursorManager* cursor_manager);
- ~SimpleTestDragDropClient() override;
-
- // Sets |window| as the topmost window for all mouse positions.
- void SetTopmostXWindow(x11::Window window);
-
- // Returns true if the move loop is running.
- bool IsMoveLoopRunning();
-
- Widget* drag_widget() { return DesktopDragDropClientAuraX11::drag_widget(); }
-
- private:
- // DesktopDragDropClientAuraX11:
- std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop(
- ui::X11MoveLoopDelegate* delegate) override;
- x11::Window FindWindowFor(const gfx::Point& screen_point) override;
-
- // The x11::Window of the window which is simulated to be the topmost window.
- x11::Window target_window_ = x11::Window::None;
-
- // The move loop. Not owned.
- TestMoveLoop* loop_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(SimpleTestDragDropClient);
-};
-
-// Implementation of DesktopDragDropClientAuraX11 which works with a fake
-// |DesktopDragDropClientAuraX11::source_current_window_|.
-class TestDragDropClient : public SimpleTestDragDropClient {
- public:
- // The location in screen coordinates used for the synthetic mouse moves
- // generated in SetTopmostXWindowAndMoveMouse().
- static constexpr int kMouseMoveX = 100;
- static constexpr int kMouseMoveY = 200;
-
- TestDragDropClient(aura::Window* window,
- DesktopNativeCursorManager* cursor_manager);
- ~TestDragDropClient() override;
-
- // Returns the x11::Window of the window which initiated the drag.
- x11::Window source_xwindow() { return source_window_; }
-
- // Returns the Atom with |name|.
- x11::Atom GetAtom(const char* name);
-
- // Returns true if the event's message has |type|.
- bool MessageHasType(const x11::ClientMessageEvent& event, const char* type);
-
- // Sets |collector| to collect x11::ClientMessageEvents which would otherwise
- // have been sent to the drop target window.
- void SetEventCollectorFor(x11::Window window,
- ClientMessageEventCollector* collector);
-
- // Builds an XdndStatus message and sends it to
- // DesktopDragDropClientAuraX11.
- void OnStatus(x11::Window target_window,
- bool will_accept_drop,
- x11::Atom accepted_action);
-
- // Builds an XdndFinished message and sends it to
- // DesktopDragDropClientAuraX11.
- void OnFinished(x11::Window target_window,
- bool accepted_drop,
- x11::Atom performed_action);
-
- // Sets |window| as the topmost window at the current mouse position and
- // generates a synthetic mouse move.
- void SetTopmostXWindowAndMoveMouse(x11::Window window);
-
- private:
- // DesktopDragDropClientAuraX11:
- void SendXClientEvent(x11::Window window,
- const x11::ClientMessageEvent& event) override;
-
- // The x11::Window of the window which initiated the drag.
- x11::Window source_window_;
-
- // Map of x11::Windows to the collector which intercepts
- // x11::ClientMessageEvents for that window.
- std::map<x11::Window, ClientMessageEventCollector*> collectors_;
-
- DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// ClientMessageEventCollector
-
-ClientMessageEventCollector::ClientMessageEventCollector(
- x11::Window window,
- TestDragDropClient* client)
- : window_(window), client_(client) {
- client->SetEventCollectorFor(window, this);
-}
-
-ClientMessageEventCollector::~ClientMessageEventCollector() {
- client_->SetEventCollectorFor(window_, nullptr);
-}
-
-std::vector<x11::ClientMessageEvent>
-ClientMessageEventCollector::PopAllEvents() {
- std::vector<x11::ClientMessageEvent> to_return;
- to_return.swap(events_);
- return to_return;
-}
-
-void ClientMessageEventCollector::RecordEvent(
- const x11::ClientMessageEvent& event) {
- events_.push_back(event);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TestMoveLoop
-
-TestMoveLoop::TestMoveLoop(ui::X11MoveLoopDelegate* delegate)
- : delegate_(delegate) {}
-
-TestMoveLoop::~TestMoveLoop() = default;
-
-bool TestMoveLoop::IsRunning() const {
- return is_running_;
-}
-
-bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) {
- is_running_ = true;
- base::RunLoop run_loop;
- quit_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- return true;
-}
-
-void TestMoveLoop::UpdateCursor(::Cursor cursor) {}
-
-void TestMoveLoop::EndMoveLoop() {
- if (is_running_) {
- delegate_->OnMoveLoopEnded();
- is_running_ = false;
- std::move(quit_closure_).Run();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// SimpleTestDragDropClient
-
-SimpleTestDragDropClient::SimpleTestDragDropClient(
- aura::Window* window,
- DesktopNativeCursorManager* cursor_manager)
- : DesktopDragDropClientAuraX11(window,
- cursor_manager,
- window->GetHost()->GetAcceleratedWidget()) {}
-
-SimpleTestDragDropClient::~SimpleTestDragDropClient() = default;
-
-void SimpleTestDragDropClient::SetTopmostXWindow(x11::Window window) {
- target_window_ = window;
-}
-
-bool SimpleTestDragDropClient::IsMoveLoopRunning() {
- return loop_->IsRunning();
-}
-
-std::unique_ptr<ui::X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop(
- ui::X11MoveLoopDelegate* delegate) {
- loop_ = new TestMoveLoop(delegate);
- return base::WrapUnique(loop_);
-}
-
-x11::Window SimpleTestDragDropClient::FindWindowFor(
- const gfx::Point& screen_point) {
- return target_window_;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TestDragDropClient
-
-TestDragDropClient::TestDragDropClient(
- aura::Window* window,
- DesktopNativeCursorManager* cursor_manager)
- : SimpleTestDragDropClient(window, cursor_manager),
- source_window_(window->GetHost()->GetAcceleratedWidget()) {}
-
-TestDragDropClient::~TestDragDropClient() = default;
-
-x11::Atom TestDragDropClient::GetAtom(const char* name) {
- return gfx::GetAtom(name);
-}
-
-bool TestDragDropClient::MessageHasType(const x11::ClientMessageEvent& event,
- const char* type) {
- return event.type == GetAtom(type);
-}
-
-void TestDragDropClient::SetEventCollectorFor(
- x11::Window window,
- ClientMessageEventCollector* collector) {
- if (collector)
- collectors_[window] = collector;
- else
- collectors_.erase(window);
-}
-
-void TestDragDropClient::OnStatus(x11::Window target_window,
- bool will_accept_drop,
- x11::Atom accepted_action) {
- x11::ClientMessageEvent event;
- event.type = GetAtom("XdndStatus");
- event.format = 32;
- event.window = source_window_;
- event.data.data32[0] = static_cast<uint32_t>(target_window);
- event.data.data32[1] = will_accept_drop ? 1 : 0;
- event.data.data32[2] = 0;
- event.data.data32[3] = 0;
- event.data.data32[4] = static_cast<uint32_t>(accepted_action);
- HandleXdndEvent(event);
-}
-
-void TestDragDropClient::OnFinished(x11::Window target_window,
- bool accepted_drop,
- x11::Atom performed_action) {
- x11::ClientMessageEvent event;
- event.type = GetAtom("XdndFinished");
- event.format = 32;
- event.window = source_window_;
- event.data.data32[0] = static_cast<uint32_t>(target_window);
- event.data.data32[1] = accepted_drop ? 1 : 0;
- event.data.data32[2] = static_cast<uint32_t>(performed_action);
- event.data.data32[3] = 0;
- event.data.data32[4] = 0;
- HandleXdndEvent(event);
-}
-
-void TestDragDropClient::SetTopmostXWindowAndMoveMouse(x11::Window window) {
- SetTopmostXWindow(window);
- OnMouseMovement(gfx::Point(kMouseMoveX, kMouseMoveY), ui::EF_NONE,
- ui::EventTimeForNow());
-}
-
-void TestDragDropClient::SendXClientEvent(
- x11::Window window,
- const x11::ClientMessageEvent& event) {
- auto it = collectors_.find(window);
- if (it != collectors_.end())
- it->second->RecordEvent(event);
-}
-
-} // namespace
-
-class DesktopDragDropClientAuraX11Test : public ViewsTestBase {
- public:
- DesktopDragDropClientAuraX11Test() = default;
- ~DesktopDragDropClientAuraX11Test() override = default;
-
- int StartDragAndDrop() {
- auto data(std::make_unique<ui::OSExchangeData>());
- data->SetString(base::ASCIIToUTF16("Test"));
- SkBitmap drag_bitmap;
- drag_bitmap.allocN32Pixels(10, 10);
- drag_bitmap.eraseARGB(0xFF, 0, 0, 0);
- gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap));
- data->provider().SetDragImage(drag_image, gfx::Vector2d());
-
- return client_->StartDragAndDrop(
- std::move(data), widget_->GetNativeWindow()->GetRootWindow(),
- widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
- }
-
- // ViewsTestBase:
- void SetUp() override {
- set_native_widget_type(NativeWidgetType::kDesktop);
-
- ViewsTestBase::SetUp();
-
- // Create widget to initiate the drags.
- widget_ = std::make_unique<Widget>();
- Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.bounds = gfx::Rect(100, 100);
- widget_->Init(std::move(params));
- widget_->Show();
-
- cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
-
- client_ = std::make_unique<TestDragDropClient>(widget_->GetNativeWindow(),
- cursor_manager_.get());
- client_->Init();
- }
-
- void TearDown() override {
- client_.reset();
- cursor_manager_.reset();
- widget_.reset();
- ViewsTestBase::TearDown();
- }
-
- TestDragDropClient* client() { return client_.get(); }
-
- private:
- std::unique_ptr<TestDragDropClient> client_;
- std::unique_ptr<DesktopNativeCursorManager> cursor_manager_;
-
- // The widget used to initiate drags.
- std::unique_ptr<Widget> widget_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test);
-};
-
-void HighDPIStep(TestDragDropClient* client) {
- float scale =
- display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
- // Start dragging at 100, 100 in native coordinates.
- gfx::Point mouse_position_in_screen_pixel(100, 100);
- client->OnMouseMovement(mouse_position_in_screen_pixel, 0,
- ui::EventTimeForNow());
-
- EXPECT_EQ(gfx::ScaleToFlooredPoint(gfx::Point(100, 100), 1.f / scale),
- client->drag_widget()->GetWindowBoundsInScreen().origin());
-
- // Drag the mouse down 200 pixels.
- mouse_position_in_screen_pixel.Offset(200, 0);
- client->OnMouseMovement(mouse_position_in_screen_pixel, 0,
- ui::EventTimeForNow());
- EXPECT_EQ(gfx::ScaleToFlooredPoint(gfx::Point(300, 100), 1.f / scale),
- client->drag_widget()->GetWindowBoundsInScreen().origin());
-
- client->OnMouseReleased();
-}
-
-// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
-// or its equivalent.
-TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) {
- aura::TestScreen* screen =
- static_cast<aura::TestScreen*>(display::Screen::GetScreen());
- screen->SetDeviceScaleFactor(2.0f);
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&HighDPIStep, client()));
- int result = StartDragAndDrop();
- EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
-}
-
-// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
-// or its equivalent.
-TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) {
- aura::TestScreen* screen =
- static_cast<aura::TestScreen*>(display::Screen::GetScreen());
- screen->SetDeviceScaleFactor(1.5f);
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&HighDPIStep, client()));
- int result = StartDragAndDrop();
- EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
-}
-
-namespace {
-
-// DragDropDelegate which counts the number of each type of drag-drop event and
-// keeps track of the most recent drag-drop event.
-class TestDragDropDelegate : public aura::client::DragDropDelegate {
- public:
- TestDragDropDelegate() = default;
- ~TestDragDropDelegate() override = default;
-
- int num_enters() const { return num_enters_; }
- int num_updates() const { return num_updates_; }
- int num_exits() const { return num_exits_; }
- int num_drops() const { return num_drops_; }
- gfx::Point last_event_mouse_position() const {
- return last_event_mouse_position_;
- }
- int last_event_flags() const { return last_event_flags_; }
-
- private:
- // aura::client::DragDropDelegate:
- void OnDragEntered(const ui::DropTargetEvent& event) override {
- ++num_enters_;
- last_event_mouse_position_ = event.location();
- last_event_flags_ = event.flags();
- }
-
- int OnDragUpdated(const ui::DropTargetEvent& event) override {
- ++num_updates_;
- last_event_mouse_position_ = event.location();
- last_event_flags_ = event.flags();
- return ui::DragDropTypes::DRAG_COPY;
- }
-
- void OnDragExited() override { ++num_exits_; }
-
- int OnPerformDrop(const ui::DropTargetEvent& event,
- std::unique_ptr<OSExchangeData> data) override {
- ++num_drops_;
- last_event_mouse_position_ = event.location();
- last_event_flags_ = event.flags();
- return ui::DragDropTypes::DRAG_COPY;
- }
-
- int num_enters_ = 0;
- int num_updates_ = 0;
- int num_exits_ = 0;
- int num_drops_ = 0;
-
- gfx::Point last_event_mouse_position_;
- int last_event_flags_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate);
-};
-
-} // namespace
-
-// Test harness for tests where the drag and drop source and target are both
-// Chrome windows.
-class DesktopDragDropClientAuraX11ChromeSourceTargetTest
- : public ViewsTestBase {
- public:
- DesktopDragDropClientAuraX11ChromeSourceTargetTest() = default;
-
- ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override = default;
-
- int StartDragAndDrop() {
- auto data(std::make_unique<ui::OSExchangeData>());
- data->SetString(base::ASCIIToUTF16("Test"));
-
- return client_->StartDragAndDrop(
- std::move(data), widget_->GetNativeWindow()->GetRootWindow(),
- widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
- }
-
- // ViewsTestBase:
- void SetUp() override {
- ViewsTestBase::SetUp();
-
- // Create widget to initiate the drags.
- widget_ = std::make_unique<Widget>();
- Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.native_widget = new DesktopNativeWidgetAura(widget_.get());
- params.bounds = gfx::Rect(100, 100);
- widget_->Init(std::move(params));
- widget_->Show();
-
- cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
-
- client_ = std::make_unique<SimpleTestDragDropClient>(
- widget_->GetNativeWindow(), cursor_manager_.get());
- client_->Init();
- }
-
- void TearDown() override {
- client_.reset();
- cursor_manager_.reset();
- widget_.reset();
- ViewsTestBase::TearDown();
- }
-
- SimpleTestDragDropClient* client() { return client_.get(); }
-
- private:
- std::unique_ptr<SimpleTestDragDropClient> client_;
- std::unique_ptr<DesktopNativeCursorManager> cursor_manager_;
-
- // The widget used to initiate drags.
- std::unique_ptr<Widget> widget_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11ChromeSourceTargetTest);
-};
-
-namespace {
-
-void ChromeSourceTargetStep2(SimpleTestDragDropClient* client,
- int modifier_flags) {
- EXPECT_TRUE(client->IsMoveLoopRunning());
-
- std::unique_ptr<Widget> target_widget(new Widget);
- Widget::InitParams target_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- target_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- target_params.native_widget =
- new DesktopNativeWidgetAura(target_widget.get());
- target_params.bounds = gfx::Rect(100, 100);
- target_widget->Init(std::move(target_params));
- target_widget->Show();
-
- std::unique_ptr<TestDragDropDelegate> delegate(new TestDragDropDelegate);
- aura::client::SetDragDropDelegate(target_widget->GetNativeWindow(),
- delegate.get());
-
- client->SetTopmostXWindow(
- target_widget->GetNativeView()->GetHost()->GetAcceleratedWidget());
-
- gfx::Rect target_widget_bounds_in_screen =
- target_widget->GetWindowBoundsInScreen();
- gfx::Point point1_in_screen = target_widget_bounds_in_screen.CenterPoint();
- gfx::Point point1_in_target_widget(
- target_widget_bounds_in_screen.width() / 2,
- target_widget_bounds_in_screen.height() / 2);
- gfx::Point point2_in_screen = point1_in_screen + gfx::Vector2d(1, 0);
- gfx::Point point2_in_target_widget =
- point1_in_target_widget + gfx::Vector2d(1, 0);
-
- client->OnMouseMovement(point1_in_screen, modifier_flags,
- ui::EventTimeForNow());
- EXPECT_EQ(1, delegate->num_enters());
- EXPECT_EQ(1, delegate->num_updates());
- EXPECT_EQ(0, delegate->num_exits());
- EXPECT_EQ(0, delegate->num_drops());
- EXPECT_EQ(point1_in_target_widget.ToString(),
- delegate->last_event_mouse_position().ToString());
- EXPECT_EQ(modifier_flags, delegate->last_event_flags());
-
- client->OnMouseMovement(point2_in_screen, modifier_flags,
- ui::EventTimeForNow());
- EXPECT_EQ(1, delegate->num_enters());
- EXPECT_EQ(2, delegate->num_updates());
- EXPECT_EQ(0, delegate->num_exits());
- EXPECT_EQ(0, delegate->num_drops());
- EXPECT_EQ(point2_in_target_widget.ToString(),
- delegate->last_event_mouse_position().ToString());
- EXPECT_EQ(modifier_flags, delegate->last_event_flags());
-
- client->OnMouseReleased();
- EXPECT_EQ(1, delegate->num_enters());
- EXPECT_EQ(2, delegate->num_updates());
- EXPECT_EQ(0, delegate->num_exits());
- EXPECT_EQ(1, delegate->num_drops());
- EXPECT_EQ(point2_in_target_widget.ToString(),
- delegate->last_event_mouse_position().ToString());
- EXPECT_EQ(modifier_flags, delegate->last_event_flags());
-
- EXPECT_FALSE(client->IsMoveLoopRunning());
-}
-
-} // namespace
-
-// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
-// or its equivalent.
-TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&ChromeSourceTargetStep2, client(), ui::EF_NONE));
- int result = StartDragAndDrop();
- EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
-}
-
-// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
-// or its equivalent.
-// Test that if 'Ctrl' is pressed during a drag and drop operation, that
-// the aura::client::DragDropDelegate is properly notified.
-TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, CtrlPressed) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&ChromeSourceTargetStep2, client(), ui::EF_CONTROL_DOWN));
- int result = StartDragAndDrop();
- EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
-}
-
-} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
index dd9232bb7e9..80b7084faaa 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
@@ -22,9 +22,10 @@
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drop_target_event.h"
#include "ui/base/layout.h"
+#include "ui/base/ui_base_features.h"
#include "ui/display/screen.h"
+#include "ui/ozone/public/ozone_platform.h"
#include "ui/platform_window/platform_window_delegate.h"
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
#include "ui/views/widget/widget.h"
@@ -43,6 +44,17 @@ aura::Window* GetTargetWindow(aura::Window* root_window,
// The minimum alpha required so we would treat the pixel as visible.
constexpr uint32_t kMinAlpha = 32;
+bool DragImageIsNeeded() {
+#if defined(USE_OZONE)
+ if (features::IsUsingOzonePlatform()) {
+ return !ui::OzonePlatform::GetInstance()
+ ->GetPlatformProperties()
+ .platform_shows_drag_image;
+ }
+#endif
+ return true;
+}
+
// Returns true if |image| has any visible regions (defined as having a pixel
// with alpha > |kMinAlpha|).
bool IsValidDragImage(const gfx::ImageSkia& image) {
@@ -107,10 +119,7 @@ DesktopDragDropClientOzone::DesktopDragDropClientOzone(
drag_handler_(drag_handler) {}
DesktopDragDropClientOzone::~DesktopDragDropClientOzone() {
- ResetDragDropTarget();
-
- if (IsDragDropInProgress())
- DragCancel();
+ ResetDragDropTarget(true);
}
int DesktopDragDropClientOzone::StartDragAndDrop(
@@ -119,16 +128,13 @@ int DesktopDragDropClientOzone::StartDragAndDrop(
aura::Window* source_window,
const gfx::Point& root_location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
if (!drag_handler_)
return ui::DragDropTypes::DragOperation::DRAG_NONE;
DCHECK(!drag_context_);
drag_context_ = std::make_unique<DragContext>();
- base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
- drag_context_->quit_closure = run_loop.QuitClosure();
-
// Chrome expects starting drag and drop to release capture.
aura::Window* capture_window =
aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
@@ -145,13 +151,15 @@ int DesktopDragDropClientOzone::StartDragAndDrop(
ui::mojom::CursorType::kGrabbing));
}
- const auto& provider = data->provider();
- gfx::ImageSkia drag_image = provider.GetDragImage();
- if (IsValidDragImage(drag_image)) {
- drag_context_->size = drag_image.size();
- drag_context_->offset = provider.GetDragImageOffset();
- drag_context_->widget =
- CreateDragWidget(root_location, drag_image, drag_context_->offset);
+ if (DragImageIsNeeded()) {
+ const auto& provider = data->provider();
+ gfx::ImageSkia drag_image = provider.GetDragImage();
+ if (IsValidDragImage(drag_image)) {
+ drag_context_->size = drag_image.size();
+ drag_context_->offset = provider.GetDragImageOffset();
+ drag_context_->widget =
+ CreateDragWidget(root_location, drag_image, drag_context_->offset);
+ }
}
// This object is owned by a DesktopNativeWidgetAura that can be destroyed
@@ -159,13 +167,16 @@ int DesktopDragDropClientOzone::StartDragAndDrop(
// of whether we are still alive after the drag ends.
auto alive = weak_factory_.GetWeakPtr();
- drag_handler_->StartDrag(*data.get(), operation, cursor_client->GetCursor(),
- this);
- run_loop.Run();
+ const bool drag_succeeded = drag_handler_->StartDrag(
+ *data.get(), operation, cursor_client->GetCursor(),
+ !source_window->HasCapture(), this);
if (!alive)
return ui::DragDropTypes::DRAG_NONE;
+ if (!drag_succeeded)
+ drag_operation_ = ui::DragDropTypes::DRAG_NONE;
+
if (cursor_client)
cursor_client->SetCursor(initial_cursor);
drag_context_.reset();
@@ -174,11 +185,15 @@ int DesktopDragDropClientOzone::StartDragAndDrop(
}
void DesktopDragDropClientOzone::DragCancel() {
- QuitRunLoop();
+ if (!drag_handler_)
+ return;
+
+ drag_handler_->CancelDrag();
+ drag_operation_ = ui::DragDropTypes::DRAG_NONE;
}
bool DesktopDragDropClientOzone::IsDragDropInProgress() {
- return bool(drag_context_) && bool(drag_context_->quit_closure);
+ return drag_context_.get();
}
void DesktopDragDropClientOzone::AddObserver(
@@ -194,9 +209,10 @@ void DesktopDragDropClientOzone::RemoveObserver(
void DesktopDragDropClientOzone::OnDragEnter(
const gfx::PointF& point,
std::unique_ptr<ui::OSExchangeData> data,
- int operation) {
+ int operation,
+ int modifiers) {
last_drag_point_ = point;
- drag_operation_ = operation;
+ last_drop_operation_ = operation;
// If |data| is empty, we defer sending any events to the
// |drag_drop_delegate_|. All necessary events will be sent on dropping.
@@ -204,13 +220,14 @@ void DesktopDragDropClientOzone::OnDragEnter(
return;
data_to_drop_ = std::move(data);
- UpdateTargetAndCreateDropEvent(point);
+ UpdateTargetAndCreateDropEvent(point, modifiers);
}
int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point,
- int operation) {
+ int operation,
+ int modifiers) {
last_drag_point_ = point;
- drag_operation_ = operation;
+ last_drop_operation_ = operation;
// If |data_to_drop_| doesn't have data, return that we accept everything.
if (!data_to_drop_)
@@ -219,14 +236,15 @@ int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point,
// Ask the delegate what operation it would accept for the current data.
int client_operation = ui::DragDropTypes::DRAG_NONE;
std::unique_ptr<ui::DropTargetEvent> event =
- UpdateTargetAndCreateDropEvent(point);
+ UpdateTargetAndCreateDropEvent(point, modifiers);
if (drag_drop_delegate_ && event)
client_operation = drag_drop_delegate_->OnDragUpdated(*event);
return client_operation;
}
void DesktopDragDropClientOzone::OnDragDrop(
- std::unique_ptr<ui::OSExchangeData> data) {
+ std::unique_ptr<ui::OSExchangeData> data,
+ int modifiers) {
// If we didn't have |data_to_drop_|, then |drag_drop_delegate_| had never
// been updated, and now it needs to receive deferred enter and update events
// before handling the actual drop.
@@ -239,7 +257,7 @@ void DesktopDragDropClientOzone::OnDragDrop(
data_to_drop_ = std::move(data);
// This will call the delegate's OnDragEntered if needed.
- auto event = UpdateTargetAndCreateDropEvent(last_drag_point_);
+ auto event = UpdateTargetAndCreateDropEvent(last_drag_point_, modifiers);
if (drag_drop_delegate_ && event) {
if (posponed_enter_and_update) {
// TODO(https://crbug.com/1014860): deal with drop refusals.
@@ -251,15 +269,14 @@ void DesktopDragDropClientOzone::OnDragDrop(
// and quit?
drag_drop_delegate_->OnDragUpdated(*event);
}
- drag_operation_ =
- drag_drop_delegate_->OnPerformDrop(*event, std::move(data_to_drop_));
+ drag_drop_delegate_->OnPerformDrop(*event, std::move(data_to_drop_));
}
- ResetDragDropTarget();
+ ResetDragDropTarget(false);
}
void DesktopDragDropClientOzone::OnDragLeave() {
data_to_drop_.reset();
- ResetDragDropTarget();
+ ResetDragDropTarget(true);
}
void DesktopDragDropClientOzone::OnWindowDestroyed(aura::Window* window) {
@@ -317,29 +334,23 @@ void DesktopDragDropClientOzone::OnDragOperationChanged(
void DesktopDragDropClientOzone::OnDragFinished(int dnd_action) {
drag_operation_ = dnd_action;
- QuitRunLoop();
-}
-
-void DesktopDragDropClientOzone::QuitRunLoop() {
- if (!drag_context_->quit_closure)
- return;
- std::move(drag_context_->quit_closure).Run();
}
std::unique_ptr<ui::DropTargetEvent>
DesktopDragDropClientOzone::UpdateTargetAndCreateDropEvent(
- const gfx::PointF& location) {
+ const gfx::PointF& location,
+ int modifiers) {
const gfx::Point point(location.x(), location.y());
aura::Window* window = GetTargetWindow(root_window_, point);
if (!window) {
- ResetDragDropTarget();
+ ResetDragDropTarget(true);
return nullptr;
}
auto* new_delegate = aura::client::GetDragDropDelegate(window);
const bool delegate_has_changed = (new_delegate != drag_drop_delegate_);
if (delegate_has_changed) {
- ResetDragDropTarget();
+ ResetDragDropTarget(true);
drag_drop_delegate_ = new_delegate;
current_window_ = window;
current_window_->AddObserver(this);
@@ -355,7 +366,8 @@ DesktopDragDropClientOzone::UpdateTargetAndCreateDropEvent(
auto event = std::make_unique<ui::DropTargetEvent>(
*data_to_drop_, target_location, gfx::PointF(root_location),
- drag_operation_);
+ last_drop_operation_);
+ event->set_flags(modifiers);
if (delegate_has_changed)
drag_drop_delegate_->OnDragEntered(*event);
return event;
@@ -376,9 +388,10 @@ void DesktopDragDropClientOzone::UpdateDragWidgetLocation() {
drag_context_->last_screen_location_px.reset();
}
-void DesktopDragDropClientOzone::ResetDragDropTarget() {
+void DesktopDragDropClientOzone::ResetDragDropTarget(bool send_exit) {
if (drag_drop_delegate_) {
- drag_drop_delegate_->OnDragExited();
+ if (send_exit)
+ drag_drop_delegate_->OnDragExited();
drag_drop_delegate_ = nullptr;
}
if (current_window_) {
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
index d4e93838750..e2420a3ad5f 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h
@@ -15,8 +15,8 @@
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
#include "ui/views/views_export.h"
namespace aura {
@@ -52,9 +52,6 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
DragContext();
~DragContext();
- // Exits the drag loop.
- base::OnceClosure quit_closure;
-
// Widget that the user drags around. May be nullptr.
std::unique_ptr<Widget> widget;
@@ -80,7 +77,7 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
aura::Window* source_window,
const gfx::Point& root_location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
void AddObserver(aura::client::DragDropClientObserver* observer) override;
@@ -89,9 +86,13 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
// ui::WmDropHandler
void OnDragEnter(const gfx::PointF& point,
std::unique_ptr<ui::OSExchangeData> data,
- int operation) override;
- int OnDragMotion(const gfx::PointF& point, int operation) override;
- void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override;
+ int operation,
+ int modifiers) override;
+ int OnDragMotion(const gfx::PointF& point,
+ int operation,
+ int modifiers) override;
+ void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data,
+ int modifiers) override;
void OnDragLeave() override;
// aura::WindowObserver
@@ -103,15 +104,14 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
ui::DragDropTypes::DragOperation operation) override;
void OnDragFinished(int operation) override;
- void QuitRunLoop();
-
// Returns a DropTargetEvent to be passed to the DragDropDelegate.
// Updates the delegate if needed, which in its turn calls their
// OnDragExited/OnDragEntered, so after getting the event the delegate
// is ready to accept OnDragUpdated or OnPerformDrop. Returns nullptr if
// drop is not possible.
std::unique_ptr<ui::DropTargetEvent> UpdateTargetAndCreateDropEvent(
- const gfx::PointF& point);
+ const gfx::PointF& point,
+ int modifiers);
// Updates |drag_drop_delegate_| along with |window|.
void UpdateDragDropDelegate(aura::Window* window);
@@ -119,8 +119,10 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
// Updates |drag_widget_| so it is aligned with the last drag location.
void UpdateDragWidgetLocation();
- // Resets |drag_drop_delegate_|. Calls OnDragExited() before resetting.
- void ResetDragDropTarget();
+ // Resets |drag_drop_delegate_|.
+ // |send_exit| controls whether to call delegate's OnDragExited() before
+ // resetting.
+ void ResetDragDropTarget(bool send_exit);
aura::Window* const root_window_;
@@ -139,6 +141,9 @@ class VIEWS_EXPORT DesktopDragDropClientOzone
// The most recent native coordinates of an incoming drag. Updated while
// the mouse is moved, and used at dropping.
gfx::PointF last_drag_point_;
+ // The most recent drop operation. Updated while the mouse is moved, and
+ // used at dropping.
+ int last_drop_operation_ = 0;
// The operation bitfield.
int drag_operation_ = 0;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
index cfb7bcca00b..90c68e1d12d 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
@@ -12,10 +12,11 @@
#include "base/threading/thread_task_runner_handle.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/platform_window/platform_window.h"
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/platform_window/wm/wm_drag_handler.h"
+#include "ui/platform_window/wm/wm_drop_handler.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
@@ -30,6 +31,8 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
FakePlatformWindow() { SetWmDragHandler(this, this); }
~FakePlatformWindow() override = default;
+ void set_modifiers(int modifiers) { modifiers_ = modifiers; }
+
// ui::PlatformWindow
void Show(bool inactive) override {}
void Hide() override {}
@@ -63,9 +66,10 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
void SizeConstraintsChanged() override {}
// ui::WmDragHandler
- void StartDrag(const OSExchangeData& data,
+ bool StartDrag(const OSExchangeData& data,
int operation,
gfx::NativeCursor cursor,
+ bool can_grab_pointer,
WmDragHandler::Delegate* delegate) override {
drag_handler_delegate_ = delegate;
source_data_ = std::make_unique<OSExchangeData>(data.provider().Clone());
@@ -73,15 +77,22 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
FROM_HERE,
base::BindOnce(&FakePlatformWindow::ProcessDrag, base::Unretained(this),
std::move(source_data_), operation));
+
+ base::RunLoop run_loop;
+ drag_loop_quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+ return true;
}
+ void CancelDrag() override { std::move(drag_loop_quit_closure_).Run(); }
+
void OnDragEnter(const gfx::PointF& point,
std::unique_ptr<OSExchangeData> data,
int operation) {
ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
if (!drop_handler)
return;
- drop_handler->OnDragEnter(point, std::move(data), operation);
+ drop_handler->OnDragEnter(point, std::move(data), operation, modifiers_);
}
int OnDragMotion(const gfx::PointF& point, int operation) {
@@ -89,14 +100,14 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
if (!drop_handler)
return 0;
- return drop_handler->OnDragMotion(point, operation);
+ return drop_handler->OnDragMotion(point, operation, modifiers_);
}
void OnDragDrop(std::unique_ptr<OSExchangeData> data) {
ui::WmDropHandler* drop_handler = ui::GetWmDropHandler(*this);
if (!drop_handler)
return;
- drop_handler->OnDragDrop(std::move(data));
+ drop_handler->OnDragDrop(std::move(data), modifiers_);
}
void OnDragLeave() {
@@ -108,6 +119,7 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
void CloseDrag(uint32_t dnd_action) {
drag_handler_delegate_->OnDragFinished(dnd_action);
+ std::move(drag_loop_quit_closure_).Run();
}
void ProcessDrag(std::unique_ptr<OSExchangeData> data, int operation) {
@@ -121,6 +133,8 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler {
private:
WmDragHandler::Delegate* drag_handler_delegate_ = nullptr;
std::unique_ptr<ui::OSExchangeData> source_data_;
+ base::OnceClosure drag_loop_quit_closure_;
+ int modifiers_ = 0;
DISALLOW_COPY_AND_ASSIGN(FakePlatformWindow);
};
@@ -136,6 +150,7 @@ class FakeDragDropDelegate : public aura::client::DragDropDelegate {
int num_updates() const { return num_updates_; }
int num_exits() const { return num_exits_; }
int num_drops() const { return num_drops_; }
+ int last_event_flags() const { return last_event_flags_; }
ui::OSExchangeData* received_data() const { return received_data_.get(); }
void SetOperation(int operation) { destination_operation_ = operation; }
@@ -144,10 +159,12 @@ class FakeDragDropDelegate : public aura::client::DragDropDelegate {
// aura::client::DragDropDelegate:
void OnDragEntered(const ui::DropTargetEvent& event) override {
++num_enters_;
+ last_event_flags_ = event.flags();
}
int OnDragUpdated(const ui::DropTargetEvent& event) override {
++num_updates_;
+ last_event_flags_ = event.flags();
return destination_operation_;
}
@@ -157,6 +174,7 @@ class FakeDragDropDelegate : public aura::client::DragDropDelegate {
std::unique_ptr<ui::OSExchangeData> data) override {
++num_drops_;
received_data_ = std::move(data);
+ last_event_flags_ = event.flags();
return destination_operation_;
}
@@ -166,6 +184,7 @@ class FakeDragDropDelegate : public aura::client::DragDropDelegate {
int num_drops_;
std::unique_ptr<ui::OSExchangeData> received_data_;
int destination_operation_;
+ int last_event_flags_ = ui::EF_NONE;
DISALLOW_COPY_AND_ASSIGN(FakeDragDropDelegate);
};
@@ -177,6 +196,11 @@ class DesktopDragDropClientOzoneTest : public ViewsTestBase {
DesktopDragDropClientOzoneTest() = default;
~DesktopDragDropClientOzoneTest() override = default;
+ void SetModifiers(int modifiers) {
+ DCHECK(platform_window_);
+ platform_window_->set_modifiers(modifiers);
+ }
+
int StartDragAndDrop(int operation) {
auto data = std::make_unique<ui::OSExchangeData>();
data->SetString(base::ASCIIToUTF16("Test"));
@@ -189,7 +213,7 @@ class DesktopDragDropClientOzoneTest : public ViewsTestBase {
return client_->StartDragAndDrop(
std::move(data), widget_->GetNativeWindow()->GetRootWindow(),
widget_->GetNativeWindow(), gfx::Point(), operation,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+ ui::mojom::DragEventSource::kMouse);
}
// ViewsTestBase:
@@ -254,7 +278,27 @@ TEST_F(DesktopDragDropClientOzoneTest, StartDrag) {
EXPECT_EQ(1, dragdrop_delegate_->num_enters());
EXPECT_EQ(1, dragdrop_delegate_->num_updates());
EXPECT_EQ(1, dragdrop_delegate_->num_drops());
- EXPECT_EQ(1, dragdrop_delegate_->num_exits());
+ EXPECT_EQ(0, dragdrop_delegate_->num_exits());
+
+ EXPECT_EQ(ui::EF_NONE, dragdrop_delegate_->last_event_flags());
+}
+
+TEST_F(DesktopDragDropClientOzoneTest, StartDragCtrlPressed) {
+ SetModifiers(ui::EF_CONTROL_DOWN);
+ // Set the operation which the destination can accept.
+ dragdrop_delegate_->SetOperation(ui::DragDropTypes::DRAG_COPY);
+ // Start Drag and Drop with the operations suggested.
+ int operation = StartDragAndDrop(ui::DragDropTypes::DRAG_COPY |
+ ui::DragDropTypes::DRAG_MOVE);
+ // The |operation| decided through negotiation should be 'DRAG_COPY'.
+ EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, operation);
+
+ EXPECT_EQ(1, dragdrop_delegate_->num_enters());
+ EXPECT_EQ(1, dragdrop_delegate_->num_updates());
+ EXPECT_EQ(1, dragdrop_delegate_->num_drops());
+ EXPECT_EQ(0, dragdrop_delegate_->num_exits());
+
+ EXPECT_EQ(ui::EF_CONTROL_DOWN, dragdrop_delegate_->last_event_flags());
}
TEST_F(DesktopDragDropClientOzoneTest, ReceiveDrag) {
@@ -290,7 +334,7 @@ TEST_F(DesktopDragDropClientOzoneTest, ReceiveDrag) {
EXPECT_EQ(1, dragdrop_delegate_->num_enters());
EXPECT_EQ(1, dragdrop_delegate_->num_updates());
EXPECT_EQ(1, dragdrop_delegate_->num_drops());
- EXPECT_EQ(1, dragdrop_delegate_->num_exits());
+ EXPECT_EQ(0, dragdrop_delegate_->num_exits());
}
TEST_F(DesktopDragDropClientOzoneTest, TargetDestroyedDuringDrag) {
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
index e3bd6355071..173c21ae65d 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
@@ -9,6 +9,7 @@
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drag_source_win.h"
#include "ui/base/dragdrop/drop_target_event.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
#include "ui/base/win/event_creation_utils.h"
#include "ui/display/win/screen_win.h"
@@ -39,10 +40,10 @@ int DesktopDragDropClientWin::StartDragAndDrop(
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
drag_drop_in_progress_ = true;
drag_operation_ = operation;
- if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
+ if (source == ui::mojom::DragEventSource::kTouch) {
gfx::Point screen_point = display::win::ScreenWin::DIPToScreenPoint(
{screen_location.x(), screen_location.y()});
// Send a mouse down and mouse move before do drag drop runs its own event
@@ -51,6 +52,11 @@ int DesktopDragDropClientWin::StartDragAndDrop(
MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE);
ui::SendMouseEvent(screen_point, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
desktop_host_->SetInTouchDrag(true);
+ // Gesture state gets left in a state where you can't start
+ // another drag, unless it's cleaned up. Cleaning it up before starting
+ // drag drop also fixes an issue with getting two kGestureScrollBegin events
+ // in a row. See crbug.com/1120809.
+ source_window->CleanupGestureState();
}
base::WeakPtr<DesktopDragDropClientWin> alive(weak_factory_.GetWeakPtr());
@@ -66,11 +72,8 @@ int DesktopDragDropClientWin::StartDragAndDrop(
ui::OSExchangeDataProviderWin::GetIDataObject(*data.get()),
drag_source_.Get(),
ui::DragDropTypes::DragOperationToDropEffect(operation), &effect);
- if (alive && source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
+ if (alive && source == ui::mojom::DragEventSource::kTouch) {
desktop_host_->SetInTouchDrag(false);
- // Gesture state gets left in a state where you can't start
- // another drag, unless it's cleaned up.
- source_window->CleanupGestureState();
}
drag_source_copy->set_data(nullptr);
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
index a55fe963a04..644de0cd15c 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
@@ -44,7 +44,7 @@ class VIEWS_EXPORT DesktopDragDropClientWin
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
void AddObserver(aura::client::DragDropClientObserver* observer) override;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 7578eef4f53..48a91153ca9 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -25,6 +25,7 @@
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/class_property.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/hit_test.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ui_base_features.h"
@@ -358,9 +359,6 @@ void DesktopNativeWidgetAura::OnDesktopWindowTreeHostDestroyed(
aura::client::SetScreenPositionClient(host->window(), nullptr);
position_client_.reset();
- aura::client::SetDragDropClient(host->window(), nullptr);
- drag_drop_client_.reset();
-
aura::client::SetEventClient(host->window(), nullptr);
event_client_.reset();
}
@@ -433,6 +431,13 @@ void DesktopNativeWidgetAura::HandleActivationChanged(bool active) {
}
}
+void DesktopNativeWidgetAura::OnHostWillClose() {
+ // The host is going to close, but our DnD client may use it, so we need to
+ // detach the DnD client and destroy it to avoid uses after free.
+ aura::client::SetDragDropClient(host_->window(), nullptr);
+ drag_drop_client_.reset();
+}
+
gfx::NativeWindow DesktopNativeWidgetAura::GetNativeWindow() const {
return content_window_;
}
@@ -596,7 +601,8 @@ void DesktopNativeWidgetAura::OnWidgetInitDone() {
desktop_window_tree_host_->OnWidgetInitDone();
}
-NonClientFrameView* DesktopNativeWidgetAura::CreateNonClientFrameView() {
+std::unique_ptr<NonClientFrameView>
+DesktopNativeWidgetAura::CreateNonClientFrameView() {
return desktop_window_tree_host_->CreateNonClientFrameView();
}
@@ -919,7 +925,7 @@ void DesktopNativeWidgetAura::RunShellDrag(
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
views::RunShellDrag(content_window_, std::move(data), location, operation,
source);
}
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index 89f7d22cbcb..41a7b8afa78 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -98,6 +98,9 @@ class VIEWS_EXPORT DesktopNativeWidgetAura
// we are being activated/deactivated.
void HandleActivationChanged(bool active);
+ // Called before the window tree host will close.
+ void OnHostWillClose();
+
// Overridden from internal::NativeWidgetPrivate:
gfx::NativeWindow GetNativeWindow() const override;
@@ -109,7 +112,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura
// internal::NativeWidgetPrivate:
void InitNativeWidget(Widget::InitParams params) override;
void OnWidgetInitDone() override;
- NonClientFrameView* CreateNonClientFrameView() override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() override;
bool ShouldUseNativeFrame() const override;
bool ShouldWindowContentsBeTransparent() const override;
void FrameTypeChanged() override;
@@ -174,7 +177,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void SchedulePaintInRect(const gfx::Rect& rect) override;
void ScheduleLayout() override;
void SetCursor(gfx::NativeCursor cursor) override;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
index 71e7bbc378b..2192dab330b 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
@@ -69,7 +69,7 @@ TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) {
// On Linux we test this with popup windows because the WM may ignore the size
// suggestion for normal windows.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
#else
Widget::InitParams init_params =
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
index 05fc9cde057..1f00883eaeb 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
@@ -26,7 +26,7 @@ gfx::NativeWindow DesktopScreenOzone::GetNativeWindowFromAcceleratedWidget(
// To avoid multiple definitions when use_x11 && use_ozone is true, disable this
// factory method for OS_LINUX as Linux has a factory method that decides what
// screen to use based on IsUsingOzonePlatform feature flag.
-#if !defined(OS_LINUX)
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
display::Screen* CreateDesktopScreen() {
return new DesktopScreenOzone();
}
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index 084ba94bd9a..b43479cd90a 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -4,6 +4,8 @@
#include "ui/views/widget/desktop_aura/desktop_screen_x11.h"
+#include <set>
+#include <string>
#include <vector>
#include "base/command_line.h"
@@ -23,7 +25,7 @@
#include "ui/gfx/switches.h"
#include "ui/platform_window/x11/x11_topmost_window_finder.h"
#include "ui/views/widget/desktop_aura/desktop_screen.h"
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
namespace views {
@@ -66,7 +68,7 @@ gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), {});
return window != x11::Window::None
? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget(
- window)
+ static_cast<gfx::AcceleratedWidget>(window))
: nullptr;
}
@@ -81,7 +83,7 @@ gfx::NativeWindow DesktopScreenX11::GetLocalProcessWindowAtPoint(
ignore_widgets);
return window != x11::Window::None
? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget(
- window)
+ static_cast<gfx::AcceleratedWidget>(window))
: nullptr;
}
@@ -149,8 +151,7 @@ std::string DesktopScreenX11::GetCurrentWorkspace() {
}
bool DesktopScreenX11::DispatchXEvent(x11::Event* event) {
- return x11_display_manager_->CanProcessEvent(*event) &&
- x11_display_manager_->ProcessEvent(event);
+ return x11_display_manager_->ProcessEvent(event);
}
void DesktopScreenX11::OnDeviceScaleFactorChanged() {
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
index d284138d2a9..b014bb2eda3 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
@@ -16,6 +16,7 @@
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/hit_test.h"
+#include "ui/base/ui_base_features.h"
#include "ui/base/x/x11_display_manager.h"
#include "ui/base/x/x11_util.h"
#include "ui/display/display.h"
@@ -26,7 +27,7 @@
#include "ui/gfx/x/x11_types.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
namespace {
@@ -70,6 +71,9 @@ class DesktopScreenX11Test : public views::ViewsTestBase,
// Overridden from testing::Test:
void SetUp() override {
ViewsTestBase::SetUp();
+ // TODO(msisov): rewrite desktop screen tests for ozone and non-ozone Linux.
+ if (features::IsUsingOzonePlatform())
+ GTEST_SKIP();
// Initialize the world to the single monitor case.
std::vector<display::Display> displays;
displays.emplace_back(kFirstDisplay, gfx::Rect(0, 0, 640, 480));
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h
index 9e15a572258..77a3bbfd623 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h
@@ -150,7 +150,7 @@ class VIEWS_EXPORT DesktopWindowTreeHost {
virtual void SetVisibilityChangedAnimationsEnabled(bool value) = 0;
- virtual NonClientFrameView* CreateNonClientFrameView() = 0;
+ virtual std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() = 0;
// Determines whether the window should use native title bar and borders.
virtual bool ShouldUseNativeFrame() const = 0;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
index 61d8b3a0678..0bdc1470708 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -14,12 +14,13 @@
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
+#include "ui/base/ui_base_features.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/platform_window/extensions/x11_extension.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
#include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/desktop_aura/window_event_filter_linux.h"
@@ -158,6 +159,8 @@ void DesktopWindowTreeHostLinux::OnNativeWidgetCreated(
base::flat_map<std::string, std::string>
DesktopWindowTreeHostLinux::GetKeyboardLayoutMap() {
+ if (features::IsUsingOzonePlatform())
+ return DesktopWindowTreeHostPlatform::GetKeyboardLayoutMap();
if (views::LinuxUI::instance())
return views::LinuxUI::instance()->GetKeyboardLayoutMap();
return {};
@@ -269,8 +272,9 @@ const ui::X11Extension* DesktopWindowTreeHostLinux::GetX11Extension() const {
}
#if BUILDFLAG(USE_ATK)
-bool DesktopWindowTreeHostLinux::OnAtkKeyEvent(AtkKeyEventStruct* atk_event) {
- if (!IsActive() && !HasCapture())
+bool DesktopWindowTreeHostLinux::OnAtkKeyEvent(AtkKeyEventStruct* atk_event,
+ bool transient) {
+ if (!transient && !IsActive() && !HasCapture())
return false;
return ui::AtkUtilAuraLinux::HandleAtkKeyEvent(atk_event) ==
ui::DiscardAtkKeyEvent::Discard;
@@ -362,11 +366,6 @@ std::list<gfx::AcceleratedWidget>& DesktopWindowTreeHostLinux::open_windows() {
return *open_windows_;
}
-// As DWTHX11 subclasses DWTHPlatform through DWTHLinux now (during transition
-// period. see https://crbug.com/990756), we need to guard this factory method.
-// TODO(msisov): remove this guard once DWTHX11 is finally merged into
-// DWTHPlatform and .
-#if !defined(USE_X11)
// static
DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
internal::NativeWidgetDelegate* native_widget_delegate,
@@ -374,6 +373,5 @@ DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
return new DesktopWindowTreeHostLinux(native_widget_delegate,
desktop_native_widget_aura);
}
-#endif
} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
index bec4d76adb1..4b6a5a4476a 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
@@ -109,7 +109,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux
void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
void OnLostMouseGrab() override;
#if BUILDFLAG(USE_ATK)
- bool OnAtkKeyEvent(AtkKeyEventStruct* atk_key_event) override;
+ bool OnAtkKeyEvent(AtkKeyEventStruct* atk_key_event, bool transient) override;
#endif
bool IsOverrideRedirect(bool is_tiling_wm) const override;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc
index cb61f3c0b4d..1a917b6cdc9 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_interactive_uitest.cc
@@ -8,13 +8,24 @@
#include "ui/aura/window_tree_host_platform.h"
#include "ui/base/hit_test.h"
#include "ui/platform_window/platform_window.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/desktop_aura/window_event_filter_linux.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/native_frame_view.h"
+#if defined(USE_X11)
+#include "ui/aura/env.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/x/test/x11_property_change_waiter.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/views/controls/textfield/textfield.h"
+#endif // defined(USE_X11)
+
namespace views {
namespace {
@@ -37,6 +48,92 @@ bool IsNonClientComponent(int hittest) {
return true;
}
+#if defined(USE_X11)
+// Creates a widget with the given bounds.
+std::unique_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
+ std::unique_ptr<Widget> widget(new Widget);
+ Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.remove_standard_frame = true;
+ params.native_widget = new DesktopNativeWidgetAura(widget.get());
+ params.bounds = bounds;
+ widget->Init(std::move(params));
+ return widget;
+}
+
+// Dispatches a XMotionEvent targeted at |host|'s X window with location
+// |point_in_screen|.
+void DispatchMouseMotionEvent(DesktopWindowTreeHostLinux* desktop_host,
+ const gfx::Point& point_in_screen) {
+ gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
+
+ auto* connection = x11::Connection::Get();
+ xcb_generic_event_t ge;
+ memset(&ge, 0, sizeof(ge));
+ auto* xev = reinterpret_cast<xcb_motion_notify_event_t*>(&ge);
+ xev->response_type = MotionNotify;
+ xev->event = static_cast<uint32_t>(desktop_host->GetAcceleratedWidget());
+ xev->root = static_cast<uint32_t>(connection->default_screen().root);
+ xev->child = 0;
+ xev->time = x11::CurrentTime;
+ xev->event_x = point_in_screen.x() - bounds_in_screen.x();
+ xev->event_y = point_in_screen.y() - bounds_in_screen.y();
+ xev->root_x = point_in_screen.x();
+ xev->root_y = point_in_screen.y();
+ xev->state = 0;
+ xev->detail = NotifyNormal;
+ xev->same_screen = x11::True;
+
+ x11::Event x11_event(&ge, connection);
+ ui::X11EventSource::GetInstance()->ProcessXEvent(&x11_event);
+}
+
+// Blocks till |window| gets activated.
+class ActivationWaiter : public ui::X11PropertyChangeWaiter {
+ public:
+ explicit ActivationWaiter(x11::Window window)
+ : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(),
+ "_NET_ACTIVE_WINDOW"),
+ window_(window) {}
+
+ ~ActivationWaiter() override = default;
+
+ ActivationWaiter(const ActivationWaiter&) = delete;
+ ActivationWaiter& operator=(ActivationWaiter&) = delete;
+
+ private:
+ // ui::X11PropertyChangeWaiter:
+ bool ShouldKeepOnWaiting(x11::Event* event) override {
+ x11::Window window = x11::Window::None;
+ ui::GetProperty(ui::GetX11RootWindow(), gfx::GetAtom("_NET_ACTIVE_WINDOW"),
+ &window);
+ return window != window_;
+ }
+
+ x11::Window window_;
+};
+#endif // defined(USE_X11)
+
+// An event handler which counts the number of mouse moves it has seen.
+class MouseMoveCounterHandler : public ui::EventHandler {
+ public:
+ MouseMoveCounterHandler() = default;
+ ~MouseMoveCounterHandler() override = default;
+
+ // ui::EventHandler:
+ void OnMouseEvent(ui::MouseEvent* event) override {
+ if (event->type() == ui::ET_MOUSE_MOVED)
+ ++count_;
+ }
+
+ int num_mouse_moves() const { return count_; }
+
+ private:
+ int count_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
+};
+
// A fake handler, which just stores the hittest and pointer location values.
class FakeWmMoveResizeHandler : public ui::WmMoveResizeHandler {
public:
@@ -131,11 +228,13 @@ class HitTestWidgetDelegate : public WidgetDelegate {
bool CanResize() const override { return can_resize_; }
Widget* GetWidget() override { return widget_; }
Widget* GetWidget() const override { return widget_; }
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
- DCHECK(widget_ == widget);
- if (!frame_view_)
- frame_view_ = new HitTestNonClientFrameView(widget);
- return frame_view_;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override {
+ DCHECK_EQ(widget_, widget);
+ DCHECK(!frame_view_);
+ auto frame_view = std::make_unique<HitTestNonClientFrameView>(widget);
+ frame_view_ = frame_view.get();
+ return frame_view;
}
void DeleteDelegate() override { delete this; }
@@ -421,4 +520,143 @@ TEST_F(DesktopWindowTreeHostLinuxTest,
widget->CloseNow();
}
+#if defined(USE_X11)
+// Test that calling Widget::Deactivate() sets the widget as inactive wrt to
+// Chrome even if it not possible to deactivate the window wrt to the x server.
+// This behavior is required by several interactive_ui_tests.
+TEST_F(DesktopWindowTreeHostLinuxTest, Deactivate) {
+ std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
+
+ ActivationWaiter waiter(static_cast<x11::Window>(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()));
+ widget->Show();
+ widget->Activate();
+ waiter.Wait();
+
+ widget->Deactivate();
+ // Regardless of whether |widget|'s X11 window eventually gets deactivated,
+ // |widget|'s "active" state should change.
+ EXPECT_FALSE(widget->IsActive());
+
+ // |widget|'s X11 window should still be active. Reactivating |widget| should
+ // update the widget's "active" state.
+ // Note: Activating a widget whose X11 window is not active does not
+ // synchronously update the widget's "active" state.
+ widget->Activate();
+ EXPECT_TRUE(widget->IsActive());
+}
+
+// Chrome attempts to make mouse capture look synchronous on Linux. Test that
+// Chrome synchronously switches the window that mouse events are forwarded to
+// when capture is changed.
+TEST_F(DesktopWindowTreeHostLinuxTest, CaptureEventForwarding) {
+ std::unique_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
+ aura::Window* window1 = widget1->GetNativeWindow();
+ DesktopWindowTreeHostLinux* host1 =
+ static_cast<DesktopWindowTreeHostLinux*>(window1->GetHost());
+ widget1->Show();
+
+ std::unique_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
+ aura::Window* window2 = widget2->GetNativeWindow();
+ DesktopWindowTreeHostLinux* host2 =
+ static_cast<DesktopWindowTreeHostLinux*>(window2->GetHost());
+ widget2->Show();
+
+ MouseMoveCounterHandler recorder1;
+ window1->AddPreTargetHandler(&recorder1);
+ MouseMoveCounterHandler recorder2;
+ window2->AddPreTargetHandler(&recorder2);
+
+ // Move the mouse to the center of |widget2|.
+ gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint();
+ DispatchMouseMotionEvent(host2, point_in_screen);
+ EXPECT_EQ(0, recorder1.num_mouse_moves());
+ EXPECT_EQ(1, recorder2.num_mouse_moves());
+ EXPECT_EQ(point_in_screen.ToString(),
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+
+ // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls
+ // XGrabPointer() with owner == False, the X server sends events to |widget2|
+ // as long as the mouse is hovered over |widget2|. Verify that Chrome
+ // redirects mouse events to |widget1|.
+ widget1->SetCapture(nullptr);
+ point_in_screen += gfx::Vector2d(1, 0);
+ DispatchMouseMotionEvent(host2, point_in_screen);
+ EXPECT_EQ(1, recorder1.num_mouse_moves());
+ EXPECT_EQ(1, recorder2.num_mouse_moves());
+ // If the event's location was correctly changed to be relative to |widget1|,
+ // Env's last mouse position will be correct.
+ EXPECT_EQ(point_in_screen.ToString(),
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+
+ // Set capture to |widget2|. Subsequent events sent to |widget2| should not be
+ // forwarded.
+ widget2->SetCapture(nullptr);
+ point_in_screen += gfx::Vector2d(1, 0);
+ DispatchMouseMotionEvent(host2, point_in_screen);
+ EXPECT_EQ(1, recorder1.num_mouse_moves());
+ EXPECT_EQ(2, recorder2.num_mouse_moves());
+ EXPECT_EQ(point_in_screen.ToString(),
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+
+ // If the mouse is not hovered over |widget1| or |widget2|, the X server will
+ // send events to the window which has capture. Test the mouse events sent to
+ // |widget2| are not forwarded.
+ DispatchMouseMotionEvent(host2, point_in_screen);
+ EXPECT_EQ(1, recorder1.num_mouse_moves());
+ EXPECT_EQ(3, recorder2.num_mouse_moves());
+ EXPECT_EQ(point_in_screen.ToString(),
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+
+ // Release capture. Test that when capture is released, mouse events are no
+ // longer forwarded to other widgets.
+ widget2->ReleaseCapture();
+ point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint();
+ DispatchMouseMotionEvent(host1, point_in_screen);
+ EXPECT_EQ(2, recorder1.num_mouse_moves());
+ EXPECT_EQ(3, recorder2.num_mouse_moves());
+ EXPECT_EQ(point_in_screen.ToString(),
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+
+ // Cleanup
+ window1->RemovePreTargetHandler(&recorder1);
+ window2->RemovePreTargetHandler(&recorder2);
+}
+
+TEST_F(DesktopWindowTreeHostLinuxTest, InputMethodFocus) {
+ std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
+
+ // Waiter should be created as early as possible so that PropertyNotify has
+ // time to be set before widget is activated.
+ ActivationWaiter waiter(static_cast<x11::Window>(
+ widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()));
+
+ std::unique_ptr<Textfield> textfield(new Textfield);
+ textfield->SetBounds(0, 0, 200, 20);
+ widget->GetRootView()->AddChildView(textfield.get());
+ widget->ShowInactive();
+ textfield->RequestFocus();
+
+ EXPECT_FALSE(widget->IsActive());
+ // TODO(shuchen): uncomment the below check once the
+ // "default-focused-input-method" logic is removed in aura::WindowTreeHost.
+ // EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
+ // widget->GetInputMethod()->GetTextInputType());
+
+ widget->Activate();
+ waiter.Wait();
+
+ EXPECT_TRUE(widget->IsActive());
+ EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
+ widget->GetInputMethod()->GetTextInputType());
+
+ widget->Deactivate();
+
+ EXPECT_FALSE(widget->IsActive());
+ EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
+ widget->GetInputMethod()->GetTextInputType());
+}
+
+#endif // defined(USE_X11)
+
} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc
index 15d8c29db55..90ea012c614 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc
@@ -4,6 +4,8 @@
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
+#include <utility>
+
#include "base/command_line.h"
#include "ui/base/hit_test.h"
#include "ui/display/display_switches.h"
@@ -71,8 +73,9 @@ class ShapedWidgetDelegate : public WidgetDelegateView {
~ShapedWidgetDelegate() override = default;
// WidgetDelegateView:
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
- return new ShapedNonClientFrameView;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override {
+ return std::make_unique<ShapedNonClientFrameView>();
}
private:
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index aaf1b482f71..6a1fcf7fe75 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -16,13 +16,14 @@
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/transient_window_client.h"
#include "ui/base/hit_test.h"
+#include "ui/base/ui_base_features.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/platform_window/extensions/workspace_extension.h"
#include "ui/platform_window/platform_window.h"
-#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
#include "ui/platform_window/platform_window_init_properties.h"
+#include "ui/platform_window/wm/wm_move_loop_handler.h"
#include "ui/views/corewm/tooltip_aura.h"
#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
@@ -209,7 +210,6 @@ DesktopWindowTreeHostPlatform::CreateTooltip() {
std::unique_ptr<aura::client::DragDropClient>
DesktopWindowTreeHostPlatform::CreateDragDropClient(
DesktopNativeCursorManager* cursor_manager) {
-#if !defined(USE_X11)
ui::WmDragHandler* drag_handler = ui::GetWmDragHandler(*(platform_window()));
std::unique_ptr<DesktopDragDropClientOzone> drag_drop_client =
std::make_unique<DesktopDragDropClientOzone>(window(), cursor_manager,
@@ -218,11 +218,6 @@ DesktopWindowTreeHostPlatform::CreateDragDropClient(
// drop action.
SetWmDropHandler(platform_window(), drag_drop_client.get());
return std::move(drag_drop_client);
-#else
- // TODO(https://crbug.com/990756): Move the X11 initialization of dnd here.
- NOTIMPLEMENTED_LOG_ONCE();
- return nullptr;
-#endif
}
void DesktopWindowTreeHostPlatform::Close() {
@@ -252,7 +247,8 @@ void DesktopWindowTreeHostPlatform::CloseNow() {
return;
#if defined(USE_OZONE)
- SetWmDropHandler(platform_window(), nullptr);
+ if (features::IsUsingOzonePlatform())
+ SetWmDropHandler(platform_window(), nullptr);
#endif
platform_window()->PrepareForShutdown();
@@ -551,8 +547,10 @@ void DesktopWindowTreeHostPlatform::SetVisibilityChangedAnimationsEnabled(
platform_window()->SetVisibilityChangedAnimationsEnabled(value);
}
-NonClientFrameView* DesktopWindowTreeHostPlatform::CreateNonClientFrameView() {
- return ShouldUseNativeFrame() ? new NativeFrameView(GetWidget()) : nullptr;
+std::unique_ptr<NonClientFrameView>
+DesktopWindowTreeHostPlatform::CreateNonClientFrameView() {
+ return ShouldUseNativeFrame() ? std::make_unique<NativeFrameView>(GetWidget())
+ : nullptr;
}
bool DesktopWindowTreeHostPlatform::ShouldUseNativeFrame() const {
@@ -679,6 +677,8 @@ void DesktopWindowTreeHostPlatform::HideImpl() {
void DesktopWindowTreeHostPlatform::OnClosed() {
wm::SetWindowMoveClient(window(), nullptr);
+ SetWmDropHandler(platform_window(), nullptr);
+ desktop_native_widget_aura_->OnHostWillClose();
SetPlatformWindow(nullptr);
desktop_native_widget_aura_->OnHostClosed();
}
@@ -713,6 +713,10 @@ void DesktopWindowTreeHostPlatform::OnCloseRequest() {
GetWidget()->Close();
}
+void DesktopWindowTreeHostPlatform::OnWillDestroyAcceleratedWidget() {
+ desktop_native_widget_aura_->OnHostWillClose();
+}
+
void DesktopWindowTreeHostPlatform::OnActivationChanged(bool active) {
if (is_active_ == active)
return;
@@ -787,7 +791,7 @@ void DesktopWindowTreeHostPlatform::AddAdditionalInitProperties(
// DesktopWindowTreeHost:
// Linux subclasses this host and adds some Linux specific bits.
-#if !defined(OS_LINUX)
+#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// static
DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
internal::NativeWidgetDelegate* native_widget_delegate,
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index 75860eaabf1..8db9bca8f17 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -89,7 +89,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform
Widget::MoveLoopEscapeBehavior escape_behavior) override;
void EndMoveLoop() override;
void SetVisibilityChangedAnimationsEnabled(bool value) override;
- NonClientFrameView* CreateNonClientFrameView() override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() override;
bool ShouldUseNativeFrame() const override;
bool ShouldWindowContentsBeTransparent() const override;
void FrameTypeChanged() override;
@@ -117,6 +117,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform
void OnClosed() override;
void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
void OnCloseRequest() override;
+ void OnWillDestroyAcceleratedWidget() override;
void OnActivationChanged(bool active) override;
base::Optional<gfx::Size> GetMinimumSizeForWindow() override;
base::Optional<gfx::Size> GetMaximumSizeForWindow() override;
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index b5430b871cf..e9e61a24dc8 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -437,10 +437,11 @@ void DesktopWindowTreeHostWin::SetVisibilityChangedAnimationsEnabled(
content_window()->SetProperty(aura::client::kAnimationsDisabledKey, !value);
}
-NonClientFrameView* DesktopWindowTreeHostWin::CreateNonClientFrameView() {
- return ShouldUseNativeFrame()
- ? new NativeFrameView(native_widget_delegate_->AsWidget())
- : nullptr;
+std::unique_ptr<NonClientFrameView>
+DesktopWindowTreeHostWin::CreateNonClientFrameView() {
+ return ShouldUseNativeFrame() ? std::make_unique<NativeFrameView>(
+ native_widget_delegate_->AsWidget())
+ : nullptr;
}
bool DesktopWindowTreeHostWin::ShouldUseNativeFrame() const {
@@ -736,10 +737,6 @@ int DesktopWindowTreeHostWin::GetInitialShowState() const {
return CanActivate() ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE;
}
-bool DesktopWindowTreeHostWin::WillProcessWorkAreaChange() const {
- return GetWidget()->widget_delegate()->WillProcessWorkAreaChange();
-}
-
int DesktopWindowTreeHostWin::GetNonClientComponent(
const gfx::Point& point) const {
gfx::Point dip_position =
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index c0f00b6d86d..0229c1c4150 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -113,7 +113,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
Widget::MoveLoopEscapeBehavior escape_behavior) override;
void EndMoveLoop() override;
void SetVisibilityChangedAnimationsEnabled(bool value) override;
- NonClientFrameView* CreateNonClientFrameView() override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() override;
bool ShouldUseNativeFrame() const override;
bool ShouldWindowContentsBeTransparent() const override;
void FrameTypeChanged() override;
@@ -175,7 +175,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
bool WidgetSizeIsClientSize() const override;
bool IsModal() const override;
int GetInitialShowState() const override;
- bool WillProcessWorkAreaChange() const override;
int GetNonClientComponent(const gfx::Point& point) const override;
void GetWindowMask(const gfx::Size& size, SkPath* path) override;
bool GetClientAreaInsets(gfx::Insets* insets,
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
deleted file mode 100644
index af81a254b0c..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
-
-#include <algorithm>
-#include <list>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/containers/flat_set.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/client/cursor_client.h"
-#include "ui/aura/null_window_targeter.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/buildflags.h"
-#include "ui/base/class_property.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_x11.h"
-#include "ui/base/hit_test.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/base/layout.h"
-#include "ui/base/x/x11_pointer_grab.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/base/x/x11_util_internal.h"
-#include "ui/display/screen.h"
-#include "ui/events/devices/x11/device_list_cache_x11.h"
-#include "ui/events/event.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/keyboard_hook.h"
-#include "ui/events/keycodes/dom/dom_code.h"
-#include "ui/events/x/events_x_utils.h"
-#include "ui/events/x/x11_window_event_manager.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/image/image_skia_rep.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_atom_cache.h"
-#include "ui/gfx/x/x11_path.h"
-#include "ui/gfx/x/xproto.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/views_switches.h"
-#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
-#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
-#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
-#include "ui/views/window/native_frame_view.h"
-#include "ui/wm/core/compound_event_filter.h"
-#include "ui/wm/core/window_util.h"
-
-namespace views {
-
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostX11, public:
-DesktopWindowTreeHostX11::DesktopWindowTreeHostX11(
- internal::NativeWidgetDelegate* native_widget_delegate,
- DesktopNativeWidgetAura* desktop_native_widget_aura)
- : DesktopWindowTreeHostLinux(native_widget_delegate,
- desktop_native_widget_aura) {}
-
-DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() = default;
-
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
-
-void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) {
- DesktopWindowTreeHostLinux::Init(params);
-
- // Set XEventDelegate to receive selection, drag&drop and raw key events.
- //
- // TODO(https://crbug.com/990756): There are two cases of this delegate:
- // x11::Events for DragAndDrop client and raw key events. DragAndDrop could be
- // unified so that DragAndrDropClientOzone is used and x11::Event are handled
- // on platform level.
- static_cast<ui::X11Window*>(platform_window())->SetXEventDelegate(this);
-}
-
-std::unique_ptr<aura::client::DragDropClient>
-DesktopWindowTreeHostX11::CreateDragDropClient(
- DesktopNativeCursorManager* cursor_manager) {
- drag_drop_client_ = new DesktopDragDropClientAuraX11(window(), cursor_manager,
- GetXWindow()->window());
- drag_drop_client_->Init();
- return base::WrapUnique(drag_drop_client_);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostX11 implementation:
-
-void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(x11::Event* xev) {
- DCHECK(xev);
- DCHECK(drag_drop_client_);
- drag_drop_client_->OnSelectionNotify(*xev->As<x11::SelectionNotifyEvent>());
-}
-
-void DesktopWindowTreeHostX11::OnXWindowDragDropEvent(x11::Event* xev) {
- DCHECK(xev);
- DCHECK(drag_drop_client_);
- drag_drop_client_->HandleXdndEvent(*xev->As<x11::ClientMessageEvent>());
-}
-
-const ui::XWindow* DesktopWindowTreeHostX11::GetXWindow() const {
- DCHECK(platform_window());
- // ui::X11Window inherits both PlatformWindow and ui::XWindow.
- return static_cast<const ui::XWindow*>(
- static_cast<const ui::X11Window*>(platform_window()));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHost, public:
-
-// static
-DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
- internal::NativeWidgetDelegate* native_widget_delegate,
- DesktopNativeWidgetAura* desktop_native_widget_aura) {
- return new DesktopWindowTreeHostX11(native_widget_delegate,
- desktop_native_widget_aura);
-}
-
-} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
deleted file mode 100644
index 69501945464..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_
-#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "ui/gfx/x/event.h"
-#include "ui/gfx/x/x11_types.h"
-#include "ui/platform_window/platform_window_delegate.h"
-#include "ui/platform_window/x11/x11_window.h"
-#include "ui/views/views_export.h"
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
-
-namespace ui {
-enum class DomCode;
-class X11Window;
-} // namespace ui
-
-namespace views {
-class DesktopDragDropClientAuraX11;
-
-class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux,
- public ui::XEventDelegate {
- public:
- DesktopWindowTreeHostX11(
- internal::NativeWidgetDelegate* native_widget_delegate,
- DesktopNativeWidgetAura* desktop_native_widget_aura);
- ~DesktopWindowTreeHostX11() override;
-
- protected:
- // Overridden from DesktopWindowTreeHost:
- void Init(const Widget::InitParams& params) override;
- std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient(
- DesktopNativeCursorManager* cursor_manager) override;
-
- private:
- friend class DesktopWindowTreeHostX11HighDPITest;
-
- // Overridden from ui::XEventDelegate.
- void OnXWindowSelectionEvent(x11::Event* xev) override;
- void OnXWindowDragDropEvent(x11::Event* xev) override;
-
- // Casts PlatformWindow into XWindow and returns the result. This is a temp
- // solution to access XWindow, which is subclassed by the X11Window, which is
- // PlatformWindow. This will be removed once we no longer to access XWindow
- // directly. See https://crbug.com/990756.
- const ui::XWindow* GetXWindow() const;
-
- DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_
diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc
deleted file mode 100644
index b7c96511462..00000000000
--- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
-
-#include <xcb/xproto.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "ui/aura/env.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/base/x/test/x11_property_change_waiter.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/events/event_handler.h"
-#include "ui/events/platform/x11/x11_event_source.h"
-#include "ui/events/x/x11_event_translation.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/x/event.h"
-#include "ui/gfx/x/x11.h"
-#include "ui/gfx/x/x11_atom_cache.h"
-#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/test/widget_test.h"
-#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
-
-namespace views {
-
-namespace {
-
-// Blocks till |window| gets activated.
-class ActivationWaiter : public ui::X11PropertyChangeWaiter {
- public:
- explicit ActivationWaiter(x11::Window window)
- : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(),
- "_NET_ACTIVE_WINDOW"),
- window_(window) {}
-
- ~ActivationWaiter() override = default;
-
- private:
- // ui::X11PropertyChangeWaiter:
- bool ShouldKeepOnWaiting(x11::Event* event) override {
- x11::Window window = x11::Window::None;
- ui::GetProperty(ui::GetX11RootWindow(), gfx::GetAtom("_NET_ACTIVE_WINDOW"),
- &window);
- return window != window_;
- }
-
- x11::Window window_;
-
- DISALLOW_COPY_AND_ASSIGN(ActivationWaiter);
-};
-
-// An event handler which counts the number of mouse moves it has seen.
-class MouseMoveCounterHandler : public ui::EventHandler {
- public:
- MouseMoveCounterHandler() = default;
- ~MouseMoveCounterHandler() override = default;
-
- // ui::EventHandler:
- void OnMouseEvent(ui::MouseEvent* event) override {
- if (event->type() == ui::ET_MOUSE_MOVED)
- ++count_;
- }
-
- int num_mouse_moves() const { return count_; }
-
- private:
- int count_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
-};
-
-// Creates a widget with the given bounds.
-std::unique_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
- std::unique_ptr<Widget> widget(new Widget);
- Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.remove_standard_frame = true;
- params.native_widget = new DesktopNativeWidgetAura(widget.get());
- params.bounds = bounds;
- widget->Init(std::move(params));
- return widget;
-}
-
-// Dispatches a XMotionEvent targeted at |host|'s X window with location
-// |point_in_screen|.
-void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host,
- const gfx::Point& point_in_screen) {
- gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
-
- auto* connection = x11::Connection::Get();
- xcb_generic_event_t ge;
- memset(&ge, 0, sizeof(ge));
- auto* xev = reinterpret_cast<xcb_motion_notify_event_t*>(&ge);
- xev->response_type = MotionNotify;
- xev->event = static_cast<uint32_t>(desktop_host->GetAcceleratedWidget());
- xev->root = static_cast<uint32_t>(connection->default_screen().root);
- xev->child = 0;
- xev->time = x11::CurrentTime;
- xev->event_x = point_in_screen.x() - bounds_in_screen.x();
- xev->event_y = point_in_screen.y() - bounds_in_screen.y();
- xev->root_x = point_in_screen.x();
- xev->root_y = point_in_screen.y();
- xev->state = 0;
- xev->detail = NotifyNormal;
- xev->same_screen = x11::True;
-
- x11::Event x11_event(&ge, connection);
- ui::X11EventSource::GetInstance()->ProcessXEvent(&x11_event);
-}
-
-} // namespace
-
-class DesktopWindowTreeHostX11Test : public test::DesktopWidgetTestInteractive {
- public:
- DesktopWindowTreeHostX11Test() = default;
- ~DesktopWindowTreeHostX11Test() override = default;
-
- // DesktopWidgetTestInteractive
- void SetUp() override {
- // Make X11 synchronous for our display connection. This does not force the
- // window manager to behave synchronously.
- XSynchronize(gfx::GetXDisplay(), x11::True);
- DesktopWidgetTestInteractive::SetUp();
- }
-
- void TearDown() override {
- XSynchronize(gfx::GetXDisplay(), x11::False);
- DesktopWidgetTestInteractive::TearDown();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
-};
-
-// Test that calling Widget::Deactivate() sets the widget as inactive wrt to
-// Chrome even if it not possible to deactivate the window wrt to the x server.
-// This behavior is required by several interactive_ui_tests.
-TEST_F(DesktopWindowTreeHostX11Test, Deactivate) {
- std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
-
- ActivationWaiter waiter(
- widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
- widget->Show();
- widget->Activate();
- waiter.Wait();
-
- widget->Deactivate();
- // Regardless of whether |widget|'s X11 window eventually gets deactivated,
- // |widget|'s "active" state should change.
- EXPECT_FALSE(widget->IsActive());
-
- // |widget|'s X11 window should still be active. Reactivating |widget| should
- // update the widget's "active" state.
- // Note: Activating a widget whose X11 window is not active does not
- // synchronously update the widget's "active" state.
- widget->Activate();
- EXPECT_TRUE(widget->IsActive());
-}
-
-// Chrome attempts to make mouse capture look synchronous on Linux. Test that
-// Chrome synchronously switches the window that mouse events are forwarded to
-// when capture is changed.
-TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) {
- std::unique_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
- aura::Window* window1 = widget1->GetNativeWindow();
- DesktopWindowTreeHostX11* host1 =
- static_cast<DesktopWindowTreeHostX11*>(window1->GetHost());
- widget1->Show();
-
- std::unique_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
- aura::Window* window2 = widget2->GetNativeWindow();
- DesktopWindowTreeHostX11* host2 =
- static_cast<DesktopWindowTreeHostX11*>(window2->GetHost());
- widget2->Show();
-
- MouseMoveCounterHandler recorder1;
- window1->AddPreTargetHandler(&recorder1);
- MouseMoveCounterHandler recorder2;
- window2->AddPreTargetHandler(&recorder2);
-
- // Move the mouse to the center of |widget2|.
- gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint();
- DispatchMouseMotionEvent(host2, point_in_screen);
- EXPECT_EQ(0, recorder1.num_mouse_moves());
- EXPECT_EQ(1, recorder2.num_mouse_moves());
- EXPECT_EQ(point_in_screen.ToString(),
- aura::Env::GetInstance()->last_mouse_location().ToString());
-
- // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls
- // XGrabPointer() with owner == False, the X server sends events to |widget2|
- // as long as the mouse is hovered over |widget2|. Verify that Chrome
- // redirects mouse events to |widget1|.
- widget1->SetCapture(nullptr);
- point_in_screen += gfx::Vector2d(1, 0);
- DispatchMouseMotionEvent(host2, point_in_screen);
- EXPECT_EQ(1, recorder1.num_mouse_moves());
- EXPECT_EQ(1, recorder2.num_mouse_moves());
- // If the event's location was correctly changed to be relative to |widget1|,
- // Env's last mouse position will be correct.
- EXPECT_EQ(point_in_screen.ToString(),
- aura::Env::GetInstance()->last_mouse_location().ToString());
-
- // Set capture to |widget2|. Subsequent events sent to |widget2| should not be
- // forwarded.
- widget2->SetCapture(nullptr);
- point_in_screen += gfx::Vector2d(1, 0);
- DispatchMouseMotionEvent(host2, point_in_screen);
- EXPECT_EQ(1, recorder1.num_mouse_moves());
- EXPECT_EQ(2, recorder2.num_mouse_moves());
- EXPECT_EQ(point_in_screen.ToString(),
- aura::Env::GetInstance()->last_mouse_location().ToString());
-
- // If the mouse is not hovered over |widget1| or |widget2|, the X server will
- // send events to the window which has capture. Test the mouse events sent to
- // |widget2| are not forwarded.
- DispatchMouseMotionEvent(host2, point_in_screen);
- EXPECT_EQ(1, recorder1.num_mouse_moves());
- EXPECT_EQ(3, recorder2.num_mouse_moves());
- EXPECT_EQ(point_in_screen.ToString(),
- aura::Env::GetInstance()->last_mouse_location().ToString());
-
- // Release capture. Test that when capture is released, mouse events are no
- // longer forwarded to other widgets.
- widget2->ReleaseCapture();
- point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint();
- DispatchMouseMotionEvent(host1, point_in_screen);
- EXPECT_EQ(2, recorder1.num_mouse_moves());
- EXPECT_EQ(3, recorder2.num_mouse_moves());
- EXPECT_EQ(point_in_screen.ToString(),
- aura::Env::GetInstance()->last_mouse_location().ToString());
-
- // Cleanup
- window1->RemovePreTargetHandler(&recorder1);
- window2->RemovePreTargetHandler(&recorder2);
-}
-
-TEST_F(DesktopWindowTreeHostX11Test, InputMethodFocus) {
- std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
-
- // Waiter should be created as early as possible so that PropertyNotify has
- // time to be set before widget is activated.
- ActivationWaiter waiter(
- widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
-
- std::unique_ptr<Textfield> textfield(new Textfield);
- textfield->SetBounds(0, 0, 200, 20);
- widget->GetRootView()->AddChildView(textfield.get());
- widget->ShowInactive();
- textfield->RequestFocus();
-
- EXPECT_FALSE(widget->IsActive());
- // TODO(shuchen): uncomment the below check once the
- // "default-focused-input-method" logic is removed in aura::WindowTreeHost.
- // EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
- // widget->GetInputMethod()->GetTextInputType());
-
- widget->Activate();
- waiter.Wait();
-
- EXPECT_TRUE(widget->IsActive());
- EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
- widget->GetInputMethod()->GetTextInputType());
-
- widget->Deactivate();
-
- EXPECT_FALSE(widget->IsActive());
- EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
- widget->GetInputMethod()->GetTextInputType());
-}
-
-} // namespace views
diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc b/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc
index 915fa77545d..439142aa0bc 100644
--- a/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc
+++ b/chromium/ui/views/widget/desktop_aura/window_event_filter_linux.cc
@@ -14,7 +14,7 @@
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/platform_window/wm/wm_move_resize_handler.h"
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
#include "ui/views/widget/native_widget_aura.h"
diff --git a/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc b/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
index c9946d29596..9d64b437e54 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
+++ b/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
@@ -22,6 +22,7 @@
#include "ui/aura/test/test_screen.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/x/x11_cursor.h"
#include "ui/base/x/x11_move_loop.h"
@@ -84,9 +85,9 @@ class TestMoveLoop : public ui::X11MoveLoop {
// ui::X11MoveLoop:
bool RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) override;
- void UpdateCursor(::Cursor cursor) override;
+ scoped_refptr<ui::X11Cursor> old_cursor,
+ scoped_refptr<ui::X11Cursor> new_cursor) override;
+ void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) override;
void EndMoveLoop() override;
private:
@@ -121,7 +122,7 @@ class SimpleTestDragDropClient : public aura::client::DragDropClient,
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
void AddObserver(aura::client::DragDropClientObserver* observer) override;
@@ -260,8 +261,8 @@ bool TestMoveLoop::IsRunning() const {
}
bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
- ::Cursor old_cursor,
- ::Cursor new_cursor) {
+ scoped_refptr<ui::X11Cursor> old_cursor,
+ scoped_refptr<ui::X11Cursor> new_cursor) {
is_running_ = true;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
@@ -269,7 +270,7 @@ bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
return true;
}
-void TestMoveLoop::UpdateCursor(::Cursor cursor) {}
+void TestMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {}
void TestMoveLoop::EndMoveLoop() {
if (is_running_) {
@@ -285,7 +286,10 @@ void TestMoveLoop::EndMoveLoop() {
SimpleTestDragDropClient::SimpleTestDragDropClient(
aura::Window* window,
DesktopNativeCursorManager* cursor_manager)
- : ui::XDragDropClient(this, window->GetHost()->GetAcceleratedWidget()) {}
+ : ui::XDragDropClient(
+ this,
+ static_cast<x11::Window>(window->GetHost()->GetAcceleratedWidget())) {
+}
SimpleTestDragDropClient::~SimpleTestDragDropClient() = default;
@@ -309,7 +313,7 @@ int SimpleTestDragDropClient::StartDragAndDrop(
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
InitDrag(operation, data.get());
auto loop = CreateMoveLoop(this);
@@ -321,13 +325,11 @@ int SimpleTestDragDropClient::StartDragAndDrop(
auto* last_cursor = static_cast<ui::X11Cursor*>(
source_window->GetHost()->last_cursor().platform());
loop_->RunMoveLoop(
- !source_window->HasCapture(),
- last_cursor ? last_cursor->xcursor() : x11::None,
+ !source_window->HasCapture(), last_cursor,
static_cast<ui::X11Cursor*>(
cursor_manager_
->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)
- .platform())
- ->xcursor());
+ .platform()));
auto resulting_operation = negotiated_operation();
CleanupDrag();
@@ -390,7 +392,9 @@ TestDragDropClient::TestDragDropClient(
aura::Window* window,
DesktopNativeCursorManager* cursor_manager)
: SimpleTestDragDropClient(window, cursor_manager),
- source_window_(window->GetHost()->GetAcceleratedWidget()) {}
+ source_window_(
+ static_cast<x11::Window>(window->GetHost()->GetAcceleratedWidget())) {
+}
TestDragDropClient::~TestDragDropClient() = default;
@@ -475,7 +479,7 @@ class X11DragDropClientTest : public ViewsTestBase {
return client_->StartDragAndDrop(
std::move(data), widget_->GetNativeWindow()->GetRootWindow(),
widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+ ui::mojom::DragEventSource::kMouse);
}
// ViewsTestBase:
@@ -483,6 +487,9 @@ class X11DragDropClientTest : public ViewsTestBase {
set_native_widget_type(NativeWidgetType::kDesktop);
ViewsTestBase::SetUp();
+ // TODO(msisov): rewrite these tests for ozone and non-ozone Linux.
+ if (features::IsUsingOzonePlatform())
+ GTEST_SKIP();
// Create widget to initiate the drags.
widget_ = std::make_unique<Widget>();
diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
index fadecc97d9d..55259bda115 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
+++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc
@@ -163,11 +163,11 @@ class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive {
// NULL if the topmost window does not have an associated aura::Window.
aura::Window* FindTopmostLocalProcessWindowAt(int screen_x, int screen_y) {
ui::X11TopmostWindowFinder finder;
- auto widget =
- finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), {});
+ auto widget = static_cast<gfx::AcceleratedWidget>(
+ finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), {}));
return widget != gfx::kNullAcceleratedWidget
? DesktopWindowTreeHostPlatform::GetContentWindowForWidget(
- static_cast<gfx::AcceleratedWidget>(widget))
+ widget)
: nullptr;
}
@@ -182,10 +182,11 @@ class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive {
ignore.insert(ignore_window->GetHost()->GetAcceleratedWidget());
ui::X11TopmostWindowFinder finder;
auto widget =
- finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), ignore);
+ static_cast<gfx::AcceleratedWidget>(finder.FindLocalProcessWindowAt(
+ gfx::Point(screen_x, screen_y), ignore));
return widget != gfx::kNullAcceleratedWidget
? DesktopWindowTreeHostPlatform::GetContentWindowForWidget(
- static_cast<gfx::AcceleratedWidget>(widget))
+ widget)
: nullptr;
}
@@ -200,14 +201,16 @@ TEST_F(X11TopmostWindowFinderTest, Basic) {
std::unique_ptr<Widget> widget1(
CreateAndShowWidget(gfx::Rect(100, 100, 200, 100)));
aura::Window* window1 = widget1->GetNativeWindow();
- x11::Window x11_window1 = window1->GetHost()->GetAcceleratedWidget();
+ x11::Window x11_window1 =
+ static_cast<x11::Window>(window1->GetHost()->GetAcceleratedWidget());
x11::Window x11_window2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200));
std::unique_ptr<Widget> widget3(
CreateAndShowWidget(gfx::Rect(100, 190, 200, 110)));
aura::Window* window3 = widget3->GetNativeWindow();
- x11::Window x11_window3 = window3->GetHost()->GetAcceleratedWidget();
+ x11::Window x11_window3 =
+ static_cast<x11::Window>(window3->GetHost()->GetAcceleratedWidget());
x11::Window windows[] = {x11_window1, x11_window2, x11_window3};
StackingClientListWaiter waiter(windows, base::size(windows));
@@ -249,7 +252,8 @@ TEST_F(X11TopmostWindowFinderTest, Minimized) {
std::unique_ptr<Widget> widget1(
CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
aura::Window* window1 = widget1->GetNativeWindow();
- x11::Window x11_window1 = window1->GetHost()->GetAcceleratedWidget();
+ x11::Window x11_window1 =
+ static_cast<x11::Window>(window1->GetHost()->GetAcceleratedWidget());
x11::Window x11_window2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100));
x11::Window windows[] = {x11_window1, x11_window2};
@@ -287,8 +291,8 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangular) {
std::unique_ptr<Widget> widget1(
CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
- x11::Window window1 =
- widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
+ x11::Window window1 = static_cast<x11::Window>(
+ widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
auto shape1 = std::make_unique<Widget::ShapeRects>();
shape1->emplace_back(0, 10, 10, 90);
shape1->emplace_back(10, 0, 90, 100);
@@ -331,8 +335,8 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) {
std::unique_ptr<Widget> widget1(
CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
- x11::Window window1 =
- widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
+ x11::Window window1 = static_cast<x11::Window>(
+ widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
auto shape1 = std::make_unique<Widget::ShapeRects>();
shape1->emplace_back();
// Widget takes ownership of |shape1|.
@@ -353,8 +357,8 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) {
std::unique_ptr<Widget> widget1(
CreateAndShowWidget(gfx::Rect(100, 100, 100, 100)));
- x11::Window window1 =
- widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
+ x11::Window window1 = static_cast<x11::Window>(
+ widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
auto shape1 = std::make_unique<Widget::ShapeRects>();
shape1->emplace_back();
widget1->SetShape(std::move(shape1));
@@ -372,7 +376,9 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) {
// Test that the TopmostWindowFinder finds windows which belong to menus
// (which may or may not belong to Chrome).
-TEST_F(X11TopmostWindowFinderTest, Menu) {
+//
+// Flakes (https://crbug.com/955316)
+TEST_F(X11TopmostWindowFinderTest, DISABLED_Menu) {
x11::Window window = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100));
x11::Window root = ui::GetX11RootWindow();
diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc
index 51f57997afa..254be7a1692 100644
--- a/chromium/ui/views/widget/native_widget_aura.cc
+++ b/chromium/ui/views/widget/native_widget_aura.cc
@@ -64,7 +64,8 @@
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
#endif
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && defined(OS_LINUX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_LINUX) || defined(OS_CHROMEOS))
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
#endif
@@ -269,7 +270,8 @@ void NativeWidgetAura::InitNativeWidget(Widget::InitParams params) {
void NativeWidgetAura::OnWidgetInitDone() {}
-NonClientFrameView* NativeWidgetAura::CreateNonClientFrameView() {
+std::unique_ptr<NonClientFrameView>
+NativeWidgetAura::CreateNonClientFrameView() {
return nullptr;
}
@@ -699,7 +701,7 @@ void NativeWidgetAura::RunShellDrag(View* view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
if (window_)
views::RunShellDrag(window_, std::move(data), location, operation, source);
}
@@ -1072,7 +1074,8 @@ void NativeWidgetAura::SetInitialFocus(ui::WindowShowState show_state) {
// Widget, public:
namespace {
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && (defined(OS_WIN) || defined(OS_LINUX))
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS))
void CloseWindow(aura::Window* window) {
if (window) {
Widget* widget = Widget::GetWidgetForNativeView(window);
@@ -1102,13 +1105,15 @@ void Widget::CloseAllSecondaryWidgets() {
EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
#endif
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && defined(OS_LINUX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_LINUX) || defined(OS_CHROMEOS))
DesktopWindowTreeHostLinux::CleanUpWindowList(CloseWindow);
#endif
}
const ui::NativeTheme* Widget::GetNativeTheme() const {
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) && defined(OS_LINUX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) && \
+ (defined(OS_LINUX) || defined(OS_CHROMEOS))
const LinuxUI* linux_ui = LinuxUI::instance();
if (linux_ui) {
ui::NativeTheme* native_theme =
diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h
index df9c3d83096..d70aa0a5fb8 100644
--- a/chromium/ui/views/widget/native_widget_aura.h
+++ b/chromium/ui/views/widget/native_widget_aura.h
@@ -22,7 +22,7 @@
#include "ui/wm/public/activation_change_observer.h"
#include "ui/wm/public/activation_delegate.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#error This file must not be included on macOS; Chromium Mac doesn't use Aura.
#endif
@@ -71,7 +71,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate,
// internal::NativeWidgetPrivate:
void InitNativeWidget(Widget::InitParams params) override;
void OnWidgetInitDone() override;
- NonClientFrameView* CreateNonClientFrameView() override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() override;
bool ShouldUseNativeFrame() const override;
bool ShouldWindowContentsBeTransparent() const override;
void FrameTypeChanged() override;
@@ -137,7 +137,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void SchedulePaintInRect(const gfx::Rect& rect) override;
void ScheduleLayout() override;
void SetCursor(gfx::NativeCursor cursor) override;
diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h
index 24b98636751..227123b00f9 100644
--- a/chromium/ui/views/widget/native_widget_mac.h
+++ b/chromium/ui/views/widget/native_widget_mac.h
@@ -96,7 +96,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate,
// internal::NativeWidgetPrivate:
void InitNativeWidget(Widget::InitParams params) override;
void OnWidgetInitDone() override;
- NonClientFrameView* CreateNonClientFrameView() override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() override;
bool ShouldUseNativeFrame() const override;
bool ShouldWindowContentsBeTransparent() const override;
void FrameTypeChanged() override;
@@ -162,7 +162,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) override;
+ ui::mojom::DragEventSource source) override;
void SchedulePaintInRect(const gfx::Rect& rect) override;
void ScheduleLayout() override;
void SetCursor(gfx::NativeCursor cursor) override;
diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm
index 22e0bd3b5ce..4b1cf535a38 100644
--- a/chromium/ui/views/widget/native_widget_mac.mm
+++ b/chromium/ui/views/widget/native_widget_mac.mm
@@ -258,8 +258,9 @@ void NativeWidgetMac::OnWidgetInitDone() {
ns_window_host_->OnWidgetInitDone();
}
-NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
- return new NativeFrameView(GetWidget());
+std::unique_ptr<NonClientFrameView>
+NativeWidgetMac::CreateNonClientFrameView() {
+ return std::make_unique<NativeFrameView>(GetWidget());
}
bool NativeWidgetMac::ShouldUseNativeFrame() const {
@@ -675,7 +676,7 @@ void NativeWidgetMac::RunShellDrag(View* view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
ns_window_host_->drag_drop_client()->StartDragAndDrop(view, std::move(data),
operation, source);
}
diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm
index 9a6b9ac6e90..2c070aee8d3 100644
--- a/chromium/ui/views/widget/native_widget_mac_unittest.mm
+++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm
@@ -870,8 +870,12 @@ TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) {
// to the child window is released inside WidgetOwnerNSWindowAdapter::
// OnWindowWillClose().
[native_parent close];
- EXPECT_TRUE(child_dealloced);
}
+
+ // Check this only once the autorelease pool has been drained: AppKit likes to
+ // autorelease NSWindows when tearing them down, presumably to make UAF bugs
+ // with NSWindows less likely.
+ EXPECT_TRUE(child_dealloced);
EXPECT_TRUE(native_parent_dealloced);
}
@@ -948,9 +952,9 @@ TEST_F(NativeWidgetMacTest, Tooltips) {
const base::string16 long_tooltip(2000, 'W');
// Create a nested layout to test corner cases.
- LabelButton* back = new LabelButton(nullptr, base::string16());
+ LabelButton* back =
+ widget->GetContentsView()->AddChildView(std::make_unique<LabelButton>());
back->SetBounds(10, 10, 80, 80);
- widget->GetContentsView()->AddChildView(back);
widget->Show();
ui::test::EventGenerator event_generator(GetContext(),
@@ -962,9 +966,9 @@ TEST_F(NativeWidgetMacTest, Tooltips) {
// Create a new button for the "front", and set the tooltip, but don't add it
// to the view hierarchy yet.
- LabelButton* front = new LabelButton(nullptr, base::string16());
- front->SetBounds(20, 20, 40, 40);
- front->SetTooltipText(tooltip_front);
+ auto front_managed = std::make_unique<LabelButton>();
+ front_managed->SetBounds(20, 20, 40, 40);
+ front_managed->SetTooltipText(tooltip_front);
// Changing the tooltip text shouldn't require an additional mousemove to take
// effect.
@@ -973,7 +977,7 @@ TEST_F(NativeWidgetMacTest, Tooltips) {
EXPECT_EQ(tooltip_back, TooltipTextForWidget(widget));
// Adding a new view under the mouse should also take immediate effect.
- back->AddChildView(front);
+ LabelButton* front = back->AddChildView(std::move(front_managed));
EXPECT_EQ(tooltip_front, TooltipTextForWidget(widget));
// A long tooltip will be wrapped by Cocoa, but the full string should appear.
@@ -1308,6 +1312,19 @@ TEST_F(NativeWidgetMacTest, ShowAnimationControl) {
EXPECT_FALSE([retained_animation isAnimating]);
}
+// Expect that |children|, the list of child windows of a window that has a
+// sheet open, is logically empty. "Logically empty" accounts for the
+// AppKit-created visual effect window that shows atop windows with open sheets
+// on macOS 11.
+void AssertNoChildrenForWindowWithSheet(NSArray<NSWindow*>* children) {
+ if (base::mac::IsAtLeastOS11()) {
+ ASSERT_EQ(1u, children.count);
+ EXPECT_NSEQ(@"NSSheetEffectDimmingWindow", children[0].className);
+ } else {
+ ASSERT_EQ(0u, children.count);
+ }
+}
+
// Tests behavior of window-modal dialogs, displayed as sheets.
TEST_F(NativeWidgetMacTest, WindowModalSheet) {
NSWindow* native_parent = MakeClosableTitledNativeParent();
@@ -1365,8 +1382,7 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) {
ASSERT_EQ(2u, children.size());
EXPECT_TRUE(children.count(sheet_widget));
- // Sheets are not child windows of their parent NSWindow, though.
- ASSERT_EQ(0u, [native_parent childWindows].count);
+ AssertNoChildrenForWindowWithSheet(native_parent.childWindows);
// Modal, so the close button in the parent window should get disabled.
EXPECT_FALSE([parent_close_button isEnabled]);
@@ -1389,7 +1405,7 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) {
widget_observer.WaitForVisibleCounts(1, 1);
EXPECT_FALSE(sheet_widget->IsVisible());
[native_parent makeKeyAndOrderFront:nil];
- ASSERT_EQ(0u, [native_parent childWindows].count);
+ AssertNoChildrenForWindowWithSheet(native_parent.childWindows);
widget_observer.WaitForVisibleCounts(2, 1);
EXPECT_TRUE(sheet_widget->IsVisible());
@@ -1937,98 +1953,6 @@ TEST_F(NativeWidgetMacTest, ChangeOpacity) {
widget->CloseNow();
}
-// Test that NativeWidgetMac::SchedulePaintInRect correctly passes the dirtyRect
-// parameter to BridgedContentView::drawRect, for a titled window (window with a
-// toolbar).
-TEST_F(NativeWidgetMacTest, SchedulePaintInRect_Titled) {
- Widget* widget = CreateTopLevelPlatformWidget();
-
- gfx::Rect screen_rect(50, 50, 100, 100);
- widget->SetBounds(screen_rect);
-
- // Setup the mock content view for the NSWindow, so that we can intercept
- // drawRect.
- NSWindow* window = widget->GetNativeWindow().GetNativeNSWindow();
- base::scoped_nsobject<MockBridgedView> mock_bridged_view(
- [[MockBridgedView alloc] init]);
- // Reset drawRect count.
- [mock_bridged_view setDrawRectCount:0];
- [window setContentView:mock_bridged_view];
-
- // Ensure the initial draw of the window is done.
- while ([mock_bridged_view drawRectCount] == 0)
- base::RunLoop().RunUntilIdle();
-
- // Add a dummy view to the widget. This will cause SchedulePaint to be called
- // on the dummy view.
- View* dummy_view = new View();
- gfx::Rect dummy_bounds(25, 30, 10, 15);
- dummy_view->SetBoundsRect(dummy_bounds);
- // Reset drawRect count.
- [mock_bridged_view setDrawRectCount:0];
- widget->GetContentsView()->AddChildView(dummy_view);
-
- // SchedulePaint is asyncronous. Wait for drawRect: to be called.
- while ([mock_bridged_view drawRectCount] == 0)
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(1u, [mock_bridged_view drawRectCount]);
- int client_area_height = widget->GetClientAreaBoundsInScreen().height();
- // These are expected dummy_view bounds in AppKit coordinate system. The y
- // coordinate of rect origin is calculated as:
- // client_area_height - 30 (dummy_view's y coordinate) - 15 (dummy view's
- // height).
- gfx::Rect expected_appkit_bounds(25, client_area_height - 45, 10, 15);
- EXPECT_NSEQ(expected_appkit_bounds.ToCGRect(),
- [mock_bridged_view lastDirtyRect]);
- widget->CloseNow();
-}
-
-// Test that NativeWidgetMac::SchedulePaintInRect correctly passes the dirtyRect
-// parameter to BridgedContentView::drawRect, for a borderless window.
-TEST_F(NativeWidgetMacTest, SchedulePaintInRect_Borderless) {
- Widget* widget = CreateTopLevelFramelessPlatformWidget();
-
- gfx::Rect screen_rect(50, 50, 100, 100);
- widget->SetBounds(screen_rect);
-
- // Setup the mock content view for the NSWindow, so that we can intercept
- // drawRect.
- NSWindow* window = widget->GetNativeWindow().GetNativeNSWindow();
- base::scoped_nsobject<MockBridgedView> mock_bridged_view(
- [[MockBridgedView alloc] init]);
- // Reset drawRect count.
- [mock_bridged_view setDrawRectCount:0];
- [window setContentView:mock_bridged_view];
-
- // Ensure the initial draw of the window is done.
- while ([mock_bridged_view drawRectCount] == 0)
- base::RunLoop().RunUntilIdle();
-
- // Add a dummy view to the widget. This will cause SchedulePaint to be called
- // on the dummy view.
- View* dummy_view = new View();
- gfx::Rect dummy_bounds(25, 30, 10, 15);
- dummy_view->SetBoundsRect(dummy_bounds);
- // Reset drawRect count.
- [mock_bridged_view setDrawRectCount:0];
- widget->GetRootView()->AddChildView(dummy_view);
-
- // SchedulePaint is asyncronous. Wait for drawRect: to be called.
- while ([mock_bridged_view drawRectCount] == 0)
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(1u, [mock_bridged_view drawRectCount]);
- // These are expected dummy_view bounds in AppKit coordinate system. The y
- // coordinate of rect origin is calculated as:
- // 100(client area height) - 30 (dummy_view's y coordinate) - 15 (dummy view's
- // height).
- gfx::Rect expected_appkit_bounds(25, 55, 10, 15);
- EXPECT_NSEQ(expected_appkit_bounds.ToCGRect(),
- [mock_bridged_view lastDirtyRect]);
- widget->CloseNow();
-}
-
// Ensure traversing NSView focus correctly updates the views::FocusManager.
TEST_F(NativeWidgetMacTest, ChangeFocusOnChangeFirstResponder) {
Widget* widget = CreateTopLevelPlatformWidget();
diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h
index 1ef89be9ec0..735392692d1 100644
--- a/chromium/ui/views/widget/native_widget_private.h
+++ b/chromium/ui/views/widget/native_widget_private.h
@@ -9,6 +9,7 @@
#include <string>
#include "base/strings/string16.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/widget/native_widget.h"
@@ -86,7 +87,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget {
// Returns a NonClientFrameView for the widget's NonClientView, or NULL if
// the NativeWidget wants no special NonClientFrameView.
- virtual NonClientFrameView* CreateNonClientFrameView() = 0;
+ virtual std::unique_ptr<NonClientFrameView> CreateNonClientFrameView() = 0;
virtual bool ShouldUseNativeFrame() const = 0;
virtual bool ShouldWindowContentsBeTransparent() const = 0;
@@ -206,7 +207,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget {
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) = 0;
+ ui::mojom::DragEventSource source) = 0;
virtual void SchedulePaintInRect(const gfx::Rect& rect) = 0;
virtual void ScheduleLayout() = 0;
virtual void SetCursor(gfx::NativeCursor cursor) = 0;
diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc
index a88f7849e6b..01224fe9b4c 100644
--- a/chromium/ui/views/widget/root_view.cc
+++ b/chromium/ui/views/widget/root_view.cc
@@ -13,7 +13,7 @@
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/cursor/cursor.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/ui_base_switches_util.h"
#include "ui/compositor/layer.h"
#include "ui/events/event.h"
@@ -101,7 +101,7 @@ class PreEventDispatchHandler : public ui::EventHandler {
if (owner_->GetFocusManager()) // Can be NULL in unittests.
v = owner_->GetFocusManager()->GetFocusedView();
// macOS doesn't have keyboard-triggered context menus.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Special case to handle keyboard-triggered context menus.
if (v && v->GetEnabled() &&
((event->key_code() == ui::VKEY_APPS) ||
@@ -149,7 +149,7 @@ class PostEventDispatchHandler : public ui::EventHandler {
target->drag_controller()->CanStartDragForView(target, location,
location))) {
if (target->DoDrag(*event, location,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)) {
+ ui::mojom::DragEventSource::kTouch)) {
event->StopPropagation();
return;
}
@@ -261,7 +261,7 @@ void RootView::DeviceScaleFactorChanged(float old_device_scale_factor,
// Accessibility ---------------------------------------------------------------
void RootView::AnnounceText(const base::string16& text) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// MacOSX has its own API for making announcements; see AnnounceText()
// override in ax_platform_node_mac.[h|mm]
NOTREACHED();
diff --git a/chromium/ui/views/widget/root_view_unittest.cc b/chromium/ui/views/widget/root_view_unittest.cc
index 6994e4864bf..188f08b42b5 100644
--- a/chromium/ui/views/widget/root_view_unittest.cc
+++ b/chromium/ui/views/widget/root_view_unittest.cc
@@ -114,7 +114,7 @@ class TestContextMenuController : public ContextMenuController {
// and VKEY_APPS) by the pre-target handler installed on RootView.
TEST_F(RootViewTest, ContextMenuFromKeyEvent) {
// This behavior is intentionally unsupported on macOS.
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
@@ -766,7 +766,7 @@ TEST_F(RootViewDesktopNativeWidgetTest, SingleLayoutDuringInit) {
widget->CloseNow();
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Tests that AnnounceText sets up the correct text value on the hidden view,
// and that the resulting hidden view actually stays hidden.
@@ -796,7 +796,7 @@ TEST_F(RootViewTest, AnnounceTextTest) {
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_APPLE)
TEST_F(RootViewTest, MouseEventDispatchedToClosestEnabledView) {
Widget widget;
diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc
index a06ebe82c33..f02508580d1 100644
--- a/chromium/ui/views/widget/widget.cc
+++ b/chromium/ui/views/widget/widget.cc
@@ -478,6 +478,14 @@ void Widget::SetContentsView(View* view) {
if (view == GetContentsView())
return;
+ // |non_client_view_| can only be non-null here if RequiresNonClientView() was
+ // true when the widget was initialized. Creating widgets with non-client
+ // views and then setting the contents view can cause subtle problems on
+ // Windows, where the native widget thinks there is still a
+ // |non_client_view_|. If you get this error, either use a different type when
+ // initializing the widget, or don't call SetContentsView().
+ DCHECK(!non_client_view_);
+
root_view_->SetContentsView(view);
// Force a layout now, since the attached hierarchy won't be ready for the
@@ -485,17 +493,6 @@ void Widget::SetContentsView(View* view) {
// calling the widget's size changed handler, since the RootView's bounds may
// not have changed, which will cause the Layout not to be done otherwise.
root_view_->Layout();
-
- if (non_client_view_ != view) {
- // |non_client_view_| can only be non-NULL here if RequiresNonClientView()
- // was true when the widget was initialized. Creating widgets with non
- // client views and then setting the contents view can cause subtle
- // problems on Windows, where the native widget thinks there is still a
- // |non_client_view_|. If you get this error, either use a different type
- // when initializing the widget, or don't call SetContentsView().
- DCHECK(!non_client_view_);
- non_client_view_ = nullptr;
- }
}
View* Widget::GetContentsView() {
@@ -583,7 +580,8 @@ void Widget::CloseWithReason(ClosedReason closed_reason) {
if (block_close_) {
return;
}
- if (non_client_view_ && !non_client_view_->CanClose())
+ if (non_client_view_ && non_client_view_->OnWindowCloseRequested() ==
+ CloseRequestResult::kCannotClose)
return;
// This is the last chance to cancel closing.
@@ -800,7 +798,7 @@ void Widget::RunShellDrag(View* view,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source) {
+ ui::mojom::DragEventSource source) {
dragged_view_ = view;
OnDragWillStart();
@@ -898,9 +896,8 @@ void Widget::ClearNativeFocus() {
native_widget_->ClearNativeFocus();
}
-NonClientFrameView* Widget::CreateNonClientFrameView() {
- NonClientFrameView* frame_view =
- widget_delegate_->CreateNonClientFrameView(this);
+std::unique_ptr<NonClientFrameView> Widget::CreateNonClientFrameView() {
+ auto frame_view = widget_delegate_->CreateNonClientFrameView(this);
if (!frame_view)
frame_view = native_widget_->CreateNonClientFrameView();
if (!frame_view) {
@@ -910,9 +907,7 @@ NonClientFrameView* Widget::CreateNonClientFrameView() {
if (frame_view)
return frame_view;
- CustomFrameView* custom_frame_view = new CustomFrameView;
- custom_frame_view->Init(this);
- return custom_frame_view;
+ return std::make_unique<CustomFrameView>(this);
}
bool Widget::ShouldUseNativeFrame() const {
@@ -1038,12 +1033,17 @@ std::string Widget::GetName() const {
return native_widget_->GetName();
}
+std::unique_ptr<Widget::PaintAsActiveCallbackList::Subscription>
+Widget::RegisterPaintAsActiveChangedCallback(
+ PaintAsActiveCallbackList::CallbackType callback) {
+ return paint_as_active_callbacks_.Add(std::move(callback));
+}
+
std::unique_ptr<Widget::PaintAsActiveLock> Widget::LockPaintAsActive() {
const bool was_paint_as_active = ShouldPaintAsActive();
++paint_as_active_refcount_;
- const bool paint_as_active = ShouldPaintAsActive();
- if (paint_as_active != was_paint_as_active)
- UpdatePaintAsActiveState(paint_as_active);
+ if (ShouldPaintAsActive() != was_paint_as_active)
+ paint_as_active_callbacks_.Notify();
return std::make_unique<PaintAsActiveLockImpl>(
weak_ptr_factory_.GetWeakPtr());
}
@@ -1092,9 +1092,8 @@ bool Widget::OnNativeWidgetActivationChanged(bool active) {
const bool was_paint_as_active = ShouldPaintAsActive();
native_widget_active_ = active;
- const bool paint_as_active = ShouldPaintAsActive();
- if (paint_as_active != was_paint_as_active)
- UpdatePaintAsActiveState(paint_as_active);
+ if (ShouldPaintAsActive() != was_paint_as_active)
+ paint_as_active_callbacks_.Notify();
return true;
}
@@ -1482,7 +1481,7 @@ void Widget::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
DCHECK(observer_manager_.IsObserving(observed_theme));
-#if defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_APPLE) || defined(OS_WIN)
ui::NativeTheme* current_native_theme = observed_theme;
#else
ui::NativeTheme* current_native_theme = GetNativeTheme();
@@ -1631,17 +1630,8 @@ void Widget::UnlockPaintAsActive() {
const bool was_paint_as_active = ShouldPaintAsActive();
DCHECK_GT(paint_as_active_refcount_, 0U);
--paint_as_active_refcount_;
- const bool paint_as_active = ShouldPaintAsActive();
- if (paint_as_active != was_paint_as_active)
- UpdatePaintAsActiveState(paint_as_active);
-}
-
-void Widget::UpdatePaintAsActiveState(bool paint_as_active) {
- if (non_client_view_)
- non_client_view_->frame_view()->PaintAsActiveChanged(paint_as_active);
-
- for (WidgetObserver& observer : observers_)
- observer.OnWidgetPaintAsActiveChanged(this, paint_as_active);
+ if (ShouldPaintAsActive() != was_paint_as_active)
+ paint_as_active_callbacks_.Notify();
}
void Widget::ClearFocusFromWidget() {
diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h
index 0cbc349dd3d..032f77d5738 100644
--- a/chromium/ui/views/widget/widget.h
+++ b/chromium/ui/views/widget/widget.h
@@ -16,6 +16,7 @@
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "build/build_config.h"
+#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-forward.h"
#include "ui/base/ui_base_types.h"
#include "ui/events/event_source.h"
#include "ui/gfx/geometry/rect.h"
@@ -62,6 +63,8 @@ class NativeWidgetPrivate;
class RootView;
} // namespace internal
+enum class CloseRequestResult { kCanClose, kCannotClose };
+
////////////////////////////////////////////////////////////////////////////////
// Widget class
//
@@ -94,6 +97,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
public:
using Widgets = std::set<Widget*>;
using ShapeRects = std::vector<gfx::Rect>;
+ using PaintAsActiveCallbackList = base::RepeatingClosureList;
enum class FrameType {
kDefault, // Use whatever the default would be.
@@ -714,7 +718,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
std::unique_ptr<ui::OSExchangeData> data,
const gfx::Point& location,
int operation,
- ui::DragDropTypes::DragEventSource source);
+ ui::mojom::DragEventSource source);
// Returns the view that requested the current drag operation via
// RunShellDrag(), or NULL if there is no such view or drag operation.
@@ -778,7 +782,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// WidgetDelegate is given the first opportunity to create one, followed by
// the NativeWidget implementation. If both return NULL, a default one is
// created.
- virtual NonClientFrameView* CreateNonClientFrameView();
+ virtual std::unique_ptr<NonClientFrameView> CreateNonClientFrameView();
// Whether we should be using a native frame.
bool ShouldUseNativeFrame() const;
@@ -906,6 +910,12 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// Returns the internal name for this Widget and NativeWidget.
std::string GetName() const;
+ // Registers |callback| to be called whenever the "paint as active" state
+ // changes.
+ std::unique_ptr<PaintAsActiveCallbackList::Subscription>
+ RegisterPaintAsActiveChangedCallback(
+ PaintAsActiveCallbackList::CallbackType callback);
+
// Prevents the widget from being rendered as inactive during the lifetime of
// the returned lock. Multiple locks can exist with disjoint lifetimes. The
// returned lock can safely outlive the associated widget.
@@ -1025,9 +1035,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// Undoes LockPaintAsActive(). Called by PaintAsActiveLock destructor.
void UnlockPaintAsActive();
- // Notifies the window frame that the active rendering state has changed.
- void UpdatePaintAsActiveState(bool paint_as_active);
-
// If a descendent of |root_view_| is focused, then clear the focus.
void ClearFocusFromWidget();
@@ -1039,6 +1046,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
base::ObserverList<WidgetRemovalsObserver>::Unchecked removals_observers_;
+ PaintAsActiveCallbackList paint_as_active_callbacks_;
+
// Non-owned pointer to the Widget's delegate. If a NULL delegate is supplied
// to Init() a default WidgetDelegate is created.
WidgetDelegate* widget_delegate_ = nullptr;
diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc
index 6916ad1c393..dc582b8586a 100644
--- a/chromium/ui/views/widget/widget_delegate.cc
+++ b/chromium/ui/views/widget/widget_delegate.cc
@@ -4,6 +4,9 @@
#include "ui/views/widget/widget_delegate.h"
+#include <memory>
+#include <utility>
+
#include "base/check.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_enums.mojom.h"
@@ -203,7 +206,8 @@ ClientView* WidgetDelegate::CreateClientView(Widget* widget) {
return new ClientView(widget, GetContentsView());
}
-NonClientFrameView* WidgetDelegate::CreateNonClientFrameView(Widget* widget) {
+std::unique_ptr<NonClientFrameView> WidgetDelegate::CreateNonClientFrameView(
+ Widget* widget) {
return nullptr;
}
@@ -211,10 +215,6 @@ View* WidgetDelegate::CreateOverlayView() {
return nullptr;
}
-bool WidgetDelegate::WillProcessWorkAreaChange() const {
- return false;
-}
-
bool WidgetDelegate::WidgetHasHitTestMask() const {
return false;
}
diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h
index 6b4ef19ee72..fdf697d5469 100644
--- a/chromium/ui/views/widget/widget_delegate.h
+++ b/chromium/ui/views/widget/widget_delegate.h
@@ -5,6 +5,7 @@
#ifndef UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_
#define UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_
+#include <memory>
#include <string>
#include <vector>
@@ -239,7 +240,8 @@ class VIEWS_EXPORT WidgetDelegate {
// Called by the Widget to create the NonClient Frame View for this widget.
// Return NULL to use the default one.
- virtual NonClientFrameView* CreateNonClientFrameView(Widget* widget);
+ virtual std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget);
// Called by the Widget to create the overlay View for this widget. Return
// NULL for no overlay. The overlay View will fill the Widget and sit on top
@@ -247,12 +249,6 @@ class VIEWS_EXPORT WidgetDelegate {
// targeting).
virtual View* CreateOverlayView();
- // Returns true if the window can be notified with the work area change.
- // Otherwise, the work area change for the top window will be processed by
- // the default window manager. In some cases, like panel, we would like to
- // manage the positions by ourselves.
- virtual bool WillProcessWorkAreaChange() const;
-
// Returns true if window has a hit-test mask.
virtual bool WidgetHasHitTestMask() const;
diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc
index a333ca057ae..c495bbbcc99 100644
--- a/chromium/ui/views/widget/widget_interactive_uitest.cc
+++ b/chromium/ui/views/widget/widget_interactive_uitest.cc
@@ -182,7 +182,7 @@ ui::WindowShowState GetWidgetShowState(const Widget* widget) {
// Give the OS an opportunity to process messages for an activation change, when
// there is actually no change expected (e.g. ShowInactive()).
void RunPendingMessagesForActiveStatusChange() {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, a single spin is *usually* enough. It isn't when a widget is shown
// and made active in two steps, so tests should follow up with a ShowSync()
// or ActivateSync to ensure a consistent state.
@@ -209,7 +209,7 @@ void ShowSync(Widget* widget) {
}
void DeactivateSync(Widget* widget) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Deactivation of a window isn't a concept on Mac: If an application is
// active and it has any activatable windows, then one of them is always
// active. But we can simulate deactivation (e.g. as if another application
@@ -827,7 +827,7 @@ class ModalDialogDelegate : public DialogDelegateView {
DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
};
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
// Tests whether the focused window is set correctly when a modal window is
// created and destroyed. When it is destroyed it should focus the owner window.
TEST_F(DesktopWidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
@@ -870,7 +870,7 @@ TEST_F(DesktopWidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
EXPECT_EQ(gfx::kNullNativeView, focus_changes[1]);
EXPECT_EQ(modal_native_view, focus_changes[2]);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Window modal dialogs on Mac are "sheets", which animate to close before
// activating their parent widget.
views::test::WidgetActivationWaiter waiter(&top_level_widget, true);
@@ -1054,7 +1054,7 @@ TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
}
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
ShowSync(widget.get());
@@ -1070,13 +1070,13 @@ TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
EXPECT_EQ(GetWidgetShowState(widget2.get()), ui::SHOW_STATE_INACTIVE);
EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
}
-#endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
// ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
// currently only Desktop widgets and fullscreen changes have to coordinate with
// the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
// Maximize on mac is also (intentionally) a no-op.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
#else
#define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
@@ -1222,7 +1222,7 @@ TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) {
#endif // defined(OS_WIN)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
// Tests that minimizing a widget causes the gesture_handler
// to be cleared when the widget is minimized.
TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) {
@@ -1242,7 +1242,8 @@ TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) {
}
#endif
-#if defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && \
+ BUILDFLAG(ENABLE_DESKTOP_AURA)
// Tests that when a desktop native widget has modal transient child, it should
// avoid restore focused view itself as the modal transient child window will do
// that, thus avoids having multiple focused view visually (crbug.com/727641).
@@ -1289,7 +1290,8 @@ TEST_F(DesktopWidgetTestInteractive,
EXPECT_TRUE(dialog_textfield->HasFocus());
EXPECT_FALSE(textfield->HasFocus());
}
-#endif // defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) &&
+ // BUILDFLAG(ENABLE_DESKTOP_AURA)
namespace {
@@ -1395,7 +1397,7 @@ TEST_F(WidgetCaptureTest, Capture) {
TestCapture(false);
}
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
// See description in TestCapture(). Creates DesktopNativeWidget.
TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
TestCapture(true);
@@ -1663,7 +1665,7 @@ TEST_F(WidgetCaptureTest, GrabUngrab) {
// Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
// was deprecated. It does have application modal windows, but only Ash requests
// those.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_SystemModalWindowReleasesCapture \
DISABLED_SystemModalWindowReleasesCapture
#elif defined(OS_CHROMEOS)
@@ -1718,14 +1720,15 @@ TEST_F(WidgetCaptureTest, MAYBE_SystemModalWindowReleasesCapture) {
// Regression test for http://crbug.com/382421 (Linux-Aura issue).
// TODO(pkotwicz): Make test pass on CrOS and Windows.
// TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
-#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_CHROMEOS) || defined(OS_APPLE)
#define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
#else
#define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
#endif
// Test that a synthetic mouse exit is sent to the widget which was handling
-// mouse events when a different widget grabs capture.
+// mouse events when a different widget grabs capture. Except for Windows,
+// which does not send a synthetic mouse exit.
TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
Widget widget1;
Widget::InitParams params1 =
@@ -1754,9 +1757,15 @@ TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
widget2.SetCapture(nullptr);
EXPECT_EQ(0, mouse_view1->EnteredCalls());
- // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
- // in addition to the one generated by Chrome.
- EXPECT_LT(0, mouse_view1->ExitedCalls());
+ // On Windows, Chrome doesn't synthesize a separate mouse exited event.
+ // Instead, it uses ::TrackMouseEvent to get notified of the mouse leaving.
+ // Calling SetCapture does not cause Windows to generate a WM_MOUSELEAVE
+ // event. See WindowEventDispatcher::OnOtherRootGotCapture() for more info.
+#if defined(OS_WIN)
+ EXPECT_EQ(0, mouse_view1->ExitedCalls());
+#else
+ EXPECT_EQ(1, mouse_view1->ExitedCalls());
+#endif // OS_WIN
}
namespace {
@@ -1806,7 +1815,7 @@ TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
child->AddObserver(&observer);
child->Show();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, activation is asynchronous. A single trip to the runloop should be
// sufficient. On Aura platforms, note that since the child widget isn't top-
// level, the aura window manager gets asked whether the widget is active, not
@@ -1917,7 +1926,7 @@ class WidgetInputMethodInteractiveTest : public DesktopWidgetTestInteractive {
DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest);
};
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_Activation DISABLED_Activation
#else
#define MAYBE_Activation Activation
@@ -1962,7 +1971,7 @@ TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
// Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
// DNWA (which just activates the last active window) and involves the
// AuraTestHelper which sets the input method as DummyInputMethod.
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
DeactivateSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
@@ -2010,7 +2019,7 @@ TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
// Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
// DNWA (which just activates the last active window) and involves the
// AuraTestHelper which sets the input method as DummyInputMethod.
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
DeactivateSync(parent.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
parent->GetInputMethod()->GetTextInputType());
diff --git a/chromium/ui/views/widget/widget_observer.h b/chromium/ui/views/widget/widget_observer.h
index e1168897442..51adacfee40 100644
--- a/chromium/ui/views/widget/widget_observer.h
+++ b/chromium/ui/views/widget/widget_observer.h
@@ -41,11 +41,6 @@ class VIEWS_EXPORT WidgetObserver : public base::CheckedObserver {
virtual void OnWidgetDragWillStart(Widget* widget) {}
virtual void OnWidgetDragComplete(Widget* widget) {}
- // Called when the widget transitions from a state in which it should render
- // as active to one in which it should render as inactive or vice-versa.
- virtual void OnWidgetPaintAsActiveChanged(Widget* widget,
- bool paint_as_active) {}
-
virtual void OnWidgetVisibilityChanging(Widget* widget, bool visible) {}
virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) {}
diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc
index 469e9150908..b4061bd8885 100644
--- a/chromium/ui/views/widget/widget_unittest.cc
+++ b/chromium/ui/views/widget/widget_unittest.cc
@@ -52,7 +52,7 @@
#include "ui/views/win/hwnd_util.h"
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "base/mac/mac_util.h"
#endif
@@ -675,7 +675,7 @@ class WidgetObserverTest : public WidgetTest, public WidgetObserver {
};
// This test appears to be flaky on Mac.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_ActivationChange DISABLED_ActivationChange
#else
#define MAYBE_ActivationChange ActivationChange
@@ -997,10 +997,14 @@ TEST_F(WidgetTest, GetWindowPlacement) {
#else
TEST_F(DesktopWidgetTest, GetWindowPlacement) {
#endif
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
if (base::mac::IsOS10_10())
return; // Fails when swarmed. http://crbug.com/660582
#endif
+#if defined(USE_X11)
+ if (features::IsUsingOzonePlatform())
+ return; // TODO(https://crbug.com/1109112): Will be enabled later.
+#endif
WidgetAutoclosePtr widget;
widget.reset(CreateTopLevelNativeWidget());
@@ -1018,7 +1022,7 @@ TEST_F(DesktopWidgetTest, GetWindowPlacement) {
native_widget->GetWindowPlacement(&restored_bounds, &show_state);
EXPECT_EQ(expected_bounds, restored_bounds);
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Non-desktop/Ash widgets start off in "default" until a Restore().
EXPECT_EQ(ui::SHOW_STATE_DEFAULT, show_state);
widget->Restore();
@@ -1185,7 +1189,7 @@ TEST_F(DesktopWidgetTest, MAYBE_GetRestoredBounds) {
toplevel->Maximize();
RunPendingMessages();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Current expectation on Mac is to do nothing on Maximize.
EXPECT_EQ(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds());
#else
@@ -1257,14 +1261,14 @@ TEST_F(DesktopWidgetTest, TestViewWidthAfterMinimizingWidget) {
std::unique_ptr<Widget> widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
NonClientView* non_client_view = widget->non_client_view();
- NonClientFrameView* frame_view = new MinimumSizeFrameView(widget.get());
- non_client_view->SetFrameView(frame_view);
+ non_client_view->SetFrameView(
+ std::make_unique<MinimumSizeFrameView>(widget.get()));
// Setting the frame view doesn't do a layout, so force one.
non_client_view->Layout();
widget->Show();
- EXPECT_NE(0, frame_view->width());
+ EXPECT_NE(0, non_client_view->frame_view()->width());
widget->Minimize();
- EXPECT_EQ(0, frame_view->width());
+ EXPECT_EQ(0, non_client_view->frame_view()->width());
}
#endif
@@ -1378,8 +1382,8 @@ TEST_F(DesktopWidgetTest, TestWindowVisibilityAfterHide) {
std::unique_ptr<Widget> widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
NonClientView* non_client_view = widget->non_client_view();
- NonClientFrameView* frame_view = new MinimumSizeFrameView(widget.get());
- non_client_view->SetFrameView(frame_view);
+ non_client_view->SetFrameView(
+ std::make_unique<MinimumSizeFrameView>(widget.get()));
widget->Show();
EXPECT_TRUE(IsNativeWindowVisible(widget->GetNativeWindow()));
@@ -1625,7 +1629,7 @@ class MousePressEventConsumer : public ui::EventHandler {
} // namespace
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
// Test that mouse presses and mouse releases are dispatched normally when a
// touch is down.
@@ -1652,7 +1656,7 @@ TEST_F(WidgetTest, MouseEventDispatchWhileTouchIsDown) {
widget->CloseNow();
}
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
// Tests that when there is no active capture, that a mouse press causes capture
// to be set.
@@ -2026,7 +2030,7 @@ TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) {
}
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if !defined(OS_MACOSX) || defined(USE_AURA)
+#if !defined(OS_APPLE) || defined(USE_AURA)
TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) {
Widget* widget = new Widget;
@@ -2049,7 +2053,7 @@ TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) {
// Yay we did not crash!
}
-#endif // !defined(OS_MACOSX) || defined(USE_AURA)
+#endif // !defined(OS_APPLE) || defined(USE_AURA)
// See description of RunGetNativeThemeFromDestructor() for details.
class GetNativeThemeFromDestructorView : public WidgetDelegateView {
@@ -2206,7 +2210,7 @@ TEST_F(WidgetTest, CloseWidgetWhileAnimating) {
// Test Widget::CloseAllSecondaryWidgets works as expected across platforms.
// ChromeOS doesn't implement or need CloseAllSecondaryWidgets() since
// everything is under a single root window.
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
TEST_F(DesktopWidgetTest, CloseAllSecondaryWidgets) {
Widget* widget1 = CreateTopLevelNativeWidget();
Widget* widget2 = CreateTopLevelNativeWidget();
@@ -2270,7 +2274,7 @@ TEST_F(WidgetTest, NoCrashOnWidgetDeleteWithPendingEvents) {
generator.MoveMouseTo(10, 10);
// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
generator.ClickLeftButton();
#else
generator.PressTouch();
@@ -3098,8 +3102,9 @@ class FullscreenAwareFrame : public views::NonClientFrameView {
// changing its size or title.
TEST_F(WidgetTest, FullscreenFrameLayout) {
WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
- FullscreenAwareFrame* frame = new FullscreenAwareFrame(widget.get());
- widget->non_client_view()->SetFrameView(frame); // Owns |frame|.
+ auto frame_view = std::make_unique<FullscreenAwareFrame>(widget.get());
+ FullscreenAwareFrame* frame = frame_view.get();
+ widget->non_client_view()->SetFrameView(std::move(frame_view));
widget->Maximize();
RunPendingMessages();
@@ -3684,7 +3689,7 @@ class WidgetShadowTest : public WidgetTest {
bool force_child_ = false;
private:
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
void InitControllers() {}
#else
class TestFocusRules : public wm::BaseFocusRules {
@@ -3708,13 +3713,13 @@ class WidgetShadowTest : public WidgetTest {
std::unique_ptr<wm::FocusController> focus_controller_;
std::unique_ptr<wm::ShadowController> shadow_controller_;
-#endif // !BUILDFLAG(ENABLE_DESKTOP_AURA) && !defined(OS_MACOSX)
+#endif // !BUILDFLAG(ENABLE_DESKTOP_AURA) && !defined(OS_APPLE)
DISALLOW_COPY_AND_ASSIGN(WidgetShadowTest);
};
// Disabled on Mac: All drop shadows are managed out of process for now.
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#define MAYBE_ShadowsInRootWindow DISABLED_ShadowsInRootWindow
#else
#define MAYBE_ShadowsInRootWindow ShadowsInRootWindow
@@ -3863,7 +3868,7 @@ TEST_F(DesktopWidgetTest, WindowModalOwnerDestroyedEnabledTest) {
#endif // defined(OS_WIN)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
namespace {
@@ -3890,7 +3895,7 @@ class CompositingWidgetTest : public DesktopWidgetTest {
const Widget::InitParams::WindowOpacity opacity) {
opacity_ = opacity;
for (const auto& widget_type : widget_types_) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Tooltips are native on Mac. See NativeWidgetNSWindowBridge::Init.
if (widget_type == Widget::InitParams::TYPE_TOOLTIP)
continue;
@@ -3907,7 +3912,7 @@ class CompositingWidgetTest : public DesktopWidgetTest {
widget_type == Widget::InitParams::TYPE_CONTROL)
continue;
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Mac always always has a compositing window manager, but doesn't have
// transparent titlebars which is what ShouldWindowContentsBeTransparent()
// is currently used for. Asking for transparency should get it. Note that
@@ -3923,6 +3928,8 @@ class CompositingWidgetTest : public DesktopWidgetTest {
should_be_transparent);
#if defined(USE_X11)
+ if (features::IsUsingOzonePlatform())
+ return;
if (HasCompositingManager() &&
(widget_type == Widget::InitParams::TYPE_DRAG ||
widget_type == Widget::InitParams::TYPE_WINDOW)) {
@@ -3957,7 +3964,7 @@ TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetTranslucent) {
CheckAllWidgetsForOpacity(Widget::InitParams::WindowOpacity::kTranslucent);
}
-#endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_MACOSX)
+#endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
} // namespace test
} // namespace views
diff --git a/chromium/ui/views/widget/window_reorderer.cc b/chromium/ui/views/widget/window_reorderer.cc
index b1ac11fc465..cba1414452d 100644
--- a/chromium/ui/views/widget/window_reorderer.cc
+++ b/chromium/ui/views/widget/window_reorderer.cc
@@ -14,6 +14,7 @@
#include "base/containers/adapters.h"
#include "base/macros.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_occlusion_tracker.h"
#include "ui/views/view.h"
#include "ui/views/view_constants_aura.h"
@@ -160,6 +161,8 @@ void WindowReorderer::ReorderChildWindows() {
std::vector<ui::Layer*> children_layer_order;
+ aura::WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
+
// For the sake of simplicity, reorder both the layers owned by views and the
// layers of windows associated with a view. Iterate through
// |view_with_layer_order| backwards and stack windows at the bottom so that
diff --git a/chromium/ui/views/win/fullscreen_handler.cc b/chromium/ui/views/win/fullscreen_handler.cc
index 8791362556f..98a19e62fca 100644
--- a/chromium/ui/views/win/fullscreen_handler.cc
+++ b/chromium/ui/views/win/fullscreen_handler.cc
@@ -72,8 +72,7 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen) {
if (fullscreen_) {
// Set new window style and size.
- SetWindowLong(hwnd_, GWL_STYLE,
- saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
+ SetWindowLong(hwnd_, GWL_STYLE, saved_window_info_.style & ~WS_CAPTION);
SetWindowLong(
hwnd_, GWL_EXSTYLE,
saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc
index db4fe784c7e..5e8d267d868 100644
--- a/chromium/ui/views/win/hwnd_message_handler.cc
+++ b/chromium/ui/views/win/hwnd_message_handler.cc
@@ -18,9 +18,9 @@
#include "base/debug/alias.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/metrics/histogram_functions.h"
#include "base/single_thread_task_runner.h"
+#include "base/task/current_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
@@ -791,7 +791,7 @@ bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset,
MoveLoopMouseWatcher watcher(this, hide_on_escape);
// In Aura, we handle touch events asynchronously. So we need to allow nested
// tasks while in windows move loop.
- base::MessageLoopCurrent::ScopedNestableTaskAllower allow_nested;
+ base::CurrentThread::ScopedNestableTaskAllower allow_nested;
SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos());
// Windows doesn't appear to offer a way to determine whether the user
@@ -2532,8 +2532,7 @@ LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) {
}
void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) {
- if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA) &&
- !delegate_->WillProcessWorkAreaChange()) {
+ if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA)) {
// Fire a dummy SetWindowPos() call, so we'll trip the code in
// OnWindowPosChanging() below that notices work area changes.
::SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0,
diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h
index 98efa2b7c40..7344cdd4cf7 100644
--- a/chromium/ui/views/win/hwnd_message_handler_delegate.h
+++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h
@@ -82,8 +82,6 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate {
// window.
virtual int GetInitialShowState() const = 0;
- virtual bool WillProcessWorkAreaChange() const = 0;
-
virtual int GetNonClientComponent(const gfx::Point& point) const = 0;
virtual void GetWindowMask(const gfx::Size& size, SkPath* mask) = 0;
diff --git a/chromium/ui/views/window/client_view.cc b/chromium/ui/views/window/client_view.cc
index 13562905b1a..d62ef27e038 100644
--- a/chromium/ui/views/window/client_view.cc
+++ b/chromium/ui/views/window/client_view.cc
@@ -28,8 +28,8 @@ int ClientView::NonClientHitTest(const gfx::Point& point) {
return bounds().Contains(point) ? HTCLIENT : HTNOWHERE;
}
-bool ClientView::CanClose() {
- return true;
+CloseRequestResult ClientView::OnWindowCloseRequested() {
+ return CloseRequestResult::kCanClose;
}
void ClientView::WidgetClosing() {}
diff --git a/chromium/ui/views/window/client_view.h b/chromium/ui/views/window/client_view.h
index b4a856e1f20..0f69d64c838 100644
--- a/chromium/ui/views/window/client_view.h
+++ b/chromium/ui/views/window/client_view.h
@@ -10,6 +10,7 @@
namespace views {
class Widget;
+enum class CloseRequestResult;
///////////////////////////////////////////////////////////////////////////////
// ClientView
@@ -29,11 +30,11 @@ class VIEWS_EXPORT ClientView : public View {
ClientView(Widget* widget, View* contents_view);
~ClientView() override = default;
- // Returns true to signal that the Widget can be closed. Specialized
+ // Returned value signals whether the Widget can be closed. Specialized
// ClientView subclasses can override this default behavior to allow the
// close to be blocked until the user corrects mistakes, accepts a warning
// dialog, etc.
- virtual bool CanClose();
+ virtual CloseRequestResult OnWindowCloseRequested();
// Notification that the widget is closing.
virtual void WidgetClosing();
diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc
index c0bc655d445..5b5cfff9cf5 100644
--- a/chromium/ui/views/window/custom_frame_view.cc
+++ b/chromium/ui/views/window/custom_frame_view.cc
@@ -67,16 +67,8 @@ void LayoutButton(ImageButton* button, const gfx::Rect& bounds) {
} // namespace
-///////////////////////////////////////////////////////////////////////////////
-// CustomFrameView, public:
-
-CustomFrameView::CustomFrameView() : frame_background_(new FrameBackground()) {}
-
-CustomFrameView::~CustomFrameView() = default;
-
-void CustomFrameView::Init(Widget* frame) {
- frame_ = frame;
-
+CustomFrameView::CustomFrameView(Widget* frame)
+ : frame_(frame), frame_background_(new FrameBackground()) {
close_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_CLOSE, IDR_CLOSE,
IDR_CLOSE_H, IDR_CLOSE_P);
minimize_button_ = InitWindowCaptionButton(
@@ -92,8 +84,7 @@ void CustomFrameView::Init(Widget* frame) {
}
}
-///////////////////////////////////////////////////////////////////////////////
-// CustomFrameView, NonClientFrameView implementation:
+CustomFrameView::~CustomFrameView() = default;
gfx::Rect CustomFrameView::GetBoundsForClientView() const {
return client_view_bounds_;
@@ -186,13 +177,6 @@ void CustomFrameView::SizeConstraintsChanged() {
LayoutWindowControls();
}
-void CustomFrameView::PaintAsActiveChanged(bool active) {
- SchedulePaint();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CustomFrameView, View overrides:
-
void CustomFrameView::OnPaint(gfx::Canvas* canvas) {
if (!ShouldShowTitleBarAndBorder())
return;
@@ -247,9 +231,6 @@ gfx::Size CustomFrameView::GetMaximumSize() const {
max_size.height() == 0 ? 0 : converted_size.height());
}
-///////////////////////////////////////////////////////////////////////////////
-// CustomFrameView, ButtonListener implementation:
-
void CustomFrameView::ButtonPressed(Button* sender, const ui::Event& event) {
if (sender == close_button_)
frame_->CloseWithReason(views::Widget::ClosedReason::kCloseButtonClicked);
@@ -261,9 +242,6 @@ void CustomFrameView::ButtonPressed(Button* sender, const ui::Event& event) {
frame_->Restore();
}
-///////////////////////////////////////////////////////////////////////////////
-// CustomFrameView, private:
-
int CustomFrameView::FrameBorderThickness() const {
return frame_->IsMaximized() ? 0 : kFrameBorderThickness;
}
diff --git a/chromium/ui/views/window/custom_frame_view.h b/chromium/ui/views/window/custom_frame_view.h
index 759b9f3f864..50ff6791aea 100644
--- a/chromium/ui/views/window/custom_frame_view.h
+++ b/chromium/ui/views/window/custom_frame_view.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/button/button.h"
+#include "ui/views/widget/widget.h"
#include "ui/views/window/frame_buttons.h"
#include "ui/views/window/non_client_view.h"
@@ -35,11 +36,9 @@ class Widget;
class VIEWS_EXPORT CustomFrameView : public NonClientFrameView,
public ButtonListener {
public:
- CustomFrameView();
+ explicit CustomFrameView(Widget* frame);
~CustomFrameView() override;
- void Init(Widget* frame);
-
// Overridden from NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override;
gfx::Rect GetWindowBoundsForClientBounds(
@@ -50,7 +49,6 @@ class VIEWS_EXPORT CustomFrameView : public NonClientFrameView,
void UpdateWindowIcon() override;
void UpdateWindowTitle() override;
void SizeConstraintsChanged() override;
- void PaintAsActiveChanged(bool active) override;
// Overridden from View:
void OnPaint(gfx::Canvas* canvas) override;
@@ -142,16 +140,16 @@ class VIEWS_EXPORT CustomFrameView : public NonClientFrameView,
gfx::Rect title_bounds_;
// Not owned.
- Widget* frame_ = nullptr;
+ Widget* const frame_;
// The icon of this window. May be NULL.
ImageButton* window_icon_ = nullptr;
// Window caption buttons.
- ImageButton* minimize_button_ = nullptr;
- ImageButton* maximize_button_ = nullptr;
- ImageButton* restore_button_ = nullptr;
- ImageButton* close_button_ = nullptr;
+ ImageButton* minimize_button_;
+ ImageButton* maximize_button_;
+ ImageButton* restore_button_;
+ ImageButton* close_button_;
// Background painter for the window frame.
std::unique_ptr<FrameBackground> frame_background_;
@@ -161,6 +159,12 @@ class VIEWS_EXPORT CustomFrameView : public NonClientFrameView,
int minimum_title_bar_x_ = 0;
int maximum_title_bar_x_ = -1;
+ std::unique_ptr<Widget::PaintAsActiveCallbackList::Subscription>
+ paint_as_active_subscription_ =
+ frame_->RegisterPaintAsActiveChangedCallback(
+ base::BindRepeating(&CustomFrameView::SchedulePaint,
+ base::Unretained(this)));
+
DISALLOW_COPY_AND_ASSIGN(CustomFrameView);
};
diff --git a/chromium/ui/views/window/custom_frame_view_unittest.cc b/chromium/ui/views/window/custom_frame_view_unittest.cc
index 37ed1a60201..6a6ab78c313 100644
--- a/chromium/ui/views/window/custom_frame_view_unittest.cc
+++ b/chromium/ui/views/window/custom_frame_view_unittest.cc
@@ -81,8 +81,9 @@ void CustomFrameViewTest::SetUp() {
params.remove_standard_frame = true;
widget_->Init(std::move(params));
- custom_frame_view_ = new CustomFrameView;
- widget_->non_client_view()->SetFrameView(custom_frame_view_);
+ auto custom_frame_view = std::make_unique<CustomFrameView>(widget_);
+ custom_frame_view_ = custom_frame_view.get();
+ widget_->non_client_view()->SetFrameView(std::move(custom_frame_view));
}
void CustomFrameViewTest::TearDown() {
@@ -112,11 +113,8 @@ TEST_F(CustomFrameViewTest, DefaultButtons) {
// Tests that layout places the buttons in order, that the restore button is
// hidden and the buttons are placed after the title.
TEST_F(CustomFrameViewTest, DefaultButtonLayout) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
EXPECT_LT(minimize_button()->x(), maximize_button()->x());
EXPECT_LT(maximize_button()->x(), close_button()->x());
@@ -128,9 +126,6 @@ TEST_F(CustomFrameViewTest, DefaultButtonLayout) {
// Tests that setting the buttons to leading places them before the title.
TEST_F(CustomFrameViewTest, LeadingButtonLayout) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
-
std::vector<views::FrameButton> leading;
leading.push_back(views::FrameButton::kClose);
leading.push_back(views::FrameButton::kMinimize);
@@ -140,9 +135,8 @@ TEST_F(CustomFrameViewTest, LeadingButtonLayout) {
SetWindowButtonOrder(leading, trailing);
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
EXPECT_LT(close_button()->x(), minimize_button()->x());
EXPECT_LT(minimize_button()->x(), maximize_button()->x());
EXPECT_FALSE(restore_button()->GetVisible());
@@ -153,19 +147,16 @@ TEST_F(CustomFrameViewTest, LeadingButtonLayout) {
// Tests that layouts occurring while maximized swap the maximize button for the
// restore button
TEST_F(CustomFrameViewTest, MaximizeRevealsRestoreButton) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
ASSERT_FALSE(restore_button()->GetVisible());
ASSERT_TRUE(maximize_button()->GetVisible());
- parent->Maximize();
- view->Layout();
+ widget()->Maximize();
+ custom_frame_view()->Layout();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// Restore buttons do not exist on Mac. The maximize button is instead a kind
// of toggle, but has no effect on frame decorations.
EXPECT_FALSE(restore_button()->GetVisible());
@@ -179,13 +170,10 @@ TEST_F(CustomFrameViewTest, MaximizeRevealsRestoreButton) {
// Tests that when the parent cannot maximize that the maximize button is not
// visible
TEST_F(CustomFrameViewTest, CannotMaximizeHidesButton) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
widget()->widget_delegate()->SetCanMaximize(false);
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
EXPECT_FALSE(restore_button()->GetVisible());
EXPECT_FALSE(maximize_button()->GetVisible());
@@ -194,22 +182,16 @@ TEST_F(CustomFrameViewTest, CannotMaximizeHidesButton) {
// Tests that when the parent cannot minimize that the minimize button is not
// visible
TEST_F(CustomFrameViewTest, CannotMinimizeHidesButton) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
widget()->widget_delegate()->SetCanMinimize(false);
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
EXPECT_FALSE(minimize_button()->GetVisible());
}
// Tests that when maximized that the edge button has an increased width.
TEST_F(CustomFrameViewTest, LargerEdgeButtonsWhenMaximized) {
- Widget* parent = widget();
- CustomFrameView* view = custom_frame_view();
-
// Custom ordering to have a button on each edge.
std::vector<views::FrameButton> leading;
leading.push_back(views::FrameButton::kClose);
@@ -218,17 +200,16 @@ TEST_F(CustomFrameViewTest, LargerEdgeButtonsWhenMaximized) {
trailing.push_back(views::FrameButton::kMinimize);
SetWindowButtonOrder(leading, trailing);
- view->Init(parent);
- parent->SetBounds(gfx::Rect(0, 0, 300, 100));
- parent->Show();
+ widget()->SetBounds(gfx::Rect(0, 0, 300, 100));
+ widget()->Show();
gfx::Rect close_button_initial_bounds = close_button()->bounds();
gfx::Rect minimize_button_initial_bounds = minimize_button()->bounds();
- parent->Maximize();
- view->Layout();
+ widget()->Maximize();
+ custom_frame_view()->Layout();
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, "Maximize" should not alter the frame. Only fullscreen does that.
EXPECT_EQ(close_button()->bounds().width(),
close_button_initial_bounds.width());
diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc
index 64e7e523b45..44ab1a8faa7 100644
--- a/chromium/ui/views/window/dialog_client_view.cc
+++ b/chromium/ui/views/window/dialog_client_view.cc
@@ -266,7 +266,7 @@ void DialogClientView::UpdateDialogButton(LabelButton** member,
return;
}
- auto button = MdTextButton::Create(this, title);
+ auto button = std::make_unique<MdTextButton>(this, title);
button->SetProminent(is_default);
button->SetIsDefault(is_default);
button->SetEnabled(delegate->IsDialogButtonEnabled(type));
diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc
index 21fea394e44..8b75d5eb1d9 100644
--- a/chromium/ui/views/window/dialog_delegate.cc
+++ b/chromium/ui/views/window/dialog_delegate.cc
@@ -62,7 +62,8 @@ Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate,
// static
bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent) {
-#if defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && \
+ BUILDFLAG(ENABLE_DESKTOP_AURA)
// The new style doesn't support unparented dialogs on Linux desktop.
return parent != nullptr;
#else
@@ -92,7 +93,7 @@ Widget::InitParams DialogDelegate::GetDialogWidgetInitParams(
if (!dialog || dialog->use_custom_frame()) {
params.opacity = Widget::InitParams::WindowOpacity::kTranslucent;
params.remove_standard_frame = true;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Except on Mac, the bubble frame includes its own shadow; remove any
// native shadowing. On Mac, the window server provides the shadow.
params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
@@ -100,7 +101,7 @@ Widget::InitParams DialogDelegate::GetDialogWidgetInitParams(
}
params.context = context;
params.parent = parent;
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// Web-modal (ui::MODAL_TYPE_CHILD) dialogs with parents are marked as child
// widgets to prevent top-level window behavior (independent movement, etc).
// On Mac, however, the parent may be a native window (not a views::Widget),
@@ -194,11 +195,10 @@ ClientView* DialogDelegate::CreateClientView(Widget* widget) {
return new DialogClientView(widget, GetContentsView());
}
-NonClientFrameView* DialogDelegate::CreateNonClientFrameView(Widget* widget) {
- if (use_custom_frame())
- return CreateDialogFrameView(widget);
-
- return WidgetDelegate::CreateNonClientFrameView(widget);
+std::unique_ptr<NonClientFrameView> DialogDelegate::CreateNonClientFrameView(
+ Widget* widget) {
+ return use_custom_frame() ? CreateDialogFrameView(widget)
+ : WidgetDelegate::CreateNonClientFrameView(widget);
}
void DialogDelegate::WindowWillClose() {
@@ -214,16 +214,6 @@ void DialogDelegate::WindowWillClose() {
if (new_callback_present)
return;
- // Old-style close behavior: if the only button was Ok, call Accept();
- // otherwise call Cancel(). Note that in this case the window is already going
- // to close, so the return values of Accept()/Cancel(), which normally say
- // whether the window should close, are ignored.
- int buttons = GetDialogButtons();
- if (buttons == ui::DIALOG_BUTTON_OK)
- Accept();
- else
- Cancel();
-
// This is set here instead of before the invocations of Accept()/Cancel() so
// that those methods can DCHECK that !already_started_close_. Otherwise,
// client code could (eg) call Accept() from inside the cancel callback, which
@@ -232,9 +222,10 @@ void DialogDelegate::WindowWillClose() {
}
// static
-NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
+std::unique_ptr<NonClientFrameView> DialogDelegate::CreateDialogFrameView(
+ Widget* widget) {
LayoutProvider* provider = LayoutProvider::Get();
- BubbleFrameView* frame = new BubbleFrameView(
+ auto frame = std::make_unique<BubbleFrameView>(
provider->GetInsetsMetric(INSETS_DIALOG_TITLE), gfx::Insets());
const BubbleBorder::Shadow kShadow = BubbleBorder::DIALOG_SHADOW;
@@ -243,13 +234,8 @@ NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
border->set_use_theme_background_color(true);
DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate();
if (delegate) {
- if (delegate->GetParams().round_corners) {
- border->SetCornerRadius(
- base::FeatureList::IsEnabled(
- features::kEnableMDRoundedCornersOnDialogs)
- ? provider->GetCornerRadiusMetric(views::EMPHASIS_HIGH)
- : 2);
- }
+ if (delegate->GetParams().round_corners)
+ border->SetCornerRadius(delegate->GetCornerRadius());
frame->SetFootnoteView(delegate->DisownFootnoteView());
}
frame->SetBubbleBorder(std::move(border));
@@ -421,6 +407,14 @@ ax::mojom::Role DialogDelegate::GetAccessibleWindowRole() {
return ax::mojom::Role::kDialog;
}
+int DialogDelegate::GetCornerRadius() const {
+ return base::FeatureList::IsEnabled(
+ features::kEnableMDRoundedCornersOnDialogs)
+ ? LayoutProvider::Get()->GetCornerRadiusMetric(
+ views::EMPHASIS_MEDIUM)
+ : 2;
+}
+
std::unique_ptr<View> DialogDelegate::DisownFootnoteView() {
return std::move(footnote_view_);
}
diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h
index 2b35fd9e2c2..76a54659225 100644
--- a/chromium/ui/views/window/dialog_delegate.h
+++ b/chromium/ui/views/window/dialog_delegate.h
@@ -119,19 +119,16 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate {
virtual bool IsDialogButtonEnabled(ui::DialogButton button) const;
// For Dialog boxes, if there is a "Cancel" button or no dialog button at all,
- // this is called when the user presses the "Cancel" button.
- // It can also be called on a close action if |Close| has not been
- // overridden. This function should return true if the window can be closed
- // after it returns, or false if it must remain open. By default, return true
- // without doing anything.
+ // this is called when the user presses the "Cancel" button. This function
+ // should return true if the window can be closed after it returns, or false
+ // if it must remain open. By default, return true without doing anything.
// DEPRECATED: use |SetCancelCallback| instead.
virtual bool Cancel();
- // For Dialog boxes, this is called when the user presses the "OK" button,
- // or the Enter key. It can also be called on a close action if |Close|
- // has not been overridden. This function should return true if the window
- // can be closed after it returns, or false if it must remain open. By
- // default, return true without doing anything.
+ // For Dialog boxes, this is called when the user presses the "OK" button, or
+ // the Enter key. This function should return true if the window can be closed
+ // after it returns, or false if it must remain open. By default, return true
+ // without doing anything.
// DEPRECATED: use |SetAcceptCallback| instead.
virtual bool Accept();
@@ -139,9 +136,11 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate {
View* GetInitiallyFocusedView() override;
DialogDelegate* AsDialogDelegate() override;
ClientView* CreateClientView(Widget* widget) override;
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override;
- static NonClientFrameView* CreateDialogFrameView(Widget* widget);
+ static std::unique_ptr<NonClientFrameView> CreateDialogFrameView(
+ Widget* widget);
const gfx::Insets& margins() const { return margins_; }
void set_margins(const gfx::Insets& margins) { margins_ = margins; }
@@ -286,6 +285,8 @@ class VIEWS_EXPORT DialogDelegate : public WidgetDelegate {
const Params& GetParams() const { return params_; }
+ int GetCornerRadius() const;
+
// Return ownership of the footnote view for this dialog. Only use this in
// subclass overrides of CreateNonClientFrameView.
std::unique_ptr<View> DisownFootnoteView();
diff --git a/chromium/ui/views/window/dialog_delegate_unittest.cc b/chromium/ui/views/window/dialog_delegate_unittest.cc
index 33e49328132..a0c0e570eb4 100644
--- a/chromium/ui/views/window/dialog_delegate_unittest.cc
+++ b/chromium/ui/views/window/dialog_delegate_unittest.cc
@@ -21,7 +21,7 @@
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
#include "ui/base/test/scoped_fake_full_keyboard_access.h"
#endif
@@ -396,7 +396,7 @@ TEST_F(DialogTest, InitialFocusWithDeactivatedWidget) {
// If the initially focused View provided is unfocusable, check the next
// available focusable View is focused.
TEST_F(DialogTest, UnfocusableInitialFocus) {
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
// On Mac, make all buttons unfocusable by turning off full keyboard access.
// This is the more common configuration, and if a dialog has a focusable
// textfield, tree or table, that should obtain focus instead.
@@ -409,7 +409,7 @@ TEST_F(DialogTest, UnfocusableInitialFocus) {
dialog->AddChildView(textfield);
Widget* dialog_widget = CreateDialogWidget(dialog);
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
// For non-Mac, turn off focusability on all the dialog's buttons manually.
// This achieves the same effect as disabling full keyboard access.
dialog->GetOkButton()->SetFocusBehavior(View::FocusBehavior::NEVER);
diff --git a/chromium/ui/views/window/frame_background.cc b/chromium/ui/views/window/frame_background.cc
index 6e8de5305b8..50e11d3ccea 100644
--- a/chromium/ui/views/window/frame_background.cc
+++ b/chromium/ui/views/window/frame_background.cc
@@ -99,7 +99,8 @@ void FrameBackground::PaintMaximized(gfx::Canvas* canvas,
const View* view) const {
// Fill the top with the frame color first so we have a constant background
// for areas not covered by the theme image.
-#if defined(OS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
+#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && \
+ BUILDFLAG(ENABLE_DESKTOP_AURA)
auto* native_theme = view->GetNativeTheme();
ui::NativeTheme::ExtraParams params;
params.frame_top_area.use_custom_frame = use_custom_frame_;
diff --git a/chromium/ui/views/window/frame_caption_button.cc b/chromium/ui/views/window/frame_caption_button.cc
index 5a57f099f7d..494738f0c5e 100644
--- a/chromium/ui/views/window/frame_caption_button.cc
+++ b/chromium/ui/views/window/frame_caption_button.cc
@@ -4,18 +4,23 @@
#include "ui/views/window/frame_caption_button.h"
+#include <memory>
+
#include "ui/base/hit_test.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/rrect_f.h"
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
#include "ui/views/animation/ink_drop_highlight.h"
#include "ui/views/animation/ink_drop_impl.h"
-#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/animation/ink_drop_ripple.h"
+#include "ui/views/controls/focus_ring.h"
+#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/window/caption_button_layout_constants.h"
#include "ui/views/window/hit_test_utils.h"
@@ -35,6 +40,29 @@ constexpr float kDisabledButtonAlphaRatio = 0.5f;
} // namespace
+// Custom highlight path generator for clipping ink drops and drawing focus
+// rings.
+class FrameCaptionButton::HighlightPathGenerator
+ : public views::HighlightPathGenerator {
+ public:
+ explicit HighlightPathGenerator(FrameCaptionButton* frame_caption_button)
+ : frame_caption_button_(frame_caption_button) {}
+ HighlightPathGenerator(const HighlightPathGenerator&) = delete;
+ HighlightPathGenerator& operator=(const HighlightPathGenerator&) = delete;
+ ~HighlightPathGenerator() override = default;
+
+ // views::HighlightPathGenerator:
+ base::Optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override {
+ gfx::Rect bounds = gfx::ToRoundedRect(rect);
+ bounds.Inset(frame_caption_button_->GetInkdropInsets(bounds.size()));
+ return gfx::RRectF(gfx::RectF(bounds),
+ frame_caption_button_->ink_drop_corner_radius());
+ }
+
+ private:
+ FrameCaptionButton* const frame_caption_button_;
+};
+
// static
const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";
@@ -58,6 +86,9 @@ FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
set_ink_drop_visible_opacity(kInkDropVisibleOpacity);
UpdateInkDropBaseColor();
+ views::HighlightPathGenerator::Install(
+ this, std::make_unique<HighlightPathGenerator>(this));
+
// Do not flip the gfx::Canvas passed to the OnPaint() method. The snap left
// and snap right button icons should not be flipped. The other icons are
// horizontally symmetrical.
@@ -179,12 +210,6 @@ std::unique_ptr<views::InkDropRipple> FrameCaptionButton::CreateInkDropRipple()
GetInkDropBaseColor(), ink_drop_visible_opacity());
}
-std::unique_ptr<views::InkDropMask> FrameCaptionButton::CreateInkDropMask()
- const {
- return std::make_unique<views::RoundRectInkDropMask>(
- size(), GetInkdropInsets(size()), ink_drop_corner_radius_);
-}
-
void FrameCaptionButton::SetBackgroundColor(SkColor background_color) {
if (background_color_ == background_color)
return;
@@ -202,7 +227,7 @@ void FrameCaptionButton::PaintButtonContents(gfx::Canvas* canvas) {
if (hover_animation().is_animating()) {
highlight_alpha = hover_animation().CurrentValueBetween(
SK_AlphaTRANSPARENT, kHighlightVisibleOpacity);
- } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) {
+ } else if (GetState() == STATE_HOVERED || GetState() == STATE_PRESSED) {
// Painting a circular highlight in both "hovered" and "pressed" states
// simulates and ink drop highlight mode of
// AutoHighlightMode::SHOW_ON_RIPPLE.
@@ -264,7 +289,7 @@ int FrameCaptionButton::GetAlphaForIcon(int base_alpha) const {
if (hover_animation().is_animating()) {
inactive_alpha =
hover_animation().CurrentValueBetween(inactive_alpha, 1.0f);
- } else if (state() == STATE_PRESSED || state() == STATE_HOVERED) {
+ } else if (GetState() == STATE_PRESSED || GetState() == STATE_HOVERED) {
inactive_alpha = 1.0f;
}
return base_alpha * inactive_alpha;
diff --git a/chromium/ui/views/window/frame_caption_button.h b/chromium/ui/views/window/frame_caption_button.h
index 5c9cb22478d..d04cc761f42 100644
--- a/chromium/ui/views/window/frame_caption_button.h
+++ b/chromium/ui/views/window/frame_caption_button.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/focus_ring.h"
#include "ui/views/views_export.h"
#include "ui/views/window/caption_button_types.h"
@@ -60,7 +61,6 @@ class VIEWS_EXPORT FrameCaptionButton : public views::Button {
views::PaintInfo::ScaleType GetPaintScaleType() const override;
std::unique_ptr<views::InkDrop> CreateInkDrop() override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
- std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
void SetBackgroundColor(SkColor background_color);
@@ -73,6 +73,7 @@ class VIEWS_EXPORT FrameCaptionButton : public views::Button {
void set_ink_drop_corner_radius(int ink_drop_corner_radius) {
ink_drop_corner_radius_ = ink_drop_corner_radius;
}
+ int ink_drop_corner_radius() const { return ink_drop_corner_radius_; }
CaptionButtonIcon icon() const { return icon_; }
@@ -87,6 +88,8 @@ class VIEWS_EXPORT FrameCaptionButton : public views::Button {
void PaintButtonContents(gfx::Canvas* canvas) override;
private:
+ class HighlightPathGenerator;
+
// Determines what alpha to use for the icon based on animation and
// active state.
int GetAlphaForIcon(int base_alpha) const;
diff --git a/chromium/ui/views/window/non_client_view.cc b/chromium/ui/views/window/non_client_view.cc
index dddc82199a8..0ffdda78ae8 100644
--- a/chromium/ui/views/window/non_client_view.cc
+++ b/chromium/ui/views/window/non_client_view.cc
@@ -5,6 +5,7 @@
#include "ui/views/window/non_client_view.h"
#include <memory>
+#include <utility>
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
@@ -105,8 +106,6 @@ gfx::Point NonClientFrameView::GetSystemMenuScreenPixelLocation() const {
}
#endif
-void NonClientFrameView::PaintAsActiveChanged(bool active) {}
-
void NonClientFrameView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kClient;
}
@@ -150,12 +149,13 @@ NonClientView::~NonClientView() {
RemoveChildView(frame_view_.get());
}
-void NonClientView::SetFrameView(NonClientFrameView* frame_view) {
+void NonClientView::SetFrameView(
+ std::unique_ptr<NonClientFrameView> frame_view) {
// See comment in header about ownership.
frame_view->set_owned_by_client();
if (frame_view_.get())
RemoveChildView(frame_view_.get());
- frame_view_.reset(frame_view);
+ frame_view_ = std::move(frame_view);
if (parent())
AddChildViewAt(frame_view_.get(), kFrameViewIndex);
}
@@ -172,8 +172,8 @@ void NonClientView::SetOverlayView(View* view) {
AddChildView(overlay_view_);
}
-bool NonClientView::CanClose() {
- return client_view_->CanClose();
+CloseRequestResult NonClientView::OnWindowCloseRequested() {
+ return client_view_->OnWindowCloseRequested();
}
void NonClientView::WindowClosing() {
diff --git a/chromium/ui/views/window/non_client_view.h b/chromium/ui/views/window/non_client_view.h
index bb4439b7df9..212aec4ad67 100644
--- a/chromium/ui/views/window/non_client_view.h
+++ b/chromium/ui/views/window/non_client_view.h
@@ -15,6 +15,7 @@
namespace views {
class ClientView;
+enum class CloseRequestResult;
////////////////////////////////////////////////////////////////////////////////
// NonClientFrameView
@@ -92,9 +93,6 @@ class VIEWS_EXPORT NonClientFrameView : public View,
// Whether the widget can be resized or maximized has changed.
virtual void SizeConstraintsChanged() = 0;
- // Called when whether the non-client view should paint as active has changed.
- virtual void PaintAsActiveChanged(bool active);
-
// View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
void OnThemeChanged() override;
@@ -165,14 +163,13 @@ class VIEWS_EXPORT NonClientView : public View, public ViewTargeterDelegate {
NonClientFrameView* frame_view() const { return frame_view_.get(); }
// Replaces the current NonClientFrameView (if any) with the specified one.
- void SetFrameView(NonClientFrameView* frame_view);
+ void SetFrameView(std::unique_ptr<NonClientFrameView> frame_view);
// Replaces the current |overlay_view_| (if any) with the specified one.
void SetOverlayView(View* view);
- // Returns true if the ClientView determines that the containing window can be
- // closed, false otherwise.
- bool CanClose();
+ // Returned value signals whether the ClientView can be closed.
+ CloseRequestResult OnWindowCloseRequested();
// Called by the containing Window when it is closed.
void WindowClosing();
diff --git a/chromium/ui/views/window/non_client_view_unittest.cc b/chromium/ui/views/window/non_client_view_unittest.cc
index fc44d897394..fd66cb2c607 100644
--- a/chromium/ui/views/window/non_client_view_unittest.cc
+++ b/chromium/ui/views/window/non_client_view_unittest.cc
@@ -49,8 +49,9 @@ class ClientTestView : public ClientView {
class TestWidgetDelegate : public WidgetDelegateView {
public:
// WidgetDelegateView:
- NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
- return new NonClientFrameTestView(widget);
+ std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
+ Widget* widget) override {
+ return std::make_unique<NonClientFrameTestView>(widget);
}
views::ClientView* CreateClientView(Widget* widget) override {
diff --git a/chromium/ui/views_content_client/BUILD.gn b/chromium/ui/views_content_client/BUILD.gn
index 813ef413a17..56f0ab7beac 100644
--- a/chromium/ui/views_content_client/BUILD.gn
+++ b/chromium/ui/views_content_client/BUILD.gn
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
@@ -12,7 +11,7 @@ import("//ui/ozone/ozone.gni")
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
-jumbo_component("views_content_client") {
+component("views_content_client") {
testonly = true
sources = [
"views_content_browser_client.cc",
diff --git a/chromium/ui/views_content_client/views_content_client_main_parts.cc b/chromium/ui/views_content_client/views_content_client_main_parts.cc
index 98301efaf76..68e02f80511 100644
--- a/chromium/ui/views_content_client/views_content_client_main_parts.cc
+++ b/chromium/ui/views_content_client/views_content_client_main_parts.cc
@@ -24,7 +24,7 @@ ViewsContentClientMainParts::ViewsContentClientMainParts(
ViewsContentClientMainParts::~ViewsContentClientMainParts() {
}
-#if !defined(OS_MACOSX)
+#if !defined(OS_APPLE)
void ViewsContentClientMainParts::PreCreateMainMessageLoop() {}
#endif
diff --git a/chromium/ui/views_content_client/views_content_client_main_parts.h b/chromium/ui/views_content_client/views_content_client_main_parts.h
index b78b96060ea..749d1d95e88 100644
--- a/chromium/ui/views_content_client/views_content_client_main_parts.h
+++ b/chromium/ui/views_content_client/views_content_client_main_parts.h
@@ -58,7 +58,7 @@ class ViewsContentClientMainParts : public content::BrowserMainParts {
const content::MainFunctionParams& content_params,
ViewsContentClient* views_content_client);
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
views::TestViewsDelegate* views_delegate() { return views_delegate_.get(); }
#endif
diff --git a/chromium/ui/views_content_client/views_content_client_main_parts_aura.cc b/chromium/ui/views_content_client/views_content_client_main_parts_aura.cc
index d540197492b..bbda25948b5 100644
--- a/chromium/ui/views_content_client/views_content_client_main_parts_aura.cc
+++ b/chromium/ui/views_content_client/views_content_client_main_parts_aura.cc
@@ -20,7 +20,9 @@ ViewsContentClientMainPartsAura::~ViewsContentClientMainPartsAura() {
void ViewsContentClientMainPartsAura::ToolkitInitialized() {
ViewsContentClientMainParts::ToolkitInitialized();
+#if !defined(OS_CHROMEOS)
wm_state_ = std::make_unique<::wm::WMState>();
+#endif
}
void ViewsContentClientMainPartsAura::PostMainMessageLoopRun() {
diff --git a/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc b/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc
index c1cdf28f990..5c74f1336b0 100644
--- a/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc
+++ b/chromium/ui/views_content_client/views_content_client_main_parts_chromeos.cc
@@ -43,7 +43,7 @@ void ViewsContentClientMainPartsChromeOS::PreMainMessageLoopRun() {
ViewsContentClientMainPartsAura::PreMainMessageLoopRun();
// Set up basic pieces of views::corewm.
- wm_test_helper_ = std::make_unique<wm::WMTestHelper>(gfx::Size(800, 600));
+ wm_test_helper_ = std::make_unique<wm::WMTestHelper>(gfx::Size(1024, 768));
// Ensure the X window gets mapped.
wm_test_helper_->host()->Show();
diff --git a/chromium/ui/web_dialogs/BUILD.gn b/chromium/ui/web_dialogs/BUILD.gn
index b70a755c3b9..154bfb21454 100644
--- a/chromium/ui/web_dialogs/BUILD.gn
+++ b/chromium/ui/web_dialogs/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("web_dialogs") {
+component("web_dialogs") {
sources = [
"web_dialog_delegate.cc",
"web_dialog_delegate.h",
@@ -33,7 +31,7 @@ jumbo_component("web_dialogs") {
}
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
sources = [
"test/test_web_contents_handler.cc",
"test/test_web_contents_handler.h",
diff --git a/chromium/ui/web_dialogs/web_dialog_delegate.cc b/chromium/ui/web_dialogs/web_dialog_delegate.cc
index 2472396089c..3deb35416b5 100644
--- a/chromium/ui/web_dialogs/web_dialog_delegate.cc
+++ b/chromium/ui/web_dialogs/web_dialog_delegate.cc
@@ -20,15 +20,15 @@ void WebDialogDelegate::GetMinimumDialogSize(gfx::Size* size) const {
GetDialogSize(size);
}
-bool WebDialogDelegate::CanCloseDialog() const {
+bool WebDialogDelegate::CanResizeDialog() const {
return true;
}
-bool WebDialogDelegate::CanResizeDialog() const {
+bool WebDialogDelegate::OnDialogCloseRequested() {
return true;
}
-bool WebDialogDelegate::OnDialogCloseRequested() {
+bool WebDialogDelegate::DeprecatedOnDialogCloseRequested() {
return true;
}
@@ -74,4 +74,7 @@ bool WebDialogDelegate::AcceleratorPressed(const Accelerator& accelerator) {
return false;
}
+WebDialogDelegate::FrameKind WebDialogDelegate::GetWebDialogFrameKind() const {
+ return WebDialogDelegate::FrameKind::kNonClient;
+}
} // namespace ui
diff --git a/chromium/ui/web_dialogs/web_dialog_delegate.h b/chromium/ui/web_dialogs/web_dialog_delegate.h
index 8f35065da2f..bf97a4bc80b 100644
--- a/chromium/ui/web_dialogs/web_dialog_delegate.h
+++ b/chromium/ui/web_dialogs/web_dialog_delegate.h
@@ -36,7 +36,13 @@ class Accelerator;
// Implement this class to receive notifications.
class WEB_DIALOGS_EXPORT WebDialogDelegate {
public:
- // Returns true if the contents needs to be run in a modal dialog.
+ enum class FrameKind {
+ kDialog, // Does not include a title bar or frame caption buttons.
+ kNonClient, // Includes a non client frame view with title & buttons.
+ };
+
+ // Returns the modal type for this dialog. Only called once, during
+ // WebDialogView creation.
virtual ModalType GetDialogModalType() const = 0;
// Returns the title of the dialog.
@@ -71,12 +77,6 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate {
// Gets the JSON string input to use when showing the dialog.
virtual std::string GetDialogArgs() const = 0;
- // Returns true to signal that the dialog can be closed. Specialized
- // WebDialogDelegate subclasses can override this default behavior to allow
- // the close to be blocked until the user corrects mistakes, accepts an
- // agreement, etc.
- virtual bool CanCloseDialog() const;
-
// Returns true if the dialog can ever be resized. Default implementation
// returns true.
virtual bool CanResizeDialog() const;
@@ -93,6 +93,13 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate {
// closed. If this returns true, the dialog is closed, otherwise the
// dialog remains open. Default implementation returns true.
virtual bool OnDialogCloseRequested();
+ // Use `OnDialogCloseRequested()` instead. This one is called too late in the
+ // closing process, so returning false here will leave you a half-broken
+ // dialog. Currently, `AddSupervisionDialog` relies on this to record
+ // histogram correctly.
+ //
+ // TODO(crbug.com/1110759): remove this function.
+ virtual bool DeprecatedOnDialogCloseRequested();
// A callback to notify the delegate that the dialog is about to close due to
// the user pressing the ESC key.
@@ -163,7 +170,10 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate {
virtual void OnMainFrameResourceLoadComplete(
const blink::mojom::ResourceLoadInfo& resource_load_info) {}
- virtual ~WebDialogDelegate() {}
+ // Whether to use dialog frame view for non client frame view.
+ virtual FrameKind GetWebDialogFrameKind() const;
+
+ virtual ~WebDialogDelegate() = default;
};
} // namespace ui
diff --git a/chromium/ui/webui/PLATFORM_OWNERS b/chromium/ui/webui/PLATFORM_OWNERS
index e875c99f4dd..9820db07c3d 100644
--- a/chromium/ui/webui/PLATFORM_OWNERS
+++ b/chromium/ui/webui/PLATFORM_OWNERS
@@ -4,7 +4,8 @@ antrim@chromium.org # CET
calamity@chromium.org
dpapad@chromium.org
dschuyler@chromium.org
+johntlee@chromium.org
khorimoto@chromium.org # For Chrome OS changes.
-michaelpg@chromium.org
+michaelpg@chromium.org # For Chrome OS changes.
rbpotter@chromium.org
tommycli@chromium.org
diff --git a/chromium/ui/webui/resources/PRESUBMIT.py b/chromium/ui/webui/resources/PRESUBMIT.py
index 35b8ac52ab4..677dbd53fed 100644
--- a/chromium/ui/webui/resources/PRESUBMIT.py
+++ b/chromium/ui/webui/resources/PRESUBMIT.py
@@ -71,8 +71,8 @@ def _CheckWebDevStyle(input_api, output_api):
cwd = input_api.PresubmitLocalPath()
sys.path += [input_api.os_path.join(cwd, '..', '..', '..', 'tools')]
from web_dev_style import presubmit_support
- BLACKLIST = ['ui/webui/resources/js/jstemplate_compiled.js']
- file_filter = lambda f: f.LocalPath() not in BLACKLIST
+ IGNORELIST = ['ui/webui/resources/js/jstemplate_compiled.js']
+ file_filter = lambda f: f.LocalPath() not in IGNORELIST
results += presubmit_support.CheckStyle(input_api, output_api, file_filter)
finally:
sys.path = old_sys_path
diff --git a/chromium/ui/webui/resources/cr_components/BUILD.gn b/chromium/ui/webui/resources/cr_components/BUILD.gn
index a9eec51ac3c..f91009bc108 100644
--- a/chromium/ui/webui/resources/cr_components/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/BUILD.gn
@@ -7,6 +7,7 @@ import("//third_party/closure_compiler/compile_js.gni")
group("closure_compile") {
deps = [
"certificate_manager:closure_compile_module",
+ "customize_themes:closure_compile",
"managed_footnote:closure_compile",
"managed_footnote:closure_compile_module",
"omnibox:closure_compile",
@@ -20,6 +21,7 @@ group("closure_compile") {
group("polymer3_elements") {
public_deps = [
"certificate_manager:web_components",
+ "customize_themes:web_components",
"managed_footnote:managed_footnote_module",
"omnibox:web_components",
]
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn
index be17a059d14..87fb0e397c1 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn
@@ -19,7 +19,9 @@ group("closure_compile") {
# Targets for auto-generated Polymer 3 JS Modules
# TODO: Uncomment as Polymer3 migration makes progress.
# "network:closure_compile_module",
- # "quick_unlock:closure_compile_module",
+
+ "quick_unlock:closure_compile_module",
+ "cellular_setup:closure_compile_module",
"bluetooth:closure_compile_module",
"network_health:closure_compile_module",
"smb_shares:closure_compile_module",
@@ -29,6 +31,7 @@ group("closure_compile") {
group("polymer3_elements") {
public_deps = [
"bluetooth:polymer3_elements",
+ "cellular_setup:polymer3_elements",
"network:polymer3_elements",
"network_health:polymer3_elements",
"quick_unlock:polymer3_elements",
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
index d075a8d1d35..8deef8d61fe 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
@@ -3,6 +3,9 @@
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+import("../os_cr_components.gni")
assert(is_chromeos, "CellularSetup UI is Chrome OS only.")
@@ -14,6 +17,7 @@ js_type_check("closure_compile") {
":final_page",
":mojo_interface_provider",
":provisioning_page",
+ ":psim_flow_ui",
":sim_detect_page",
":webview_post_util",
]
@@ -53,9 +57,8 @@ js_library("final_page") {
deps = [ ":base_page" ]
}
-js_library("cellular_setup") {
+js_library("psim_flow_ui") {
deps = [
- ":button_bar",
":final_page",
":mojo_interface_provider",
":provisioning_page",
@@ -65,9 +68,122 @@ js_library("cellular_setup") {
]
}
+js_library("cellular_setup") {
+ deps = [
+ ":button_bar",
+ ":psim_flow_ui",
+ ]
+}
+
js_library("mojo_interface_provider") {
deps = [
"//chromeos/services/cellular_setup/public/mojom:mojom_js_library_for_compile",
"//ui/webui/resources/js:cr",
]
}
+
+# Polymer3 files
+
+js_type_check("closure_compile_module") {
+ is_polymer3 = true
+ deps = [
+ # ":button_bar.m",
+ ":base_page.m",
+
+ # ":button_bar.m",
+ # ":cellular_setup.m",
+ ":final_page.m",
+ ":mojo_interface_provider.m",
+
+ # ":provisioning_page.m",
+ # ":psim_flow_ui.m",
+ ":sim_detect_page.m",
+ ":webview_post_util.m",
+ ]
+}
+
+js_library("base_page.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.m.js" ]
+ deps = [
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/js:i18n_behavior.m",
+ ]
+ extra_deps = [ ":base_page_module" ]
+}
+
+js_library("final_page.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/cellular_setup/final_page.m.js" ]
+ deps = [
+ ":base_page.m",
+ "//ui/webui/resources/js:i18n_behavior.m",
+ ]
+ extra_deps = [ ":final_page_module" ]
+}
+
+js_library("mojo_interface_provider.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js" ]
+ deps = [
+ "//chromeos/services/cellular_setup/public/mojom:mojom_js_library_for_compile",
+ "//ui/webui/resources/js:cr.m",
+ ]
+ extra_deps = [ ":modulize" ]
+}
+
+js_library("webview_post_util.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.m.js" ]
+ deps = []
+ externs_list = [
+ "$externs_path/chrome_extensions.js",
+ "$externs_path/webview_tag.js",
+ ]
+ extra_deps = [ ":modulize" ]
+}
+
+js_library("sim_detect_page.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.m.js" ]
+ deps = [
+ ":base_page.m",
+ ]
+ extra_deps = [ ":sim_detect_page_module" ]
+}
+
+group("polymer3_elements") {
+ public_deps = [
+ ":base_page_module",
+ ":final_page_module",
+ ":sim_detect_page_module",
+ ":modulize",
+ ]
+}
+
+polymer_modulizer("base_page") {
+ js_file = "base_page.js"
+ html_file = "base_page.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+ auto_imports = cr_components_chromeos_auto_imports
+}
+
+polymer_modulizer("final_page") {
+ js_file = "final_page.js"
+ html_file = "final_page.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+ auto_imports = cr_components_chromeos_auto_imports
+}
+
+polymer_modulizer("sim_detect_page") {
+ js_file = "sim_detect_page.js"
+ html_file = "sim_detect_page.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+ auto_imports = cr_components_chromeos_auto_imports
+}
+
+js_modulizer("modulize") {
+ input_files = [
+ "mojo_interface_provider.js",
+ "webview_post_util.js",
+ ]
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
index 31727da02b2..d20e457a0cb 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.html
@@ -1,39 +1,32 @@
<link rel="import" href="../../../html/polymer.html">
-<link rel="import" href="../../../html/i18n_behavior.html">
-<link rel="import" href="mojo_interface_provider.html">
<link rel="import" href="button_bar.html">
-<link rel="import" href="sim_detect_page.html">
-<link rel="import" href="provisioning_page.html">
-<link rel="import" href="final_page.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="psim_flow_ui.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
<dom-module id="cellular-setup">
<template>
- <style include="iron-flex">
+ <style>
:host {
- display: flex;
flex: 1 1 auto;
- flex-direction: column;
padding: 10px;
}
+
+ iron-pages {
+ display: flex;
+ flex: 1 1 auto;
+ }
</style>
<iron-pages attr-for-selected="is"
- selected="[[selectedPageName_]]"
- selected-item="{{selectedPage_}}">
- <sim-detect-page show-error="[[showError_]]"></sim-detect-page>
- <provisioning-page show-error="{{showError_}}"
- cellular-metadata="[[cellularMetadata_]]"
- on-carrier-portal-loaded="onCarrierPortalLoaded_"
- on-carrier-portal-result="onCarrierPortalResult_">
- </provisioning-page>
- <final-page show-error="[[showError_]]"></final-page>
+ selected="[[selectedPageName_]]">
+ <!-- TODO(hsuregan): Implement <setup-flow-selection>. -->
+ <psim-flow-ui></psim-flow-ui>
+ <!-- TODO(hsuregan): Implement <esim-flow>. -->
</iron-pages>
<button-bar show-try-again-button="[[showTryAgainButton_]]"
show-finish-button="[[showFinishButton_]]"
- show-cancel-button="[[showCancelButton_]]"></button-bar>
+ show-cancel-button="[[showCancelButton_]]">
+ </button-bar>
</template>
<script src="cellular_setup.js">
</script>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
index e1c84626120..e885a7f1ba2 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
@@ -1,369 +1,55 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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.
cr.define('cellularSetup', function() {
/** @enum{string} */
- const PageName = {
- SIM_DETECT: 'sim-detect-page',
- PROVISIONING: 'provisioning-page',
- FINAL: 'final-page',
+ const CellularSetupPageName = {
+ PSIM_FLOW_UI: 'psim-flow-ui',
};
- /** @enum{string} */
- const State = {
- IDLE: 'idle',
- STARTING_ACTIVATION: 'starting-activation',
- WAITING_FOR_ACTIVATION_TO_START: 'waiting-for-activation-to-start',
- TIMEOUT_START_ACTIVATION: 'timeout-start-activation',
- WAITING_FOR_PORTAL_TO_LOAD: 'waiting-for-portal-to-load',
- TIMEOUT_PORTAL_LOAD: 'timeout-portal-load',
- WAITING_FOR_USER_PAYMENT: 'waiting-for-user-payment',
- WAITING_FOR_ACTIVATION_TO_FINISH: 'waiting-for-activation-to-finish',
- TIMEOUT_FINISH_ACTIVATION: 'timeout-finish-activation',
- ACTIVATION_SUCCESS: 'activation-success',
- ALREADY_ACTIVATED: 'already-activated',
- ACTIVATION_FAILURE: 'activation-failure',
- };
-
- /**
- * @param {!cellularSetup.State} state
- * @return {?number} The time delta, in ms, for the timeout corresponding to
- * |state|. If no timeout is applicable for this state, null is returned.
- */
- function getTimeoutMsForState(state) {
- // In some cases, starting activation may require power-cycling the device's
- // modem, a process that can take several seconds.
- if (state === State.STARTING_ACTIVATION) {
- return 10000; // 10 seconds.
- }
-
- // The portal is a website served by the mobile carrier.
- if (state === State.WAITING_FOR_PORTAL_TO_LOAD) {
- return 10000; // 10 seconds.
- }
-
- // Finishing activation only requires sending a D-Bus message to Shill.
- if (state === State.WAITING_FOR_ACTIVATION_TO_FINISH) {
- return 1000; // 1 second.
- }
-
- // No other states require timeouts.
- return null;
- }
-
return {
- PageName: PageName,
- State: State,
- getTimeoutMsForState: getTimeoutMsForState
+ CellularSetupPageName: CellularSetupPageName,
};
});
/**
- * Root element for the cellular setup flow. This element interacts with the
- * CellularSetup service to carry out the activation flow. It contains
- * navigation buttons and sub-pages corresponding to each step of the flow.
+ * @fileoverview Root element for the cellular setup flow. This element wraps
+ * the psim setup flow, esim setup flow, and setup flow selection page.
*/
Polymer({
is: 'cellular-setup',
- behaviors: [I18nBehavior],
-
properties: {
- /** @private {!cellularSetup.State} */
- state_: {
- type: String,
- value: cellularSetup.State.IDLE,
- },
-
/**
* Element name of the current selected sub-page.
- * @private {!cellularSetup.PageName}
+ * @private {!cellularSetup.CellularSetupPageName}
*/
selectedPageName_: {
type: String,
- value: cellularSetup.PageName.SIM_DETECT,
+ value: cellularSetup.CellularSetupPageName.PSIM_FLOW_UI,
notify: true,
- },
-
- /**
- * DOM Element for the current selected sub-page.
- * @private {!SimDetectPageElement|!ProvisioningPageElement|
- * !FinalPageElement}
- */
- selectedPage_: Object,
-
- /**
- * Whether error state should be shown for the current page.
- * @private {boolean}
- */
- showError_: {type: Boolean, value: false},
-
- /**
- * Cellular metadata received via the onActivationStarted() callback. If
- * that callback has not occurred, this field is null.
- * @private {?chromeos.cellularSetup.mojom.CellularMetadata}
- */
- cellularMetadata_: {
- type: Object,
- value: null,
- },
-
- /**
- * Whether try again should be shown in the button bar.
- * @private {boolean}
- */
- showTryAgainButton_: {type: Boolean, value: false},
-
- /**
- * Whether finish button should be shown in the button bar.
- * @private {boolean}
- */
- showFinishButton_: {type: Boolean, value: false},
-
- /**
- * Whether cancel button should be shown in the button bar.
- * @private {boolean}
- */
- showCancelButton_: {type: Boolean, value: false}
+ }
},
- observers: [
- 'updateShowError_(state_)',
- 'updateSelectedPage_(state_)',
- 'handleStateChange_(state_)',
- ],
-
listeners: {
'backward-nav-requested': 'onBackwardNavRequested_',
'retry-requested': 'onRetryRequested_',
'complete-flow-requested': 'onCompleteFlowRequested_',
},
- /**
- * Provides an interface to the CellularSetup Mojo service.
- * @private {?cellular_setup.MojoInterfaceProvider}
- */
- mojoInterfaceProvider_: null,
-
- /**
- * Delegate responsible for routing activation started/finished events.
- * @private {?chromeos.cellularSetup.mojom.ActivationDelegateReceiver}
- */
- activationDelegateReceiver_: null,
-
- /**
- * The timeout ID corresponding to a timeout for the current state. If no
- * timeout is active, this value is null.
- * @private {?number}
- */
- currentTimeoutId_: null,
-
- /**
- * Handler used to communicate state updates back to the CellularSetup
- * service.
- * @private {?chromeos.cellularSetup.mojom.CarrierPortalHandlerRemote}
- */
- carrierPortalHandler_: null,
-
- /** @override */
- created() {
- this.mojoInterfaceProvider_ =
- cellular_setup.MojoInterfaceProviderImpl.getInstance();
- },
-
- /** @override */
- ready() {
- this.state_ = cellularSetup.State.STARTING_ACTIVATION;
- },
-
- /**
- * Overrides chromeos.cellularSetup.mojom.ActivationDelegateInterface.
- * @param {!chromeos.cellularSetup.mojom.CellularMetadata} metadata
- * @private
- */
- onActivationStarted(metadata) {
- this.clearTimer_();
- this.cellularMetadata_ = metadata;
- this.state_ = cellularSetup.State.WAITING_FOR_PORTAL_TO_LOAD;
- },
-
- /**
- * Overrides chromeos.cellularSetup.mojom.ActivationDelegateInterface.
- * @param {!chromeos.cellularSetup.mojom.ActivationResult} result
- * @private
- */
- onActivationFinished(result) {
- this.closeActivationConnection_();
-
- const ActivationResult = chromeos.cellularSetup.mojom.ActivationResult;
- switch (result) {
- case ActivationResult.kSuccessfullyStartedActivation:
- this.state_ = cellularSetup.State.ALREADY_ACTIVATED;
- break;
- case ActivationResult.kAlreadyActivated:
- this.state_ = cellularSetup.State.ACTIVATION_SUCCESS;
- break;
- case ActivationResult.kFailedToActivate:
- this.state_ = cellularSetup.State.ACTIVATION_FAILURE;
- break;
- default:
- assertNotReached();
- }
- },
-
- /** @private */
- updateShowError_() {
- switch (this.state_) {
- case cellularSetup.State.TIMEOUT_START_ACTIVATION:
- case cellularSetup.State.TIMEOUT_PORTAL_LOAD:
- case cellularSetup.State.TIMEOUT_FINISH_ACTIVATION:
- case cellularSetup.State.ACTIVATION_FAILURE:
- this.showError_ = true;
- return;
- default:
- this.showError_ = false;
- return;
- }
- },
-
- /** @private */
- updateSelectedPage_() {
- switch (this.state_) {
- case cellularSetup.State.IDLE:
- case cellularSetup.State.STARTING_ACTIVATION:
- case cellularSetup.State.WAITING_FOR_ACTIVATION_TO_START:
- case cellularSetup.State.TIMEOUT_START_ACTIVATION:
- this.selectedPageName_ = cellularSetup.PageName.SIM_DETECT;
- return;
- case cellularSetup.State.WAITING_FOR_PORTAL_TO_LOAD:
- case cellularSetup.State.TIMEOUT_PORTAL_LOAD:
- case cellularSetup.State.WAITING_FOR_USER_PAYMENT:
- this.selectedPageName_ = cellularSetup.PageName.PROVISIONING;
- return;
- case cellularSetup.State.WAITING_FOR_ACTIVATION_TO_FINISH:
- case cellularSetup.State.TIMEOUT_FINISH_ACTIVATION:
- case cellularSetup.State.ACTIVATION_SUCCESS:
- case cellularSetup.State.ALREADY_ACTIVATED:
- case cellularSetup.State.ACTIVATION_FAILURE:
- this.selectedPageName_ = cellularSetup.PageName.FINAL;
- return;
- default:
- assertNotReached();
- }
- },
-
- /** @private */
- handleStateChange_() {
- // Since the state has changed, the previous state did not time out, so
- // clear any active timeout.
- this.clearTimer_();
-
- // If the new state has an associated timeout, set it.
- const timeoutMs = cellularSetup.getTimeoutMsForState(this.state_);
- if (timeoutMs !== null) {
- this.currentTimeoutId_ =
- setTimeout(this.onTimeout_.bind(this), timeoutMs);
- }
-
- if (this.state_ === cellularSetup.State.STARTING_ACTIVATION) {
- this.startActivation_();
- return;
- }
- },
-
- /** @private */
- onTimeout_() {
- // The activation attempt failed, so close the connection to the service.
- this.closeActivationConnection_();
-
- switch (this.state_) {
- case cellularSetup.State.STARTING_ACTIVATION:
- this.state_ = cellularSetup.State.TIMEOUT_START_ACTIVATION;
- return;
- case cellularSetup.State.WAITING_FOR_PORTAL_TO_LOAD:
- this.state_ = cellularSetup.State.TIMEOUT_PORTAL_LOAD;
- return;
- case cellularSetup.State.WAITING_FOR_ACTIVATION_TO_FINISH:
- this.state_ = cellularSetup.State.TIMEOUT_FINISH_ACTIVATION;
- return;
- default:
- // Only the above states are expected to time out.
- assertNotReached();
- }
- },
-
- /** @private */
- startActivation_() {
- assert(!this.activationDelegateReceiver_);
- this.activationDelegateReceiver_ =
- new chromeos.cellularSetup.mojom.ActivationDelegateReceiver(
- /**
- * @type {!chromeos.cellularSetup.mojom.ActivationDelegateInterface}
- */
- (this));
-
- this.mojoInterfaceProvider_.getMojoServiceRemote()
- .startActivation(
- this.activationDelegateReceiver_.$.bindNewPipeAndPassRemote())
- .then(
- /**
- * @param {!chromeos.cellularSetup.
- * mojom.CellularSetup_StartActivation_ResponseParams}
- * params
- */
- (params) => {
- this.carrierPortalHandler_ = params.observer;
- });
- },
-
- /** @private */
- closeActivationConnection_() {
- assert(!!this.activationDelegateReceiver_);
- this.activationDelegateReceiver_.$.close();
- this.activationDelegateReceiver_ = null;
- this.carrierPortalHandler_ = null;
- this.cellularMetadata_ = null;
- },
-
- /** @private */
- clearTimer_() {
- if (this.currentTimeoutId_) {
- clearTimeout(this.currentTimeoutId_);
- }
- this.currentTimeoutId_ = null;
- },
-
- /** @private */
- onCarrierPortalLoaded_() {
- this.state_ = cellularSetup.State.WAITING_FOR_USER_PAYMENT;
- this.carrierPortalHandler_.onCarrierPortalStatusChange(
- chromeos.cellularSetup.mojom.CarrierPortalStatus
- .kPortalLoadedWithoutPaidUser);
- },
-
- /**
- * @param {!CustomEvent<boolean>} event
- * @private
- */
- onCarrierPortalResult_(event) {
- const success = event.detail;
- this.state_ = success ? cellularSetup.State.ACTIVATION_SUCCESS :
- cellularSetup.State.ACTIVATION_FAILURE;
- },
-
/** @private */
onBackwardNavRequested_() {
- // TODO(azeemarshad): Add back navigation.
+ // TODO(crbug.com/1093185): Add back navigation.
},
/** @private */
onRetryRequested_() {
- // TODO(azeemarshad): Add try again logic.
+ // TODO(crbug.com/1093185): Add try again logic.
},
/** @private */
- onCompleteFlowRequested__() {
- // TODO(azeemarshad): Add completion logic.
+ onCompleteFlowRequested_() {
+ // TODO(crbug.com/1093185): Add completion logic.
},
});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
index 782b14cff1e..e5f87bd0164 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
@@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+
cr.define('cellular_setup', function() {
/** @interface */
- class MojoInterfaceProvider {
+ /* #export */ class MojoInterfaceProvider {
/** @return {!chromeos.cellularSetup.mojom.CellularSetupRemote} */
getMojoServiceRemote() {}
}
/** @implements {cellular_setup.MojoInterfaceProvider} */
- class MojoInterfaceProviderImpl {
+ /* #export */ class MojoInterfaceProviderImpl {
constructor() {
/** @private {?chromeos.cellularSetup.mojom.CellularSetupRemote} */
this.remote_ = null;
@@ -28,6 +30,7 @@ cr.define('cellular_setup', function() {
cr.addSingletonGetter(MojoInterfaceProviderImpl);
+ // #cr_define_end
return {
MojoInterfaceProvider: MojoInterfaceProvider,
MojoInterfaceProviderImpl: MojoInterfaceProviderImpl,
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.html b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.html
new file mode 100644
index 00000000000..2782ca84a7e
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.html
@@ -0,0 +1,35 @@
+<link rel="import" href="../../../html/polymer.html">
+
+<link rel="import" href="../../../html/i18n_behavior.html">
+<link rel="import" href="mojo_interface_provider.html">
+<link rel="import" href="sim_detect_page.html">
+<link rel="import" href="provisioning_page.html">
+<link rel="import" href="final_page.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
+
+<dom-module id="psim-flow-ui">
+ <template>
+ <style include="iron-flex">
+ :host {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ }
+ </style>
+ <iron-pages attr-for-selected="is"
+ selected="[[selectedPSimPageName_]]"
+ selected-item="{{selectedPage_}}">
+ <sim-detect-page show-error="[[showError_]]"></sim-detect-page>
+ <provisioning-page show-error="{{showError_}}"
+ cellular-metadata="[[cellularMetadata_]]"
+ on-carrier-portal-loaded="onCarrierPortalLoaded_"
+ on-carrier-portal-result="onCarrierPortalResult_">
+ </provisioning-page>
+ <final-page show-error="[[showError_]]"></final-page>
+ </iron-pages>
+ </template>
+ <script src="psim_flow_ui.js">
+ </script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
new file mode 100644
index 00000000000..4f7dce5d81f
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
@@ -0,0 +1,348 @@
+// 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.
+
+cr.define('cellularSetup', function() {
+ /** @enum{string} */
+ const PSimPageName = {
+ SIM_DETECT: 'sim-detect-page',
+ PROVISIONING: 'provisioning-page',
+ FINAL: 'final-page',
+ };
+
+ /** @enum{string} */
+ const PSimUIState = {
+ IDLE: 'idle',
+ STARTING_ACTIVATION: 'starting-activation',
+ WAITING_FOR_ACTIVATION_TO_START: 'waiting-for-activation-to-start',
+ TIMEOUT_START_ACTIVATION: 'timeout-start-activation',
+ WAITING_FOR_PORTAL_TO_LOAD: 'waiting-for-portal-to-load',
+ TIMEOUT_PORTAL_LOAD: 'timeout-portal-load',
+ WAITING_FOR_USER_PAYMENT: 'waiting-for-user-payment',
+ WAITING_FOR_ACTIVATION_TO_FINISH: 'waiting-for-activation-to-finish',
+ TIMEOUT_FINISH_ACTIVATION: 'timeout-finish-activation',
+ ACTIVATION_SUCCESS: 'activation-success',
+ ALREADY_ACTIVATED: 'already-activated',
+ ACTIVATION_FAILURE: 'activation-failure',
+ };
+
+ /**
+ * @param {!cellularSetup.PSimUIState} state
+ * @return {?number} The time delta, in ms, for the timeout corresponding to
+ * |state|. If no timeout is applicable for this state, null is returned.
+ */
+ function getTimeoutMsForPSimUIState(state) {
+ // In some cases, starting activation may require power-cycling the device's
+ // modem, a process that can take several seconds.
+ if (state === PSimUIState.STARTING_ACTIVATION) {
+ return 10000; // 10 seconds.
+ }
+
+ // The portal is a website served by the mobile carrier.
+ if (state === PSimUIState.WAITING_FOR_PORTAL_TO_LOAD) {
+ return 10000; // 10 seconds.
+ }
+
+ // Finishing activation only requires sending a D-Bus message to Shill.
+ if (state === PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH) {
+ return 1000; // 1 second.
+ }
+
+ // No other states require timeouts.
+ return null;
+ }
+
+ return {
+ PSimPageName: PSimPageName,
+ PSimUIState: PSimUIState,
+ getTimeoutMsForPSimUIState: getTimeoutMsForPSimUIState
+ };
+});
+
+/**
+ * Root element for the pSIM cellular setup flow. This element interacts with
+ * the CellularSetup service to carry out the psim activation flow. It contains
+ * navigation buttons and sub-pages corresponding to each step of the flow.
+ */
+Polymer({
+ is: 'psim-flow-ui',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @private {!cellularSetup.PSimUIState} */
+ state_: {
+ type: String,
+ value: cellularSetup.PSimUIState.IDLE,
+ },
+
+ /**
+ * Element name of the current selected sub-page.
+ * @private {!cellularSetup.PSimPageName}
+ */
+ selectedPSimPageName_: {
+ type: String,
+ value: cellularSetup.PSimPageName.SIM_DETECT,
+ notify: true,
+ },
+
+ /**
+ * DOM Element for the current selected sub-page.
+ * @private {!SimDetectPageElement|!ProvisioningPageElement|
+ * !FinalPageElement}
+ */
+ selectedPage_: Object,
+
+ /**
+ * Whether error state should be shown for the current page.
+ * @private {boolean}
+ */
+ showError_: {type: Boolean, value: false},
+
+ /**
+ * Cellular metadata received via the onActivationStarted() callback. If
+ * that callback has not occurred, this field is null.
+ * @private {?chromeos.cellularSetup.mojom.CellularMetadata}
+ */
+ cellularMetadata_: {
+ type: Object,
+ value: null,
+ },
+
+ /**
+ * Whether try again should be shown in the button bar.
+ * @private {boolean}
+ */
+ showTryAgainButton_: {type: Boolean, value: false},
+
+ /**
+ * Whether finish button should be shown in the button bar.
+ * @private {boolean}
+ */
+ showFinishButton_: {type: Boolean, value: false},
+
+ /**
+ * Whether cancel button should be shown in the button bar.
+ * @private {boolean}
+ */
+ showCancelButton_: {type: Boolean, value: false}
+ },
+
+ observers: [
+ 'updateShowError_(state_)',
+ 'updateSelectedPage_(state_)',
+ 'handlePSimUIStateChange_(state_)',
+ ],
+
+ /**
+ * Provides an interface to the CellularSetup Mojo service.
+ * @private {?cellular_setup.MojoInterfaceProvider}
+ */
+ mojoInterfaceProvider_: null,
+
+ /**
+ * Delegate responsible for routing activation started/finished events.
+ * @private {?chromeos.cellularSetup.mojom.ActivationDelegateReceiver}
+ */
+ activationDelegateReceiver_: null,
+
+ /**
+ * The timeout ID corresponding to a timeout for the current state. If no
+ * timeout is active, this value is null.
+ * @private {?number}
+ */
+ currentTimeoutId_: null,
+
+ /**
+ * Handler used to communicate state updates back to the CellularSetup
+ * service.
+ * @private {?chromeos.cellularSetup.mojom.CarrierPortalHandlerRemote}
+ */
+ carrierPortalHandler_: null,
+
+ /** @override */
+ created() {
+ this.mojoInterfaceProvider_ =
+ cellular_setup.MojoInterfaceProviderImpl.getInstance();
+ },
+
+ /** @override */
+ ready() {
+ this.state_ = cellularSetup.PSimUIState.STARTING_ACTIVATION;
+ },
+
+ /**
+ * Overrides chromeos.cellularSetup.mojom.ActivationDelegateInterface.
+ * @param {!chromeos.cellularSetup.mojom.CellularMetadata} metadata
+ * @private
+ */
+ onActivationStarted(metadata) {
+ this.clearTimer_();
+ this.cellularMetadata_ = metadata;
+ this.state_ = cellularSetup.PSimUIState.WAITING_FOR_PORTAL_TO_LOAD;
+ },
+
+ /**
+ * Overrides chromeos.cellularSetup.mojom.ActivationDelegateInterface.
+ * @param {!chromeos.cellularSetup.mojom.ActivationResult} result
+ * @private
+ */
+ onActivationFinished(result) {
+ this.closeActivationConnection_();
+
+ const ActivationResult = chromeos.cellularSetup.mojom.ActivationResult;
+ switch (result) {
+ case ActivationResult.kSuccessfullyStartedActivation:
+ this.state_ = cellularSetup.PSimUIState.ALREADY_ACTIVATED;
+ break;
+ case ActivationResult.kAlreadyActivated:
+ this.state_ = cellularSetup.PSimUIState.ACTIVATION_SUCCESS;
+ break;
+ case ActivationResult.kFailedToActivate:
+ this.state_ = cellularSetup.PSimUIState.ACTIVATION_FAILURE;
+ break;
+ default:
+ assertNotReached();
+ }
+ },
+
+ /** @private */
+ updateShowError_() {
+ switch (this.state_) {
+ case cellularSetup.PSimUIState.TIMEOUT_START_ACTIVATION:
+ case cellularSetup.PSimUIState.TIMEOUT_PORTAL_LOAD:
+ case cellularSetup.PSimUIState.TIMEOUT_FINISH_ACTIVATION:
+ case cellularSetup.PSimUIState.ACTIVATION_FAILURE:
+ this.showError_ = true;
+ return;
+ default:
+ this.showError_ = false;
+ return;
+ }
+ },
+
+ /** @private */
+ updateSelectedPage_() {
+ switch (this.state_) {
+ case cellularSetup.PSimUIState.IDLE:
+ case cellularSetup.PSimUIState.STARTING_ACTIVATION:
+ case cellularSetup.PSimUIState.WAITING_FOR_ACTIVATION_TO_START:
+ case cellularSetup.PSimUIState.TIMEOUT_START_ACTIVATION:
+ this.selectedPSimPageName_ = cellularSetup.PSimPageName.SIM_DETECT;
+ return;
+ case cellularSetup.PSimUIState.WAITING_FOR_PORTAL_TO_LOAD:
+ case cellularSetup.PSimUIState.TIMEOUT_PORTAL_LOAD:
+ case cellularSetup.PSimUIState.WAITING_FOR_USER_PAYMENT:
+ this.selectedPSimPageName_ = cellularSetup.PSimPageName.PROVISIONING;
+ return;
+ case cellularSetup.PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH:
+ case cellularSetup.PSimUIState.TIMEOUT_FINISH_ACTIVATION:
+ case cellularSetup.PSimUIState.ACTIVATION_SUCCESS:
+ case cellularSetup.PSimUIState.ALREADY_ACTIVATED:
+ case cellularSetup.PSimUIState.ACTIVATION_FAILURE:
+ this.selectedPSimPageName_ = cellularSetup.PSimPageName.FINAL;
+ return;
+ default:
+ assertNotReached();
+ }
+ },
+
+ /** @private */
+ handlePSimUIStateChange_() {
+ // Since the state has changed, the previous state did not time out, so
+ // clear any active timeout.
+ this.clearTimer_();
+
+ // If the new state has an associated timeout, set it.
+ const timeoutMs = cellularSetup.getTimeoutMsForPSimUIState(this.state_);
+ if (timeoutMs !== null) {
+ this.currentTimeoutId_ =
+ setTimeout(this.onTimeout_.bind(this), timeoutMs);
+ }
+
+ if (this.state_ === cellularSetup.PSimUIState.STARTING_ACTIVATION) {
+ this.startActivation_();
+ return;
+ }
+ },
+
+ /** @private */
+ onTimeout_() {
+ // The activation attempt failed, so close the connection to the service.
+ this.closeActivationConnection_();
+
+ switch (this.state_) {
+ case cellularSetup.PSimUIState.STARTING_ACTIVATION:
+ this.state_ = cellularSetup.PSimUIState.TIMEOUT_START_ACTIVATION;
+ return;
+ case cellularSetup.PSimUIState.WAITING_FOR_PORTAL_TO_LOAD:
+ this.state_ = cellularSetup.PSimUIState.TIMEOUT_PORTAL_LOAD;
+ return;
+ case cellularSetup.PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH:
+ this.state_ = cellularSetup.PSimUIState.TIMEOUT_FINISH_ACTIVATION;
+ return;
+ default:
+ // Only the above states are expected to time out.
+ assertNotReached();
+ }
+ },
+
+ /** @private */
+ startActivation_() {
+ assert(!this.activationDelegateReceiver_);
+ this.activationDelegateReceiver_ =
+ new chromeos.cellularSetup.mojom.ActivationDelegateReceiver(
+ /**
+ * @type {!chromeos.cellularSetup.mojom.ActivationDelegateInterface}
+ */
+ (this));
+
+ this.mojoInterfaceProvider_.getMojoServiceRemote()
+ .startActivation(
+ this.activationDelegateReceiver_.$.bindNewPipeAndPassRemote())
+ .then(
+ /**
+ * @param {!chromeos.cellularSetup.
+ * mojom.CellularSetup_StartActivation_ResponseParams}
+ * params
+ */
+ (params) => {
+ this.carrierPortalHandler_ = params.observer;
+ });
+ },
+
+ /** @private */
+ closeActivationConnection_() {
+ assert(!!this.activationDelegateReceiver_);
+ this.activationDelegateReceiver_.$.close();
+ this.activationDelegateReceiver_ = null;
+ this.carrierPortalHandler_ = null;
+ this.cellularMetadata_ = null;
+ },
+
+ /** @private */
+ clearTimer_() {
+ if (this.currentTimeoutId_) {
+ clearTimeout(this.currentTimeoutId_);
+ }
+ this.currentTimeoutId_ = null;
+ },
+
+ /** @private */
+ onCarrierPortalLoaded_() {
+ this.state_ = cellularSetup.PSimUIState.WAITING_FOR_USER_PAYMENT;
+ this.carrierPortalHandler_.onCarrierPortalStatusChange(
+ chromeos.cellularSetup.mojom.CarrierPortalStatus
+ .kPortalLoadedWithoutPaidUser);
+ },
+
+ /**
+ * @param {!CustomEvent<boolean>} event
+ * @private
+ */
+ onCarrierPortalResult_(event) {
+ const success = event.detail;
+ this.state_ = success ? cellularSetup.PSimUIState.ACTIVATION_SUCCESS :
+ cellularSetup.PSimUIState.ACTIVATION_FAILURE;
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
index 088b82645d2..5ee36ed4016 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
@@ -95,7 +95,8 @@ cr.define('webviewPost.util', function() {
* @param {string} paymentUrl URL to load.
* @param {string} postData Data to pass.
*/
- function postDeviceDataToWebview(webview, paymentUrl, postData) {
+ /* #export */ function postDeviceDataToWebview(
+ webview, paymentUrl, postData) {
const webviewSrc = 'data:text/html;charset=utf-8,' +
encodeURIComponent(WEBVIEW_REDIRECT_HTML);
webview.addEventListener(
@@ -105,5 +106,6 @@ cr.define('webviewPost.util', function() {
webview.src = webviewSrc;
}
+ // #cr_define_end
return {postDeviceDataToWebview: postDeviceDataToWebview};
});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html
index 2eee1473ba5..797d976b284 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html
@@ -25,6 +25,9 @@
<g id="messages" fill="none" fill-rule="evenodd">
<path d="M16.3107503,3 L3.66666667,3 C2.75,3 2,3.75 2,4.66666667 L2,18.3161621 L5.33333333,15 L16.3107503,15 C17.227417,15 17.977417,14.2328288 17.977417,13.3161621 L17.977417,4.66666667 C17.977417,3.75 17.227417,3 16.3107503,3 Z M16,13 L4,13 L4,5 L16,5 L16,13 Z M6,8 L8,8 L8,10 L6,10 L6,8 Z M9,8 L11,8 L11,10 L9,10 L9,8 Z M12,8 L14,8 L14,10 L12,10 L12,8 Z" fill="#9AA0A6"></path>
</g>
+ <g id="notifications" fill="none" fill-rule="evenodd">
+ <path d="M11,3.10001812 C13.2822403,3.56328845 15,5.58104209 15,8 C15,10.7614237 15,13 15,13 L17,13 L17,15 L3,15 L3,13 L5,13 C5,13 5,9.9021552 5,8 C5,5.58104209 6.71775968,3.56328845 9,3.10001812 L9,2.5 C9,1.94771525 9.44771525,1.5 10,1.5 C10.5522847,1.5 11,1.94771525 11,2.5 L11,3.10001812 Z M7,8 L7,13 L13,13 L13,8 C13,6.34314575 11.6568542,5 10,5 C8.34314575,5 7,6.34314575 7,8 Z M10,18 C8.8954305,18 8,17.1045695 8,16 L12,16 C12,17.1045695 11.1045695,18 10,18 Z" fill="#9AA0A6"></path>
+ </g>
<g id="downloads" fill="none" fill-rule="evenodd">
<path d="M2,13 L4,13 L4,16 L16,16 L16,13 L18,13 L18,16 C18,17.1 17.1,18 16,18 L4,18 C2.9,18 2,17.1 2,16 L2,13 Z M13.59,7.59 L11,10.17 L11,2 L9,2 L9,10.17 L6.41,7.59 L5,9 L10,14 L15,9 L13.59,7.59 Z" fill="#9AA0A6"></path>
</g>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
index d5de70f7d2f..5da74e7edb3 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
@@ -153,6 +153,15 @@
locale, 'startSetupPageFeatureListAwm')]]">
</span>
</div>
+ <template is="dom-if" if="[[phoneHubEnabled_]]">
+ <div class="feature-detail">
+ <iron-icon icon="multidevice-setup-icons-20:notifications">
+ </iron-icon>
+ <span>
+ [[i18nDynamic(locale, 'startSetupPageFeatureMirrorPhoneNotifications')]]
+ </span>
+ </div>
+ </template>
<div class="feature-detail">
<iron-icon icon="multidevice-setup-icons-20:downloads">
</iron-icon>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
index 8783fffae9d..fc28b6edfe9 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
@@ -51,6 +51,15 @@ Polymer({
* @type {!multidevice_setup.MultiDeviceSetupDelegate}
*/
delegate: Object,
+
+ /** @private */
+ phoneHubEnabled_: {
+ type: Boolean,
+ value() {
+ return loadTimeData.valueExists('phoneHubEnabled') &&
+ loadTimeData.getBoolean('phoneHubEnabled');
+ },
+ },
},
behaviors: [
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
index ca7f9fc53fc..57abd5fe8a1 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
@@ -3,11 +3,14 @@
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
+import("../os_cr_components.gni")
assert(is_chromeos, "Only ChromeOS components belong here.")
js_type_check("closure_compile") {
deps = [
+ ":cr_policy_network_behavior_mojo",
+ ":cr_policy_network_indicator_mojo",
":mojo_interface_provider",
":network_apnlist",
":network_choose_mobile",
@@ -40,16 +43,33 @@ js_library("mojo_interface_provider") {
]
}
+js_library("cr_policy_network_behavior_mojo") {
+ deps = [
+ ":mojo_interface_provider",
+ ":onc_mojo",
+ "../../../cr_elements/policy:cr_policy_indicator_behavior",
+ "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
+ ]
+}
+
+js_library("cr_policy_network_indicator_mojo") {
+ deps = [
+ ":cr_policy_network_behavior_mojo",
+ "../../../cr_elements/policy:cr_policy_indicator_behavior",
+ "../../../cr_elements/policy:cr_tooltip_icon",
+ ]
+}
+
js_library("network_apnlist") {
deps = [
- "//ui/webui/resources/cr_components/chromeos/network:network_config",
+ ":network_config",
"//ui/webui/resources/js:i18n_behavior",
]
}
js_library("network_choose_mobile") {
deps = [
- "//ui/webui/resources/cr_components/chromeos/network:network_config",
+ ":network_config",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -57,9 +77,9 @@ js_library("network_choose_mobile") {
js_library("network_config") {
deps = [
+ ":mojo_interface_provider",
":network_listener_behavior",
"//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
- "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -70,15 +90,15 @@ js_library("network_config") {
js_library("network_config_element_behavior") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":onc_mojo",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
]
}
js_library("network_config_input") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":network_config_element_behavior",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
]
externs_list = [ "$externs_path/networking_private.js" ]
extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
@@ -86,8 +106,8 @@ js_library("network_config_input") {
js_library("network_config_select") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":network_config_element_behavior",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -97,8 +117,8 @@ js_library("network_config_select") {
js_library("network_config_toggle") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":network_config_element_behavior",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
]
externs_list = [ "$externs_path/networking_private.js" ]
extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
@@ -107,22 +127,24 @@ js_library("network_config_toggle") {
js_library("network_ip_config") {
deps = [
":onc_mojo",
+ "../../../cr_elements/cr_toggle:cr_toggle",
"//ui/webui/resources/js:i18n_behavior",
]
}
js_library("network_nameservers") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":onc_mojo",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
+ "../../../cr_elements/cr_radio_group:cr_radio_group",
"//ui/webui/resources/js:i18n_behavior",
]
}
js_library("network_password_input") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":network_config_element_behavior",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
"//ui/webui/resources/js:i18n_behavior",
]
externs_list = [ "$externs_path/networking_private.js" ]
@@ -131,8 +153,8 @@ js_library("network_password_input") {
js_library("network_property_list_mojo") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":onc_mojo",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -140,8 +162,8 @@ js_library("network_property_list_mojo") {
js_library("network_proxy") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":onc_mojo",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -185,8 +207,8 @@ js_library("network_list") {
js_library("network_list_item") {
deps = [
+ ":cr_policy_network_behavior_mojo",
":network_list_types",
- "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior_mojo",
"//ui/webui/resources/js:assert",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -222,44 +244,69 @@ js_library("onc_mojo") {
]
}
-# TODO: Uncomment as the Polymer3 migration makes progress.
-# js_type_check("closure_compile_module") {
-# is_polymer3 = true
-# deps = [
-# ":mojo_interface_provider.m",
-# ":network_apnlist.m",
-# ":network_choose_mobile.m",
-# ":network_config.m",
-# ":network_config_element_behavior.m",
-# ":network_config_input.m",
-# ":network_config_select.m",
-# ":network_config_toggle.m",
-# ":network_icon.m",
-# ":network_icons.m",
-# ":network_ip_config.m",
-# ":network_list.m",
-# ":network_list_item.m",
-# ":network_list_types.m",
-# ":network_listener_behavior.m",
-# ":network_nameservers.m",
-# ":network_password_input.m",
-# ":network_property_list_mojo.m",
-# ":network_proxy.m",
-# ":network_proxy_exclusions.m",
-# ":network_proxy_input.m",
-# ":network_select.m",
-# ":network_shared_css.m",
-# ":network_siminfo.m",
-# ":onc_mojo.m"
-# ]
-# }
+js_type_check("closure_compile_module") {
+ is_polymer3 = true
+ deps = [
+ ":cr_policy_network_behavior_mojo.m",
+ ":cr_policy_network_indicator_mojo.m",
+ ":mojo_interface_provider.m",
+
+ # ":network_apnlist.m",
+ # ":network_choose_mobile.m",
+ # ":network_config.m",
+ ":network_config_element_behavior.m",
+ # ":network_config_input.m",
+ # ":network_config_select.m",
+ # ":network_config_toggle.m",
+ ":network_icon.m",
+
+ # ":network_ip_config.m",
+ ":network_list.m",
+ ":network_list_item.m",
+ ":network_list_types.m",
+ ":network_listener_behavior.m",
+
+ # ":network_nameservers.m",
+ ":network_password_input.m",
+ # ":network_property_list_mojo.m",
+ # ":network_proxy.m",
+ # ":network_proxy_exclusions.m",
+ # ":network_proxy_input.m",
+ # ":network_select.m",
+ ":network_shared_css.m",
+ # ":network_siminfo.m",
+ ":onc_mojo.m",
+ ]
+}
js_library("mojo_interface_provider.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
+ "//ui/webui/resources/js:cr.m",
]
- extra_deps = [ ":mojo_interface_provider_module" ]
+ extra_deps = [ ":modulize" ]
+}
+
+js_library("cr_policy_network_behavior_mojo.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.m.js" ]
+ deps = [
+ "../../../cr_elements/policy:cr_policy_indicator_behavior.m",
+ "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+ extra_deps = [ ":modulize" ]
+}
+
+js_library("cr_policy_network_indicator_mojo.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.m.js" ]
+ deps = [
+ ":cr_policy_network_behavior_mojo.m",
+ ":onc_mojo",
+ "../../../cr_elements/policy:cr_policy_indicator_behavior.m",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+ extra_deps = [ ":cr_policy_network_indicator_mojo_module" ]
}
js_library("network_apnlist.m") {
@@ -288,9 +335,6 @@ js_library("network_config.m") {
js_library("network_config_element_behavior.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.m.js" ]
- deps = [
- # TODO: Fill those in.
- ]
extra_deps = [ ":modulize" ]
}
@@ -321,19 +365,14 @@ js_library("network_config_toggle.m") {
js_library("network_icon.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_icon.m.js" ]
deps = [
- # TODO: Fill those in.
+ ":onc_mojo.m",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/js:assert.m",
+ "//ui/webui/resources/js:i18n_behavior.m",
]
extra_deps = [ ":network_icon_module" ]
}
-js_library("network_icons.m") {
- sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_icons.m.js" ]
- deps = [
- # TODO: Fill those in.
- ]
- extra_deps = [ ":network_icons_module" ]
-}
-
js_library("network_ip_config.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_ip_config.m.js" ]
deps = [
@@ -345,7 +384,9 @@ js_library("network_ip_config.m") {
js_library("network_list.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_list.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//third_party/polymer/v3_0/components-chromium/iron-list:iron-list",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_elements:cr_scrollable_behavior.m",
]
extra_deps = [ ":network_list_module" ]
}
@@ -353,23 +394,21 @@ js_library("network_list.m") {
js_library("network_list_item.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_list_item.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
extra_deps = [ ":network_list_item_module" ]
}
js_library("network_list_types.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_list_types.m.js" ]
- deps = [
- # TODO: Fill those in.
- ]
- extra_deps = [ ":network_list_types_module" ]
+ extra_deps = [ ":modulize" ]
}
js_library("network_listener_behavior.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.m.js" ]
deps = [
- # TODO: Fill those in.
+ ":mojo_interface_provider.m",
+ "//ui/webui/resources/js:assert.m",
]
extra_deps = [ ":modulize" ]
}
@@ -385,7 +424,14 @@ js_library("network_nameservers.m") {
js_library("network_password_input.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/network_password_input.m.js" ]
deps = [
- # TODO: Fill those in.
+ ":cr_policy_network_behavior_mojo.m",
+ ":cr_policy_network_indicator_mojo.m",
+ ":network_config_element_behavior.m",
+ ":network_shared_css.m",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+ "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+ "//ui/webui/resources/js:i18n_behavior.m",
]
extra_deps = [ ":network_password_input_module" ]
}
@@ -449,17 +495,19 @@ js_library("network_siminfo.m") {
js_library("onc_mojo.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network/onc_mojo.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
+ "//ui/webui/resources/js:assert.m",
+ "//ui/webui/resources/js:load_time_data.m",
]
- extra_deps = [ ":onc_mojo_module" ]
+ extra_deps = [ ":modulize" ]
}
import("//tools/polymer/polymer.gni")
group("polymer3_elements") {
public_deps = [
+ ":cr_policy_network_indicator_mojo_module",
":modulize",
- ":mojo_interface_provider_module",
":network_apnlist_module",
":network_choose_mobile_module",
":network_config_input_module",
@@ -471,7 +519,6 @@ group("polymer3_elements") {
":network_ip_config_module",
":network_list_item_module",
":network_list_module",
- ":network_list_types_module",
":network_nameservers_module",
":network_password_input_module",
":network_property_list_mojo_module",
@@ -480,14 +527,16 @@ group("polymer3_elements") {
":network_select_module",
":network_shared_css_module",
":network_siminfo_module",
- ":onc_mojo_module",
]
}
-polymer_modulizer("mojo_interface_provider") {
- js_file = "mojo_interface_provider.js"
- html_file = "mojo_interface_provider.html"
+polymer_modulizer("cr_policy_network_indicator_mojo") {
+ js_file = "cr_policy_network_indicator_mojo.js"
+ html_file = "cr_policy_network_indicator_mojo.html"
html_type = "dom-module"
+ auto_imports = cr_components_chromeos_auto_imports + [
+ "ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html|CrPolicyIndicatorBehavior,CrPolicyIndicatorType",
+ ]
}
polymer_modulizer("network_apnlist") {
@@ -530,6 +579,7 @@ polymer_modulizer("network_icon") {
js_file = "network_icon.js"
html_file = "network_icon.html"
html_type = "dom-module"
+ auto_imports = cr_components_chromeos_auto_imports
}
polymer_modulizer("network_icons") {
@@ -548,18 +598,14 @@ polymer_modulizer("network_list") {
js_file = "network_list.js"
html_file = "network_list.html"
html_type = "dom-module"
+ auto_imports = cr_components_chromeos_auto_imports
}
polymer_modulizer("network_list_item") {
js_file = "network_list_item.js"
html_file = "network_list_item.html"
html_type = "dom-module"
-}
-
-polymer_modulizer("network_list_types") {
- js_file = "network_list_types.js"
- html_file = "network_list_types.html"
- html_type = "dom-module"
+ auto_imports = cr_components_chromeos_auto_imports
}
polymer_modulizer("network_nameservers") {
@@ -572,6 +618,9 @@ polymer_modulizer("network_password_input") {
js_file = "network_password_input.js"
html_file = "network_password_input.html"
html_type = "dom-module"
+ auto_imports = cr_components_chromeos_auto_imports + [
+ "ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.html|NetworkConfigElementBehavior",
+ ]
}
polymer_modulizer("network_property_list_mojo") {
@@ -610,18 +659,17 @@ polymer_modulizer("network_siminfo") {
html_type = "dom-module"
}
-polymer_modulizer("onc_mojo") {
- js_file = "onc_mojo.js"
- html_file = "onc_mojo.html"
- html_type = "dom-module"
-}
-
import("//ui/webui/resources/tools/js_modulizer.gni")
js_modulizer("modulize") {
input_files = [
+ "cr_policy_network_behavior_mojo.js",
+ "mojo_interface_provider.js",
"network_config_element_behavior.js",
"network_listener_behavior.js",
+ "network_list_types.js",
"network_proxy.js",
+ "onc_mojo.js",
]
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.html b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.html
new file mode 100644
index 00000000000..56e995a45ea
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.html
@@ -0,0 +1,5 @@
+<link rel="import" href="../../../html/polymer.html">
+<link rel="import" href="../../../cr_elements/policy/cr_policy_indicator_behavior.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
+<link rel="import" href="chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html">
+<script src="cr_policy_network_behavior_mojo.js"></script>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.js b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.js
index 10d48330d00..3845594d058 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.js
@@ -8,8 +8,14 @@
* optional properties (which may be null|undefined).
*/
+// clang-format off
+// #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-lite.js';
+// #import {CrPolicyIndicatorType} from 'chrome://resources/cr_elements/policy/cr_policy_indicator_behavior.m.js';
+// clang-format on
+
/** @polymerBehavior */
-const CrPolicyNetworkBehaviorMojo = {
+/* #export */ const CrPolicyNetworkBehaviorMojo = {
/**
* @param {?OncMojo.ManagedProperty|undefined} property
* @return {boolean} True if the property is controlled by network policy.
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.html b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.html
index c0385286655..62cddfd80cd 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.html
@@ -1,9 +1,9 @@
-<link rel="import" href="../../html/polymer.html">
+<link rel="import" href="../../../html/polymer.html">
-<link rel="import" href="../hidden_style_css.html">
-<link rel="import" href="cr_policy_indicator_behavior.html">
+<link rel="import" href="../../../cr_elements/hidden_style_css.html">
+<link rel="import" href="../../../cr_elements/policy/cr_policy_indicator_behavior.html">
+<link rel="import" href="../../../cr_elements/policy/cr_tooltip_icon.html">
<link rel="import" href="cr_policy_network_behavior_mojo.html">
-<link rel="import" href="cr_tooltip_icon.html">
<dom-module id="cr-policy-network-indicator-mojo">
<template>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.js b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js
index 59af73c1a26..73ad2eaa2b3 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator_mojo.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js
@@ -6,8 +6,6 @@
* @fileoverview Polymer element for indicating policies based on network
* properties.
*/
-(function() {
-'use strict';
Polymer({
is: 'cr-policy-network-indicator-mojo',
@@ -80,4 +78,3 @@ Polymer({
return this.getIndicatorTooltip(this.indicatorType, '', matches);
}
});
-})();
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html b/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html
index 7eaa61575ef..3e7907f0ad6 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html
@@ -1,4 +1,7 @@
<link rel="import" href="../../../html/cr.html">
<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
+<link rel="import" href="chrome://resources/mojo/services/network/public/mojom/ip_address.mojom.html">
+<link rel="import" href="chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/mojom/base/time.mojom.html">
<link rel="import" href="chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html">
<script src="mojo_interface_provider.js"></script>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js b/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
index e83e3853d95..d90fc30a09f 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.js
@@ -2,15 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// clang-format off
+// #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-lite.js';
+// #import 'chrome://resources/mojo/services/network/public/mojom/ip_address.mojom-lite.js';
+// #import 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-lite.js';
+// clang-format on
+
+// #import {addSingletonGetter} from '../../../js/cr.m.js';
+
cr.define('network_config', function() {
/** @interface */
- class MojoInterfaceProvider {
+ /* #export */ class MojoInterfaceProvider {
/** @return {!chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
getMojoServiceRemote() {}
}
/** @implements {network_config.MojoInterfaceProvider} */
- class MojoInterfaceProviderImpl {
+ /* #export */ class MojoInterfaceProviderImpl {
constructor() {
/** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
this.remote_ = null;
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
index e12cc9d632c..250d68a8c23 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
@@ -16,24 +16,28 @@
<div class="property-box">
<div class="start">[[i18n('networkAccessPoint')]]</div>
<select id="selectApn" class="md-select" on-change="onSelectApnChange_"
+ value="[[selectedApn_]]"
+ disabled="[[isDisabled_(selectedApn_)]]"
aria-label="[[i18n('networkAccessPoint')]]">
<template is="dom-repeat" items="[[apnSelectList_]]">
- <option value="[[item.accessPointName]]"
- selected$="[[isApnItemSelected_(item, selectedApn_)]]">[[apnDesc_(item)]]</option>
+ <option value="[[item.name]]">
+ [[apnDesc_(item)]]
+ </option>
</template>
</select>
</div>
- <div class="property-box single-column indented"
- hidden$="[[!isOtherSelected_(selectedApn_, managedProperties)]]">
- <network-property-list-mojo on-property-change="onOtherApnChange_"
- fields="[[otherApnFields_]]" property-dict="[[otherApn_]]"
- edit-field-types="[[otherApnEditTypes_]]" prefix="cellular.apn.">
- </network-property-list-mojo>
- <cr-button class="action-button" on-click="onSaveOtherTap_">
- [[i18n('save')]]
- </cr-button>
- </div>
+ <template is="dom-if" if="[[showOtherApn_(selectedApn_)]]">
+ <div class="property-box single-column indented">
+ <network-property-list-mojo on-property-change="onOtherApnChange_"
+ fields="[[otherApnFields_]]" property-dict="[[otherApn_]]"
+ edit-field-types="[[otherApnEditTypes_]]" prefix="cellular.apn.">
+ </network-property-list-mojo>
+ <cr-button class="action-button" on-click="onSaveOtherTap_">
+ [[i18n('save')]]
+ </cr-button>
+ </div>
+ </template>
</template>
<script src="network_apnlist.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
index afbc5ed687d..54f54419f47 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
@@ -25,7 +25,12 @@ Polymer({
},
/**
- * accessPointName value of the selected APN.
+ * The name property of the selected APN. If a name property is empty, the
+ * accessPointName property will be used. We use 'name' so that multiple
+ * APNs with the same accessPointName can be supported, so long as they have
+ * a unique 'name' property. This is necessary to allow custom 'other'
+ * entries (which are always named 'Other') that match an existing
+ * accessPointName but provide a different username/password.
* @private
*/
selectedApn_: {
@@ -49,10 +54,16 @@ Polymer({
* The user settable properties for a new ('other') APN. The values for
* accessPointName, username, and password will be set to the currently
* active APN if it does not match an existing list entry.
- * @private {chromeos.networkConfig.mojom.ApnProperties|undefined}
+ * @private {!chromeos.networkConfig.mojom.ApnProperties}
*/
otherApn_: {
type: Object,
+ value() {
+ return {
+ accessPointName: kDefaultAccessPointName,
+ name: kOtherAccessPointName,
+ };
+ }
},
/**
@@ -84,6 +95,14 @@ Polymer({
},
},
+ /*
+ * Returns the select APN SelectElement.
+ * @return {?HTMLSelectElement}
+ */
+ getApnSelect() {
+ return /** @type {?HTMLSelectElement} */ (this.$$('#selectApn'));
+ },
+
/**
* @param {!chromeos.networkConfig.mojom.ManagedApnProperties} apn
* @return {!chromeos.networkConfig.mojom.ApnProperties}
@@ -91,9 +110,8 @@ Polymer({
*/
getApnFromManaged_(apn) {
return {
+ // authentication and language are ignored in this UI.
accessPointName: OncMojo.getActiveString(apn.accessPointName),
- authentication: OncMojo.getActiveString(apn.authentication),
- language: OncMojo.getActiveString(apn.language),
localizedName: OncMojo.getActiveString(apn.localizedName),
name: OncMojo.getActiveString(apn.name),
password: OncMojo.getActiveString(apn.password),
@@ -111,80 +129,84 @@ Polymer({
} else if (cellular.lastGoodApn && cellular.lastGoodApn.accessPointName) {
activeApn = cellular.lastGoodApn;
}
+ if (activeApn && !activeApn.accessPointName) {
+ activeApn = undefined;
+ }
this.setApnSelectList_(activeApn);
},
/**
* Sets the list of selectable APNs for the UI. Appends an 'Other' entry
* (see comments for |otherApn_| above).
- * @param {chromeos.networkConfig.mojom.ApnProperties|undefined} activeApn The
- * currently active APN properties.
+ * @param {chromeos.networkConfig.mojom.ApnProperties|undefined} activeApn
* @private
*/
setApnSelectList_(activeApn) {
- // Copy the list of APNs from this.managedProperties.
- const apnList = this.getApnList_().slice();
-
- // Test whether |activeApn| is in the current APN list in managedProperties.
- const activeApnInList = activeApn && apnList.some(function(a) {
- return a.accessPointName === activeApn.accessPointName;
- });
-
- // If |activeApn| is specified and not in the list, use the active
- // properties for 'other'. Otherwise use any existing 'other' properties.
- const otherApnProperties =
- (activeApn && !activeApnInList) ? activeApn : this.otherApn_;
- const otherApn = this.createApnObject_(otherApnProperties);
-
- // Always use 'Other' for the name of custom APN entries (the name does
- // not get saved).
- otherApn.name = kOtherAccessPointName;
-
- // If no 'active' or 'other' AccessPointName was provided, use the default.
- otherApn.accessPointName =
- otherApn.accessPointName || kDefaultAccessPointName;
-
- // Save the 'other' properties.
- this.otherApn_ = otherApn;
+ assert(!activeApn || activeApn.accessPointName);
+ // The generated APN list ensures nonempty accessPointName and name
+ // properties.
+ const apnList = this.generateApnList_();
+ if (apnList === undefined) {
+ // No APNList property indicates that the network is not in a
+ // connectable state. Disable the UI.
+ this.apnSelectList_ = [];
+ this.set('selectedApn_', '');
+ return;
+ }
+ // Get the list entry for activeApn if it exists. It will have 'name' set.
+ let activeApnInList;
+ if (activeApn) {
+ activeApnInList = apnList.find(a => a.name === activeApn.name);
+ }
- // Append 'other' to the end of the list of APNs.
- apnList.push(otherApn);
+ // If the active APN is not in the list, copy it to otherApn_.
+ if (!activeApnInList && activeApn && activeApn.accessPointName) {
+ this.otherApn_ = {
+ accessPointName: activeApn.accessPointName,
+ name: kOtherAccessPointName,
+ username: activeApn.username,
+ password: activeApn.password,
+ };
+ }
+ apnList.push(this.otherApn_);
this.apnSelectList_ = apnList;
- this.selectedApn_ =
- (activeApn && activeApn.accessPointName) || otherApn.accessPointName;
- },
+ const selectedApn =
+ activeApnInList ? activeApnInList.name : kOtherAccessPointName;
+ assert(selectedApn);
+ this.set('selectedApn_', selectedApn);
- /**
- * @param {chromeos.networkConfig.mojom.ApnProperties=}
- * apnProperties
- * @return {!chromeos.networkConfig.mojom.ApnProperties} A new APN object with
- * properties from |apnProperties| if provided.
- * @private
- */
- createApnObject_(apnProperties) {
- const newApn = {accessPointName: ''};
- if (apnProperties) {
- Object.assign(newApn, apnProperties);
- }
- return newApn;
+ // Wait for the dom-repeat to populate the <option> entries then explicitly
+ // set the selected value.
+ this.async(function() {
+ this.$.selectApn.value = this.selectedApn_;
+ });
},
/**
- * @return {!Array<!chromeos.networkConfig.mojom.ApnProperties>} The list of
- * APN properties in |managedProperties| or an empty list if the property
- * is not set.
+ * Returns a modified copy of the APN properties or undefined if the
+ * property is not set. All entries in the returned copy will have nonempty
+ * name and accessPointName properties.
+ * @return {!Array<!chromeos.networkConfig.mojom.ApnProperties>|undefined}
* @private
*/
- getApnList_() {
+ generateApnList_() {
if (!this.managedProperties) {
- return [];
+ return undefined;
}
const apnList = this.managedProperties.typeProperties.cellular.apnList;
if (!apnList) {
- return [];
+ return undefined;
}
- return apnList.activeValue;
+ return apnList.activeValue.filter(apn => !!apn.accessPointName).map(apn => {
+ return {
+ accessPointName: apn.accessPointName,
+ localizedName: apn.localizedName,
+ name: apn.name || apn.accessPointName,
+ username: apn.username,
+ password: apn.password,
+ };
+ });
},
/**
@@ -194,16 +216,18 @@ Polymer({
*/
onSelectApnChange_(event) {
const target = /** @type {!HTMLSelectElement} */ (event.target);
- const accessPointName = target.value;
- // When selecting 'Other', don't set a change event unless a valid
+ const name = target.value;
+ // When selecting 'Other', don't send a change event unless a valid
// non-default value has been set for Other.
- if (this.isOtherSelected_(accessPointName) &&
- (!this.otherApn_ || !this.otherApn_.accessPointName ||
+ if (name === kOtherAccessPointName &&
+ (!this.otherApn_.accessPointName ||
this.otherApn_.accessPointName === kDefaultAccessPointName)) {
- this.selectedApn_ = accessPointName;
+ this.selectedApn_ = name;
return;
}
- this.sendApnChange_(accessPointName);
+ // The change will generate an update which will update selectedApn_ and
+ // refresh the UI.
+ this.sendApnChange_(name);
},
/**
@@ -232,57 +256,57 @@ Polymer({
/**
* Send the apn-change event.
- * @param {string} accessPointName
+ * @param {string} name The APN name property.
* @private
*/
- sendApnChange_(accessPointName) {
- const apnList = this.getApnList_();
- let apn = this.findApnInList_(apnList, accessPointName);
- if (apn === undefined) {
- apn = this.createApnObject_();
- if (this.otherApn_) {
- apn.accessPointName = this.otherApn_.accessPointName;
- apn.username = this.otherApn_.username;
- apn.password = this.otherApn_.password;
+ sendApnChange_(name) {
+ let apn;
+ if (name === kOtherAccessPointName) {
+ if (!this.otherApn_.accessPointName ||
+ this.otherApn_.accessPointName === kDefaultAccessPointName) {
+ // No valid APN set, do nothing.
+ return;
+ }
+ apn = {
+ accessPointName: this.otherApn_.accessPointName,
+ username: this.otherApn_.username,
+ password: this.otherApn_.password,
+ };
+ } else {
+ apn = this.apnSelectList_.find(a => a.name === name);
+ if (apn === undefined) {
+ // Potential edge case if an update is received before this is invoked.
+ console.error('Selected APN not in list');
+ return;
}
}
this.fire('apn-change', apn);
},
/**
- * @param {string} accessPointName
- * @return {boolean} True if the 'other' APN is currently selected.
+ * @return {boolean}
* @private
*/
- isOtherSelected_(accessPointName) {
- if (!this.managedProperties) {
- return false;
- }
- const apnList = this.getApnList_();
- const apn = this.findApnInList_(apnList, accessPointName);
- return apn === undefined;
+ isDisabled_() {
+ return this.selectedApn_ === '';
},
/**
- * @param {!chromeos.networkConfig.mojom.ApnProperties} apn
- * @return {string} The most descriptive name for the access point.
+ * @return {boolean}
* @private
*/
- apnDesc_(apn) {
- return apn.localizedName || apn.name || apn.accessPointName;
+ showOtherApn_() {
+ return this.selectedApn_ === kOtherAccessPointName;
},
/**
- * @param {!Array<!chromeos.networkConfig.mojom.ApnProperties>} apnList
- * @param {string} accessPointName
- * @return {chromeos.networkConfig.mojom.ApnProperties|undefined} The entry in
- * |apnList| matching |accessPointName| if it exists, or undefined.
+ * @param {!chromeos.networkConfig.mojom.ApnProperties} apn
+ * @return {string} The most descriptive name for the access point.
* @private
*/
- findApnInList_(apnList, accessPointName) {
- return apnList.find(function(a) {
- return a.accessPointName === accessPointName;
- });
+ apnDesc_(apn) {
+ assert(apn.name);
+ return apn.localizedName || apn.name;
},
/**
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html
index a01762f6acb..d0203e82933 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html
@@ -1,5 +1,7 @@
<link rel="import" href="../../../html/polymer.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
<link rel="import" href="mojo_interface_provider.html">
<link rel="import" href="network_listener_behavior.html">
<link rel="import" href="../../../cr_elements/action_link_css.html">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.js
index c341b01eba1..95d24a2dd8c 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.js
@@ -7,7 +7,7 @@
*/
/** @polymerBehavior */
-const NetworkConfigElementBehavior = {
+/* #export */ const NetworkConfigElementBehavior = {
properties: {
disabled: {
type: Boolean,
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
index 9e57d8efd1b..3d36500a2a7 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
@@ -1,9 +1,9 @@
<link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_indicator_mojo.html">
<link rel="import" href="../../../cr_elements/shared_vars_css.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="cr_policy_network_indicator_mojo.html">
<link rel="import" href="network_config_element_behavior.html">
<link rel="import" href="network_shared_css.html">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_select.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
index 8220cc12d82..c22845fdfc4 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_select.html
@@ -1,11 +1,11 @@
<link rel="import" href="../../../html/polymer.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_indicator_mojo.html">
<link rel="import" href="../../../cr_elements/policy/cr_tooltip_icon.html">
<link rel="import" href="../../../cr_elements/shared_vars_css.html">
<link rel="import" href="../../../cr_elements/shared_style_css.html">
<link rel="import" href="../../../cr_elements/md_select_css.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="cr_policy_network_indicator_mojo.html">
<link rel="import" href="network_config_element_behavior.html">
<link rel="import" href="network_shared_css.html">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
index 1036dedc58f..2fc37bed31b 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
@@ -1,34 +1,53 @@
<link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../../cr_elements/cr_toggle/cr_toggle.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_indicator_mojo.html">
<link rel="import" href="../../../cr_elements/shared_vars_css.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="cr_policy_network_indicator_mojo.html">
<link rel="import" href="network_config_element_behavior.html">
<link rel="import" href="network_shared_css.html">
<dom-module id="network-config-toggle">
<template>
<style include="network-shared">
+ :host {
+ cursor: pointer;
+ }
+ :host([disabled]) {
+ cursor: initial;
+ }
cr-policy-network-indicator-mojo {
--cr-tooltip-icon-margin-start: var(--cr-controlled-by-spacing);
}
+ cr-policy-network-indicator-mojo.left {
+ margin-inline-end: var(--cr-controlled-by-spacing);
+ }
div.property-box {
width: 100%;
}
</style>
- <div class="property-box" actionable>
+ <div class="property-box">
<div class="start">
- [[label]]
+ <div aria-hidden="true">[[label]]</div>
+ <div id="sub-label" class="cr-secondary-text" aria-hidden="true">
+ [[subLabel]]
+ </div>
</div>
+ <template is="dom-if" if="[[policyOnLeft]]">
+ <cr-policy-network-indicator-mojo class="left"
+ property="[[property]]" tooltip-position="left">
+ </cr-policy-network-indicator-mojo>
+ </template>
<cr-toggle checked="{{checked}}"
disabled="[[getDisabled_(disabled, property)]]"
- aria-label$="[[label]]">
+ aria-label$="[[label]]" aria-describedby="sub-label">
</cr-toggle>
- <cr-policy-network-indicator-mojo
- property="[[property]]" tooltip-position="left">
- </cr-policy-network-indicator-mojo>
+ <template is="dom-if" if="[[!policyOnLeft]]">
+ <cr-policy-network-indicator-mojo
+ property="[[property]]" tooltip-position="left">
+ </cr-policy-network-indicator-mojo>
+ </template>
</div>
</template>
<script src="network_config_toggle.js"></script>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
index fe2e459cf37..87ebe656635 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
@@ -16,18 +16,34 @@ Polymer({
properties: {
label: String,
+ subLabel: String,
+
checked: {
type: Boolean,
value: false,
reflectToAttribute: true,
notify: true,
},
+
+ /**
+ * Uses Settings styling when true (policy icon is left of the toggle)
+ */
+ policyOnLeft: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
},
listeners: {
'click': 'onHostTap_',
},
+ /** @override */
+ focus() {
+ this.$$('cr-toggle').focus();
+ },
+
/**
* Handles non cr-toggle button clicks (cr-toggle handles its own click events
* which don't bubble).
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_icon.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_icon.html
index ca1475871b4..84065f5c3bf 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_icon.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_icon.html
@@ -1,10 +1,10 @@
<link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../chromeos/network/network_icons.html">
-<link rel="import" href="onc_mojo.html">
<link rel="import" href="../../../cr_elements/hidden_style_css.html">
<link rel="import" href="../../../html/i18n_behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="onc_mojo.html">
<dom-module id="network-icon">
<template>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html
index a55a625f35a..7926dfafe55 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html
@@ -2,8 +2,8 @@
<link rel="import" href="../../../cr_elements/cr_toggle/cr_toggle.html">
<link rel="import" href="../../../cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
<link rel="import" href="../../../html/i18n_behavior.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
<link rel="import" href="network_property_list_mojo.html">
<link rel="import" href="network_shared_css.html">
@@ -21,7 +21,7 @@
<cr-policy-indicator indicator-type="[[getPolicyIndicatorType(
managedProperties.ipAddressConfigType)]]">
</cr-policy-indicator>
- <cr-toggle checked="{{automatic_}}"
+ <cr-toggle id="autoConfigIpToggle" checked="{{automatic_}}"
disabled="[[!canChangeIPConfigType_(managedProperties)]]"
on-change="onAutomaticChange_"
aria-labelledby="autoIPConfigLabel">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
index 247901e5d17..aca1decb29c 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
@@ -49,11 +49,14 @@ const getRoutingPrefixAsNetmask = function(prefixLength) {
*/
const getRoutingPrefixAsLength = function(netmask) {
'use strict';
- let prefixLength = 0;
+ if (!netmask) {
+ return chromeos.networkConfig.mojom.NO_ROUTING_PREFIX;
+ }
const tokens = netmask.split('.');
if (tokens.length !== 4) {
- return -1;
+ return chromeos.networkConfig.mojom.NO_ROUTING_PREFIX;
}
+ let prefixLength = 0;
for (let i = 0; i < tokens.length; ++i) {
const token = tokens[i];
// If we already found the last mask and the current one is not
@@ -137,6 +140,14 @@ Polymer({
},
/**
+ * Returns the automatically configure IP CrToggleElement.
+ * @return {?CrToggleElement}
+ */
+ getAutoConfigIpToggle() {
+ return /** @type {?CrToggleElement} */ (this.$$('#autoConfigIpToggle'));
+ },
+
+ /**
* Saved static IP configuration properties when switching to 'automatic'.
* @private {!OncMojo.IPConfigUIProperties|undefined}
*/
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list.js
index 2fed17a7beb..77c50ebf42e 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list.js
@@ -90,7 +90,7 @@ Polymer({
/** @private */
updateListItems_() {
- this.saveScroll(this.$.networkList);
+ this.saveScroll(/** @type {!IronListElement} */ (this.$.networkList));
const beforeNetworks = this.customItems.filter(function(item) {
return item.showBeforeNetworksList === true;
});
@@ -98,7 +98,7 @@ Polymer({
return item.showBeforeNetworksList !== true;
});
this.listItems_ = beforeNetworks.concat(this.networks, afterNetworks);
- this.restoreScroll(this.$.networkList);
+ this.restoreScroll(/** @type {!IronListElement} */ (this.$.networkList));
this.updateScrollableContents();
if (this.focusRequested_) {
this.async(function() {
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.html
index cab8b8eed10..e760d59f223 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.html
@@ -1,15 +1,15 @@
<link rel="import" href="../../../html/polymer.html">
-<link rel="import" href="network_icon.html">
-<link rel="import" href="onc_mojo.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../../cr_elements/cr_icon_button/cr_icon_button.html">
<link rel="import" href="../../../cr_elements/icons.html">
<link rel="import" href="../../../cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
<link rel="import" href="../../../cr_elements/shared_style_css.html">
<link rel="import" href="../../../cr_elements/shared_vars_css.html">
<link rel="import" href="../../../html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="network_icon.html">
+<link rel="import" href="onc_mojo.html">
<dom-module id="network-list-item">
<template>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
index dc84e735be9..c9a66919de1 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
@@ -169,8 +169,21 @@ Polymer({
const status = this.getNetworkStateText_();
const isManaged = this.item.source === OncSource.kDevicePolicy ||
this.item.source === OncSource.kUserPolicy;
- const index = this.parentElement.items.indexOf(this.item) + 1;
- const total = this.parentElement.items.length;
+
+ // TODO(jonmann): Reaching into the parent element breaks encapsulation so
+ // refactor this logic into the parent (NetworkList) and pass into
+ // NetworkListItem as a property.
+ let index;
+ let total;
+ if (this.parentElement.items) {
+ index = this.parentElement.items.indexOf(this.item) + 1;
+ total = this.parentElement.items.length;
+ } else {
+ // This should only happen in tests; see TODO above.
+ index = 0;
+ total = 1;
+ }
+
switch (this.item.type) {
case NetworkType.kCellular:
if (isManaged) {
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_types.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_types.js
index 678692f020a..a98bcf66a95 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_types.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_list_types.js
@@ -23,3 +23,5 @@ NetworkList.CustomItemState;
/** @typedef {OncMojo.NetworkStateProperties|NetworkList.CustomItemState} */
NetworkList.NetworkListItemType;
+
+// #export {NetworkList}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.js
index d80f8ffdd20..c1e6de07362 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.js
@@ -2,13 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// #import {MojoInterfaceProviderImpl} from './mojo_interface_provider.m.js';
+
/**
* @fileoverview Polymer behavior for observing CrosNetworkConfigObserver
* events.
*/
/** @polymerBehavior */
-const NetworkListenerBehavior = {
+/* #export */ const NetworkListenerBehavior = {
/** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigObserver} */
observer_: null,
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
index 0c68cc71ce8..0c0c0f4ddfb 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
@@ -1,13 +1,13 @@
<link rel="import" href="../../../html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
<link rel="import" href="../../../cr_elements/cr_radio_button/cr_radio_button.html">
<link rel="import" href="../../../cr_elements/cr_radio_group/cr_radio_group.html">
<link rel="import" href="../../../cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
<link rel="import" href="../../../html/i18n_behavior.html">
<link rel="import" href="../../../cr_elements/md_select_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
<link rel="import" href="network_shared_css.html">
<dom-module id="network-nameservers">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
index ad2206877c3..a7025298708 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
@@ -71,6 +71,14 @@ Polymer({
*/
savedNameservers_: [],
+ /*
+ * Returns the nameserver type CrRadioGroupElement.
+ * @return {?CrRadioGroupElement}
+ */
+ getNameserverRadioButtons() {
+ return /** @type {?CrRadioGroupElement} */ (this.$$('#nameserverType'));
+ },
+
/**
* Returns true if |nameservers| contains any all google nameserver entries
* and only google nameserver entries or empty entries.
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_password_input.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
index 66e8763a97d..f327000afd8 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_password_input.html
@@ -2,10 +2,10 @@
<link rel="import" href="../../../cr_elements/cr_icon_button/cr_icon_button.html">
<link rel="import" href="../../../cr_elements/cr_icons_css.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_indicator_mojo.html">
<link rel="import" href="../../../cr_elements/shared_vars_css.html">
<link rel="import" href="../../../html/i18n_behavior.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="cr_policy_network_indicator_mojo.html">
<link rel="import" href="network_config_element_behavior.html">
<link rel="import" href="network_shared_css.html">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.html
index 24c4de57996..a982414f911 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.html
@@ -1,11 +1,11 @@
<link rel="import" href="../../../html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_indicator_mojo.html">
<link rel="import" href="../../../cr_elements/shared_style_css.html">
<link rel="import" href="../../../html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
+<link rel="import" href="cr_policy_network_indicator_mojo.html">
<link rel="import" href="network_shared_css.html">
<dom-module id="network-property-list-mojo">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
index 075080a3639..b9f058855de 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
@@ -117,6 +117,8 @@ Polymer({
result += 'OTP';
} else if (subKey === 'ssid') {
result += 'SSID';
+ } else if (subKey === 'bssid') {
+ result += 'BSSID';
} else if (subKey === 'serverCa') {
result += 'ServerCA';
} else if (subKey === 'vpn') {
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
index 7df225d8a45..41cf8344064 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
@@ -1,14 +1,14 @@
<link rel="import" href="../../../html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../../cr_elements/cr_button/cr_button.html">
<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
<link rel="import" href="../../../cr_elements/cr_toggle/cr_toggle.html">
<link rel="import" href="../../../cr_elements/hidden_style_css.html">
-<link rel="import" href="../../../cr_elements/policy/cr_policy_network_behavior_mojo.html">
<link rel="import" href="../../../html/assert.html">
<link rel="import" href="../../../html/i18n_behavior.html">
<link rel="import" href="../../../cr_elements/md_select_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="cr_policy_network_behavior_mojo.html">
<link rel="import" href="network_proxy_exclusions.html">
<link rel="import" href="network_proxy_input.html">
<link rel="import" href="network_shared_css.html">
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.html b/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.html
index fa7ff789d05..ad9823a513e 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.html
@@ -1,2 +1,7 @@
<link rel="import" href="../../../html/assert.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
+<link rel="import" href="chrome://resources/mojo/services/network/public/mojom/ip_address.mojom.html">
+<link rel="import" href="chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom.html">
+<link rel="import" href="chrome://resources/mojo/mojo/public/mojom/base/time.mojom.html">
+<link rel="import" href="chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html">
<script src="onc_mojo.js"></script>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js b/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
index a193123d179..d9dd579801e 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
@@ -2,13 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// #import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+// #import {loadTimeData} from '../../../js/load_time_data.m.js';
+
/**
* @fileoverview Utilities supporting network_config.mojom types. The strings
* returned in the getFooTypeString methods are used for looking up localized
* strings and for debugging. They are not intended to be drectly user facing.
*/
-class OncMojo {
+/* #export */ class OncMojo {
/**
* @param {number|undefined} value
* @return {string}
@@ -1055,6 +1058,43 @@ class OncMojo {
assertNotReached();
return 'OncNotConnected';
}
+
+ /**
+ * Returns true the IPAddress bytes match.
+ * @param {?network.mojom.IPAddress|undefined} a
+ * @param {?network.mojom.IPAddress|undefined} b
+ * @return {boolean}
+ */
+ static ipAddressMatch(a, b) {
+ if (!a || !b) {
+ return !!a === !!b;
+ }
+ const abytes = a.addressBytes;
+ const bbytes = b.addressBytes;
+ if (abytes.length !== bbytes.length) {
+ return false;
+ }
+ for (let i = 0; i < abytes.length; ++i) {
+ if (abytes[i] !== bbytes[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true the SIMLockStatus properties match.
+ * @param {?chromeos.networkConfig.mojom.SIMLockStatus|undefined} a
+ * @param {?chromeos.networkConfig.mojom.SIMLockStatus|undefined} b
+ * @return {boolean}
+ */
+ static simLockStatusMatch(a, b) {
+ if (!a || !b) {
+ return !!a === !!b;
+ }
+ return a.lockType === b.lockType && a.lockEnabled === b.lockEnabled &&
+ a.retriesLeft === b.retriesLeft;
+ }
}
/** @typedef {chromeos.networkConfig.mojom.DeviceStateProperties} */
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network_health/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/network_health/BUILD.gn
index 6c1f0d8f6ce..607bd812ae1 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/network_health/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network_health/BUILD.gn
@@ -11,12 +11,18 @@ assert(is_chromeos, "Only ChromeOS components belong here.")
# JS type check for Polymer 2 and 3
js_type_check("closure_compile") {
- deps = [ ":network_health_summary" ]
+ deps = [
+ ":network_diagnostics",
+ ":network_health_summary",
+ ]
}
js_type_check("closure_compile_module") {
is_polymer3 = true
- deps = [ ":network_health_summary.m" ]
+ deps = [
+ ":network_diagnostics.m",
+ ":network_health_summary.m",
+ ]
}
# Sources with Polymer 3 generated modules
@@ -29,6 +35,13 @@ js_library("network_health_summary") {
]
}
+js_library("network_diagnostics") {
+ deps = [
+ "//chromeos/services/network_health/public/mojom:mojom_js_library_for_compile",
+ "//ui/webui/resources/js:i18n_behavior",
+ ]
+}
+
js_library("network_health_summary.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network_health/network_health_summary.m.js" ]
deps = [
@@ -40,6 +53,16 @@ js_library("network_health_summary.m") {
extra_deps = [ ":network_health_summary_module" ]
}
+js_library("network_diagnostics.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.m.js" ]
+ deps = [
+ "//chromeos/services/network_health/public/mojom:mojom_js_library_for_compile",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/js:i18n_behavior.m",
+ ]
+ extra_deps = [ ":network_diagnostics_module" ]
+}
+
# polymer_modulizer for converting Polymer2 to Polymer3
polymer_modulizer("network_health_summary") {
@@ -48,6 +71,15 @@ polymer_modulizer("network_health_summary") {
html_type = "dom-module"
}
+polymer_modulizer("network_diagnostics") {
+ js_file = "network_diagnostics.js"
+ html_file = "network_diagnostics.html"
+ html_type = "dom-module"
+}
+
group("polymer3_elements") {
- public_deps = [ ":network_health_summary_module" ]
+ public_deps = [
+ ":network_diagnostics_module",
+ ":network_health_summary_module",
+ ]
}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html b/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html
new file mode 100644
index 00000000000..79255f781ac
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.html
@@ -0,0 +1,86 @@
+<link rel="import" href="../../../html/polymer.html">
+
+<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
+<link rel="import" href="chrome://resources/mojo/chromeos/services/network_health/public/mojom/network_diagnostics.mojom.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
+<link rel="import" href="../../../cr_elements/cr_button/cr_button.html">
+<link rel="import" href="../../../html/i18n_behavior.html">
+
+<dom-module id="network-diagnostics">
+ <template>
+ <style>
+ .button-group > * {
+ margin-inline-start: 10px;
+ }
+
+ .routine-group {
+ display: inline-flex;
+ flex-wrap: wrap;
+ }
+
+ .routine-container {
+ border: 2px solid rgb(150, 150, 150);
+ height: 50px;
+ margin: 10px;
+ padding: 5px;
+ width: 400px;
+ }
+
+ .run-btn {
+ background-color: white;
+ }
+
+ .routine-name {
+ font-size: 1rem;
+ font-weight: bold;
+ }
+
+ .routine-content {
+ display: flex;
+ }
+
+ .routine-result {
+ flex: 1;
+ }
+
+ .result-passed {
+ background-color: rgb(217, 234, 211);
+ }
+
+ .result-error {
+ background-color: rgb(244, 204, 204);
+ }
+
+ .result-not-run {
+ background-color: rgb(255, 242, 204);
+ }
+ </style>
+ <div class="button-group">
+ <cr-button on-click="onRunAllRoutinesClick_">
+ [[i18n('NetworkDiagnosticsRunAll')]]
+ </cr-button>
+ <cr-button on-click="onSendFeedbackReportClick_">
+ [[i18n('NetworkDiagnosticsSendFeedback')]]
+ </cr-button>
+ </div>
+ <div class="routine-group">
+ <template is="dom-repeat" items="[[routines_]]" as="routine">
+ <div class="routine-container">
+ <div class="routine-name">[[i18n(routine.name)]]</div>
+ <div class="routine-content">
+ <span class="routine-result">[[routine.resultMsg]]</span>
+
+ <template is="dom-if" if="[[routine.running]]">
+ <paper-spinner-lite active></paper-spinner-lite>
+ </template>
+
+ <cr-button class="run-btn" hidden="[[routine.running]]" on-click="onRunRoutineClick_">
+ [[i18n('NetworkDiagnosticsRun')]]
+ </cr-button>
+ </div>
+ </div>
+ </template>
+ </div>
+ </template>
+ <script src="network_diagnostics.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js b/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
new file mode 100644
index 00000000000..ca948dc298d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
@@ -0,0 +1,348 @@
+
+// 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.
+
+/**
+ * @fileoverview Polymer element for interacting with Network Diagnostics.
+ */
+
+// Namespace to make using the mojom objects more readable.
+const diagnosticsMojom = chromeos.networkDiagnostics.mojom;
+
+/**
+ * A routine response from the Network Diagnostics mojo service.
+ * @typedef {{
+ * verdict: chromeos.networkDiagnostics.mojom.RoutineVerdict,
+ * }}
+ * RoutineResponse can optionally have a `problems` field, which is an array of
+ * enums relevant to the routine run. Unfortunately the closure compiler cannot
+ * handle optional object fields.
+ */
+let RoutineResponse;
+
+/**
+ * A network diagnostics routine. Holds descriptive information about the
+ * routine, and it's transient state.
+ * @typedef {{
+ * name: string,
+ * type: !RoutineType,
+ * running: boolean,
+ * resultMsg: string,
+ * result: ?RoutineResponse,
+ * }}
+ */
+let Routine;
+
+/**
+ * Definition for all Network diagnostic routine types. This enum is intended
+ * to be used as an index in an array of routines.
+ * @enum {number}
+ */
+const RoutineType = {
+ LAN_CONNECTIVITY: 0,
+ SIGNAL_STRENGTH: 1,
+ GATEWAY_PING: 2,
+ SECURE_WIFI: 3,
+ DNS_RESOLVER: 4,
+ DNS_LATENCY: 5,
+ DNS_RESOLUTION: 6,
+};
+
+/**
+ * Helper function to create a routine object.
+ * @param {string} name
+ * @param {!RoutineType} type
+ * @return {!Routine} Routine object
+ */
+function createRoutine(name, type) {
+ return {name: name, type: type, running: false, resultMsg: '', result: null};
+}
+
+Polymer({
+ is: 'network-diagnostics',
+
+ behaviors: [
+ I18nBehavior,
+ ],
+
+ properties: {
+ /**
+ * List of Diagnostics Routines
+ * @private {!Array<!Routine>}
+ */
+ routines_: {
+ type: Array,
+ value: function() {
+ const routines = [];
+ routines[RoutineType.LAN_CONNECTIVITY] = createRoutine(
+ 'NetworkDiagnosticsLanConnectivity', RoutineType.LAN_CONNECTIVITY);
+ routines[RoutineType.SIGNAL_STRENGTH] = createRoutine(
+ 'NetworkDiagnosticsSignalStrength', RoutineType.SIGNAL_STRENGTH);
+ routines[RoutineType.GATEWAY_PING] = createRoutine(
+ 'NetworkDiagnosticsGatewayCanBePinged', RoutineType.GATEWAY_PING);
+ routines[RoutineType.SECURE_WIFI] = createRoutine(
+ 'NetworkDiagnosticsHasSecureWiFiConnection',
+ RoutineType.SECURE_WIFI);
+ routines[RoutineType.DNS_RESOLVER] = createRoutine(
+ 'NetworkDiagnosticsDnsResolverPresent', RoutineType.DNS_RESOLVER);
+ routines[RoutineType.DNS_LATENCY] = createRoutine(
+ 'NetworkDiagnosticsDnsLatency', RoutineType.DNS_LATENCY);
+ routines[RoutineType.DNS_RESOLUTION] = createRoutine(
+ 'NetworkDiagnosticsDnsResolution', RoutineType.DNS_RESOLUTION);
+ return routines;
+ }
+ }
+ },
+
+ /**
+ * Network Diagnostics mojo remote.
+ * @private {
+ * ?chromeos.networkDiagnostics.mojom.NetworkDiagnosticsRoutinesRemote}
+ */
+ networkDiagnostics_: null,
+
+ /** @override */
+ created() {
+ this.networkDiagnostics_ =
+ diagnosticsMojom.NetworkDiagnosticsRoutines.getRemote();
+ },
+
+ /** @private */
+ onRunAllRoutinesClick_() {
+ for (const routine of this.routines_) {
+ this.runRoutine_(routine.type);
+ }
+ },
+
+ /**
+ * @param {!Event} event
+ * @private
+ */
+ onRunRoutineClick_(event) {
+ this.runRoutine_(event.model.index);
+ },
+
+ /** @private */
+ onSendFeedbackReportClick_() {
+ const results = {};
+ for (const routine of this.routines_) {
+ if (routine.result) {
+ const name = routine.name.replace('NetworkDiagnostics', '');
+ const result = {};
+ result['verdict'] =
+ this.getRoutineVerdictFeedbackString_(routine.result.verdict);
+ if (routine.result.problems && routine.result.problems.length > 0) {
+ result['problems'] = this.getRoutineProblemsFeedbackString_(
+ routine.type, routine.result.problems);
+ }
+
+ results[name] = result;
+ }
+ }
+ this.fire('open-feedback-dialog', JSON.stringify(results, undefined, 2));
+ },
+
+ /**
+ * @param {!RoutineType} type
+ * @private
+ */
+ runRoutine_(type) {
+ this.set(`routines_.${type}.running`, true);
+ this.set(`routines_.${type}.resultMsg`, '');
+ this.set(`routines_.${type}.result`, null);
+ const element =
+ this.shadowRoot.querySelectorAll('.routine-container')[type];
+ element.classList.remove('result-passed', 'result-error', 'result-not-run');
+ switch (type) {
+ case RoutineType.LAN_CONNECTIVITY:
+ this.networkDiagnostics_.lanConnectivity().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.SIGNAL_STRENGTH:
+ this.networkDiagnostics_.signalStrength().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.GATEWAY_PING:
+ this.networkDiagnostics_.gatewayCanBePinged().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.SECURE_WIFI:
+ this.networkDiagnostics_.hasSecureWiFiConnection().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.DNS_RESOLVER:
+ this.networkDiagnostics_.dnsResolverPresent().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.DNS_LATENCY:
+ this.networkDiagnostics_.dnsLatency().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ case RoutineType.DNS_RESOLUTION:
+ this.networkDiagnostics_.dnsResolution().then(
+ result => this.evaluateRoutine_(type, result));
+ break;
+ }
+ },
+
+ /**
+ * @param {!RoutineType} type
+ * @param {!RoutineResponse} result
+ * @private
+ */
+ evaluateRoutine_(type, result) {
+ const routine = `routines_.${type}`;
+ this.set(`${routine}.running`, false);
+
+ const element =
+ this.shadowRoot.querySelectorAll('.routine-container')[type];
+ let resultMsg = '';
+
+ switch (result.verdict) {
+ case diagnosticsMojom.RoutineVerdict.kNoProblem:
+ resultMsg = this.i18n('NetworkDiagnosticsPassed');
+ element.classList.add('result-passed');
+ break;
+ case diagnosticsMojom.RoutineVerdict.kProblem:
+ resultMsg = this.i18n('NetworkDiagnosticsFailed');
+ element.classList.add('result-error');
+ break;
+ case diagnosticsMojom.RoutineVerdict.kNotRun:
+ resultMsg = this.i18n('NetworkDiagnosticsNotRun');
+ element.classList.add('result-not-run');
+ break;
+ }
+
+ this.set(routine + '.result', result);
+ this.set(routine + '.resultMsg', resultMsg);
+ },
+
+ /**
+ *
+ * @param {!RoutineType} type The type of routine
+ * @param {!Array<number>} problems The list of problems for the routine
+ * @return {Array<string>} String for a networking problem used for feedback
+ * @private
+ */
+ getRoutineProblemsFeedbackString_(type, problems) {
+ const problemStrings = [];
+ for (const problem of problems) {
+ switch (type) {
+ case RoutineType.SIGNAL_STRENGTH:
+ switch (problem) {
+ case diagnosticsMojom.SignalStrengthProblem.kSignalNotFound:
+ problemStrings.push('Signal Not Found');
+ break;
+ case diagnosticsMojom.SignalStrengthProblem.kWeakSignal:
+ problemStrings.push('Weak Signal');
+ break;
+ }
+ break;
+
+ case RoutineType.GATEWAY_PING:
+ switch (problem) {
+ case diagnosticsMojom.GatewayCanBePingedProblem.kUnreachableGateway:
+ problemStrings.push('Gateway is Unreachable');
+ break;
+ case diagnosticsMojom.GatewayCanBePingedProblem
+ .kFailedToPingDefaultNetwork:
+ problemStrings.push('Failed to ping default network');
+ break;
+ case diagnosticsMojom.GatewayCanBePingedProblem
+ .kDefaultNetworkAboveLatencyThreshold:
+ problemStrings.push('Default network above latency threshold');
+ break;
+ case diagnosticsMojom.GatewayCanBePingedProblem
+ .kUnsuccessfulNonDefaultNetworksPings:
+ problemStrings.push('Non-default network has failed pings');
+ break;
+ case diagnosticsMojom.GatewayCanBePingedProblem
+ .kNonDefaultNetworksAboveLatencyThreshold:
+ problemStrings.push(
+ 'Non-default network is above latency threshold');
+ break;
+ }
+ break;
+
+ case RoutineType.SECURE_WIFI:
+ switch (problem) {
+ case diagnosticsMojom.HasSecureWiFiConnectionProblem
+ .kSecurityTypeNone:
+ problemStrings.push('WiFi Network is not secure');
+ break;
+ case diagnosticsMojom.HasSecureWiFiConnectionProblem
+ .kSecurityTypeWep8021x:
+ problemStrings.push('WiFi Network secured with WEP 802.1x');
+ break;
+ case diagnosticsMojom.HasSecureWiFiConnectionProblem
+ .kSecurityTypeWepPsk:
+ problemStrings.push('WiFi Network secured with WEP PSK');
+ break;
+ case diagnosticsMojom.HasSecureWiFiConnectionProblem
+ .kUnknownSecurityType:
+ problemStrings.push('WiFi Network secured with WEP PSK');
+ break;
+ }
+ break;
+
+ case RoutineType.DNS_RESOLVER:
+ switch (problem) {
+ case diagnosticsMojom.DnsResolverPresentProblem.kNoNameServersFound:
+ problemStrings.push('No name servers found');
+ break;
+ case diagnosticsMojom.DnsResolverPresentProblem
+ .kMalformedNameServers:
+ problemStrings.push('Malformed name servers');
+ break;
+ case diagnosticsMojom.DnsResolverPresentProblem.kEmptyNameServers:
+ problemStrings.push('Empty name servers');
+ break;
+ }
+ break;
+
+ case RoutineType.DNS_LATENCY:
+ switch (problem) {
+ case diagnosticsMojom.DnsLatencyProblem.kFailedToResolveAllHosts:
+ problemStrings.push('Failed to resolve all hosts');
+ break;
+ case diagnosticsMojom.DnsLatencyProblem.kSlightlyAboveThreshold:
+ problemStrings.push('DNS latency slightly above threshold');
+ break;
+ case diagnosticsMojom.DnsLatencyProblem
+ .kSignificantlyAboveThreshold:
+ problemStrings.push('DNS latency significantly above threshold');
+ break;
+ }
+ break;
+
+ case RoutineType.DNS_RESOLUTION:
+ switch (problem) {
+ case diagnosticsMojom.DnsResolutionProblem.kFailedToResolveHost:
+ problemStrings.push('Failed to resolve host');
+ break;
+ }
+ break;
+ }
+ }
+
+ return problemStrings;
+ },
+
+ /**
+ * @param {!chromeos.networkDiagnostics.mojom.RoutineVerdict} verdict
+ * @return {string} String for a network diagnostic verdict used for feedback
+ * @private
+ */
+ getRoutineVerdictFeedbackString_(verdict) {
+ switch (verdict) {
+ case diagnosticsMojom.RoutineVerdict.kNoProblem:
+ return 'Passed';
+ case diagnosticsMojom.RoutineVerdict.kNotRun:
+ return 'Not Run';
+ case diagnosticsMojom.RoutineVerdict.kProblem:
+ return 'Failed';
+ }
+ return 'Unknown';
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/os_cr_components.gni b/chromium/ui/webui/resources/cr_components/chromeos/os_cr_components.gni
new file mode 100644
index 00000000000..a99f39d8bbc
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/os_cr_components.gni
@@ -0,0 +1,22 @@
+# 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("//third_party/closure_compiler/compile_js.gni")
+
+cr_components_chromeos_namespace_rewrites = [
+ "network_config.MojoInterfaceProvider|MojoInterfaceProvider",
+ "settings.receivedEventFromKeyboard|receivedEventFromKeyboard",
+ "settings.LockScreenProgress|LockScreenProgress",
+ "cellular_setup.MojoInterfaceProvider|MojoInterfaceProvider",
+]
+
+cr_components_chromeos_auto_imports = [
+ "ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html|CrPolicyIndicatorType",
+ "ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.html|CrPolicyNetworkBehaviorMojo",
+ "ui/webui/resources/cr_components/chromeos/network/onc_mojo.html|OncMojo",
+ "ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.html|NetworkListenerBehavior",
+ "ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.html|MojoInterfaceProviderImpl,MojoInterfaceProvider",
+ "ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html|recordLockScreenProgress,LockScreenProgress",
+ "ui/webui/resources/html/assert.html|assert,assertNotReached",
+]
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
index a27e999205e..03c04595dce 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
@@ -3,6 +3,9 @@
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+import("../os_cr_components.gni")
js_type_check("closure_compile") {
deps = [
@@ -13,6 +16,7 @@ js_type_check("closure_compile") {
js_library("pin_keyboard") {
deps = [
+ ":lock_screen_constants",
"//ui/webui/resources/cr_elements/cr_input:cr_input",
"//ui/webui/resources/js:i18n_behavior",
]
@@ -36,28 +40,31 @@ js_library("setup_pin_keyboard") {
extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ]
}
-# TODO: Uncomment as the Polymer3 migration makes progress.
-# js_type_check("closure_compile_module") {
-# is_polymer3 = true
-# deps = [
-# ":lock_screen_constants.m",
-# ":pin_keyboard.m",
-# ":setup_pin_keyboard.m"
-# ]
-# }
+js_type_check("closure_compile_module") {
+ is_polymer3 = true
+ deps = [
+ ":lock_screen_constants.m",
+ ":pin_keyboard.m",
+ ":setup_pin_keyboard.m",
+ ]
+}
js_library("lock_screen_constants.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_elements/cr_profile_avatar_selector:cr_profile_avatar_selector.m",
+ "//ui/webui/resources/js:cr.m",
]
- extra_deps = [ ":lock_screen_constants_module" ]
+ extra_deps = [ ":modulize" ]
}
js_library("pin_keyboard.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.m.js" ]
deps = [
- # TODO: Fill those in.
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+ "//ui/webui/resources/js:i18n_behavior.m",
]
extra_deps = [ ":pin_keyboard_module" ]
}
@@ -65,8 +72,14 @@ js_library("pin_keyboard.m") {
js_library("setup_pin_keyboard.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js" ]
deps = [
- # TODO: Fill those in.
+ ":lock_screen_constants.m",
+ ":pin_keyboard.m",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+ "//ui/webui/resources/js:i18n_behavior.m",
]
+ externs_list = [ "$externs_path/quick_unlock_private.js" ]
+ extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ]
extra_deps = [ ":setup_pin_keyboard_module" ]
}
@@ -74,26 +87,36 @@ import("//tools/polymer/polymer.gni")
group("polymer3_elements") {
public_deps = [
- ":lock_screen_constants_module",
+ ":modulize",
+ ":pin_keyboard_icon_module",
":pin_keyboard_module",
":setup_pin_keyboard_module",
]
}
-polymer_modulizer("lock_screen_constants") {
- js_file = "lock_screen_constants.js"
- html_file = "lock_screen_constants.html"
- html_type = "dom-module"
-}
-
polymer_modulizer("pin_keyboard") {
js_file = "pin_keyboard.js"
html_file = "pin_keyboard.html"
html_type = "dom-module"
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+ auto_imports = cr_components_chromeos_auto_imports
}
polymer_modulizer("setup_pin_keyboard") {
js_file = "setup_pin_keyboard.js"
html_file = "setup_pin_keyboard.html"
html_type = "dom-module"
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+ auto_imports = cr_components_chromeos_auto_imports
+}
+
+js_modulizer("modulize") {
+ input_files = [ "lock_screen_constants.js" ]
+ namespace_rewrites = cr_components_chromeos_namespace_rewrites
+}
+
+polymer_modulizer("pin_keyboard_icon") {
+ js_file = "pin_keyboard_icon.m.js"
+ html_file = "pin_keyboard_icon.html"
+ html_type = "iron-iconset"
}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
index dbc85a36812..fd1606d2d22 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
@@ -16,7 +16,7 @@ cr.define('settings', function() {
* Stages the user can enter while setting up pin unlock.
* @enum {number}
*/
- const LockScreenProgress = {
+ /* #export */ const LockScreenProgress = {
START_SCREEN_LOCK: 0,
ENTER_PASSWORD_CORRECTLY: 1,
CHOOSE_PIN_OR_PASSWORD: 2,
@@ -29,7 +29,7 @@ cr.define('settings', function() {
* histogram.
* @param {settings.LockScreenProgress} currentProgress
*/
- const recordLockScreenProgress = function(currentProgress) {
+ /* #export */ const recordLockScreenProgress = function(currentProgress) {
if (currentProgress >= LockScreenProgress.MAX_BUCKET) {
console.error(
'Expected a enumeration value of ' + LockScreenProgress.MAX_BUCKET +
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index 1dc6eb3d818..74d26d2f35c 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -1,7 +1,3 @@
-<!-- TODO(crbug.com/603217): Use i18n instead of string literals. Figure out
- what i18n to use for keypad, ie, does 1 ABC make
- sense in every scenario? -->
-
<link rel="import" href="../../../html/polymer.html">
<link rel="import" href="../../../cr_elements/cr_button/cr_button.html">
@@ -267,3 +263,7 @@
</template>
<script src="pin_keyboard.js"></script>
</dom-module>
+
+<!-- TODO(crbug.com/603217): Use i18n instead of string literals. Figure out
+ what i18n to use for keypad, ie, does 1 ABC make
+ sense in every scenario? -->
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
index 1443e8fee07..eeebe765de4 100644
--- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
+++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
@@ -10,9 +10,6 @@
*
*/
-(function() {
-'use strict';
-
/**
* Keep in sync with the string keys provided by settings.
* @enum {string}
@@ -383,5 +380,3 @@ Polymer({
'';
},
});
-
-})();
diff --git a/chromium/ui/webui/resources/cr_components/cr_components_images.grdp b/chromium/ui/webui/resources/cr_components/cr_components_images.grdp
index 70d2a577251..04457a9e837 100644
--- a/chromium/ui/webui/resources/cr_components/cr_components_images.grdp
+++ b/chromium/ui/webui/resources/cr_components/cr_components_images.grdp
@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
+ <!-- CustomizeThemes Images -->
+ <include name="IDR_WEBUI_CR_COMPONENTS_CUSTOMIZE_THEMES_COLORIZE_ICON_SVG"
+ file="cr_components/customize_themes/colorize.svg"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CR_COMPONENTS_CUSTOMIZE_THEMES_BRUSH_ICON_SVG"
+ file="cr_components/customize_themes/brush.svg"
+ type="BINDATA" />
<if expr="chromeos">
<!-- MultiDeviceSetup Images -->
<include name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_START_SETUP_ICON_1X_PNG"
diff --git a/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp b/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp
index 2e2468044c8..61c6cd77fa0 100644
--- a/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp
+++ b/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp
@@ -134,6 +134,12 @@
<structure name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_CELLULAR_SETUP_JS"
file="cr_components/chromeos/cellular_setup/cellular_setup.js"
type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_PSIM_FLOW_HTML"
+ file="cr_components/chromeos/cellular_setup/psim_flow_ui.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_PSIM_FLOW_JS"
+ file="cr_components/chromeos/cellular_setup/psim_flow_ui.js"
+ type="chrome_html" />
<structure name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_MOJO_INTERFACE_PROVIDER_HTML"
file="cr_components/chromeos/cellular_setup/mojo_interface_provider.html"
type="chrome_html" />
@@ -264,6 +270,12 @@
<structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_HEALTH_SUMMARY_JS"
file="cr_components/chromeos/network_health/network_health_summary.js"
type="chrome_html" />
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_DIAGNOSTICS_HTML"
+ file="cr_components/chromeos/network_health/network_diagnostics.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_DIAGNOSTICS_JS"
+ file="cr_components/chromeos/network_health/network_diagnostics.js"
+ type="chrome_html" />
<!-- Shared between settings and add new share flow. -->
<structure name="IDR_WEBUI_CHROMEOS_SMB_SHARES_SMB_BROWSER_PROXY_HTML"
@@ -280,6 +292,18 @@
type="chrome_html" />
<!-- Used to render network-list and related subcomponents. -->
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_CR_POLICY_NETWORK_BEHAVIOR_MOJO_HTML"
+ file="cr_components/chromeos/network/cr_policy_network_behavior_mojo.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_CR_POLICY_NETWORK_BEHAVIOR_MOJO_JS"
+ file="cr_components/chromeos/network/cr_policy_network_behavior_mojo.js"
+ type="chrome_html" />
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_CR_POLICY_NETWORK_INDICATOR_MOJO_HTML"
+ file="cr_components/chromeos/network/cr_policy_network_indicator_mojo.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_CR_POLICY_NETWORK_INDICATOR_MOJO_JS"
+ file="cr_components/chromeos/network/cr_policy_network_indicator_mojo.js"
+ type="chrome_html" />
<structure name="IDR_CR_COMPONENTS_CHROMEOS_NETWORK_ICON_HTML"
file="cr_components/chromeos/network/network_icon.html"
type="chrome_html" />
diff --git a/chromium/ui/webui/resources/cr_components/cr_components_resources_v3.grdp b/chromium/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
index 96b5ae7929a..5924dd69a8e 100644
--- a/chromium/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
+++ b/chromium/ui/webui/resources/cr_components/cr_components_resources_v3.grdp
@@ -9,6 +9,24 @@
file="${root_gen_dir}/ui/webui/resources/cr_components/omnibox/cr_autocomplete_match_list.js"
use_base_dir="false"
type="BINDATA" />
+
+ <!-- customize_themes -->
+ <include name="IDR_WEBUI_CR_COMPONENTS_CUSTOMIZE_THEMES_CUSTOMIZE_THEMES_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/customize_themes/customize_themes.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CR_COMPONENTS_CUSTOMIZE_THEMES_THEME_ICON_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/customize_themes/theme_icon.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_CUSTOMIZE_THEMES_MOJOM_LITE_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom-lite.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CR_COMPONENTS_CUSTOMIZE_THEMES_BROWSER_PROXY_JS"
+ file="cr_components/customize_themes/browser_proxy.js"
+ type="BINDATA" />
+
<if expr="chromeos">
<include name="IDR_WEBUI_CHROMEOS_SMB_SHARES_ADD_SMB_SHARE_DIALOG_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.m.js"
@@ -22,6 +40,94 @@
file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_dialog.m.js"
use_base_dir="false"
type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CR_POLICY_NETWORK_BEHAVIOR_MOJO_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CR_POLICY_NETWORK_INDICATOR_MOJO_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_CONFIG_ELEMENT_BEHAVIOR_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_config_element_behavior.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_LISTENER_BEHAVIOR_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_listener_behavior.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_LIST_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_list.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_LIST_ITEM_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_list_item.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_LIST_TYPES_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_list_types.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_PASSWORD_INPUT_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_password_input.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_SHARED_CSS_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_shared_css.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_ONC_MOJOM_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/onc_mojo.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_MOJO_INTERFACE_PROVIDER_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/mojo_interface_provider.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_ICON_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_icon.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_NETWORK_ICONS_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/network/network_icons.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_SCREEN_CONSTANTS_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_SETUP_PIN_KEYBOARD_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_PIN_KEYBOARD_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_PIN_KEYBOARD_ICON_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_BASE_PAGE_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_FINAL_PAGE_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/cellular_setup/final_page.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_MOJO_INTERFACE_PROVIDER_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_WEBVIEW_POST_UTIL_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_WEBUI_CHROMEOS_CELLULAR_SETUP_SIM_DETECT_PAGE_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_components/chromeos/cellular_setup/sim_detect_page.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
</if>
<if expr="use_nss_certs">
<include name="IDR_WEBUI_CA_TRUST_EDIT_DIALOG_JS"
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/BUILD.gn b/chromium/ui/webui/resources/cr_components/customize_themes/BUILD.gn
new file mode 100644
index 00000000000..2f2f94cf709
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/html_to_js.gni")
+
+group("web_components") {
+ public_deps = [
+ ":mojom_js",
+ ":web_components_local",
+ ]
+}
+
+mojom("mojom") {
+ sources = [ "customize_themes.mojom" ]
+
+ public_deps = [
+ "//mojo/public/mojom/base",
+ "//skia/public/mojom",
+ ]
+}
+
+html_to_js("web_components_local") {
+ js_files = [
+ "customize_themes.js",
+ "theme_icon.js",
+ ]
+}
+
+js_type_check("closure_compile") {
+ is_polymer3 = true
+ deps = [
+ ":browser_proxy",
+ ":customize_themes",
+ ":theme_icon",
+ ]
+}
+
+js_library("customize_themes") {
+ deps = [
+ ":browser_proxy",
+ ":mojom_js_library_for_compile",
+ ":theme_icon",
+ "//skia/public/mojom:mojom_js_library_for_compile",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ "//ui/webui/resources/cr_elements/cr_grid",
+ "//ui/webui/resources/js:i18n_behavior.m",
+ ]
+}
+
+js_library("theme_icon") {
+ deps = [
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
+
+js_library("browser_proxy") {
+ deps = [
+ ":mojom_js_library_for_compile",
+ "//ui/webui/resources/js:cr.m",
+ ]
+}
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/OWNERS b/chromium/ui/webui/resources/cr_components/customize_themes/OWNERS
new file mode 100644
index 00000000000..3e630904d62
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/OWNERS
@@ -0,0 +1,9 @@
+aee@chromium.org
+alexilin@chromium.org
+mahmadi@chromium.org
+tiborg@chromium.org
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: UI>Browser>Themes
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/browser_proxy.js b/chromium/ui/webui/resources/cr_components/customize_themes/browser_proxy.js
new file mode 100644
index 00000000000..a5d2c2a92df
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/browser_proxy.js
@@ -0,0 +1,62 @@
+// 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.
+
+/**
+ * @fileoverview A helper object used by the customize-themes component to
+ * interact with the browser.
+ */
+
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-lite.js';
+import './customize_themes.mojom-lite.js';
+
+import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+
+/** @interface */
+export class CustomizeThemesBrowserProxy {
+ /** @return {customizeThemes.mojom.CustomizeThemesHandlerInterface} */
+ handler() {}
+
+ /** @return {customizeThemes.mojom.CustomizeThemesClientCallbackRouter} */
+ callbackRouter() {}
+
+ /** @param {string} url */
+ open(url) {}
+}
+
+/** @implements {CustomizeThemesBrowserProxy} */
+export class CustomizeThemesBrowserProxyImpl {
+ constructor() {
+ /** @private {customizeThemes.mojom.CustomizeThemesHandlerRemote} */
+ this.handler_ = new customizeThemes.mojom.CustomizeThemesHandlerRemote();
+
+ /** @private {customizeThemes.mojom.CustomizeThemesClientCallbackRouter} */
+ this.callbackRouter_ =
+ new customizeThemes.mojom.CustomizeThemesClientCallbackRouter();
+
+ /** @type {customizeThemes.mojom.CustomizeThemesHandlerFactoryRemote} */
+ const factory =
+ customizeThemes.mojom.CustomizeThemesHandlerFactory.getRemote();
+ factory.createCustomizeThemesHandler(
+ this.callbackRouter_.$.bindNewPipeAndPassRemote(),
+ this.handler_.$.bindNewPipeAndPassReceiver());
+ }
+
+ /** @override */
+ handler() {
+ return this.handler_;
+ }
+
+ /** @override */
+ callbackRouter() {
+ return this.callbackRouter_;
+ }
+
+ /** @override */
+ open(url) {
+ window.open(url, '_blank');
+ }
+}
+
+addSingletonGetter(CustomizeThemesBrowserProxyImpl);
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/brush.svg b/chromium/ui/webui/resources/cr_components/customize_themes/brush.svg
new file mode 100644
index 00000000000..f6d29d556ac
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/brush.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34a.996.996 0 0 0-1.41 0L9 12.25 11.75 15l8.96-8.96a.996.996 0 0 0 0-1.41z"/></svg> \ No newline at end of file
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/colorize.svg b/chromium/ui/webui/resources/cr_components/customize_themes/colorize.svg
new file mode 100644
index 00000000000..984de18801f
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/colorize.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M17.66 5.41l.92.92-2.69 2.69-.92-.92 2.69-2.69M17.67 3c-.26 0-.51.1-.71.29l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c.4-.4.4-1.03.01-1.42l-2.34-2.34c-.2-.19-.45-.29-.7-.29zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"/></svg> \ No newline at end of file
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.html b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.html
new file mode 100644
index 00000000000..3f73e993044
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.html
@@ -0,0 +1,152 @@
+<style include="cr-hidden-style cr-icons">
+ :host {
+ --cr-customize-themes-grid-gap: 20px;
+ --cr-customize-themes-icon-size: 72px;
+ }
+
+ #thirdPartyThemeContainer {
+ max-width: 544px;
+ width: 100%;
+ }
+
+ #thirdPartyTheme {
+ align-items: center;
+ border: 1px solid var(--google-grey-refresh-300);
+ border-radius: 5px;
+ color: var(--cr-primary-text-color);
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 24px;
+ padding: 0 16px;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ #thirdPartyTheme {
+ border-color: var(--google-grey-refresh-700);
+ }
+ }
+
+ #thirdPartyBrushIcon {
+ -webkit-mask-image: url(chrome://resources/cr_components/customize_themes/brush.svg);
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: 100%;
+ background-color: var(--cr-primary-text-color);
+ margin-inline-end: 20px;
+ min-height: 24px;
+ min-width: 24px;
+ }
+
+ #thirdPartyThemeNameContainer {
+ flex-grow: 1;
+ margin-inline-end: 24px;
+ }
+
+ #thirdPartyThemeName {
+ font-weight: bold;
+ }
+
+ #thirdPartyLink {
+ --cr-icon-button-fill-color: var(--cr-primary-text-color);
+ margin-inline-end: 24px;
+ }
+
+ #uninstallThirdPartyButton {
+ margin: 16px 0;
+ }
+
+ #themesContainer {
+ --cr-grid-gap: var(--cr-customize-themes-grid-gap);
+ }
+
+ #themesContainer > * {
+ outline-width: 0;
+ }
+
+ :host-context(.focus-outline-visible) #themesContainer > *:focus {
+ box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
+ }
+
+ #autogeneratedThemeContainer {
+ display: flex;
+ position: relative;
+ }
+
+ #colorPickerIcon {
+ -webkit-mask-image: url(chrome://resources/cr_components/customize_themes/colorize.svg);
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: 100%;
+ background-color: var(--google-grey-refresh-700);
+ height: 20px;
+ left: calc(50% - 10px);
+ position: absolute;
+ top: calc(50% - 10px);
+ width: 20px;
+ }
+
+ cr-theme-icon {
+ --cr-theme-icon-size: var(--cr-customize-themes-icon-size);
+ }
+
+ #autogeneratedTheme {
+ --cr-theme-icon-frame-color: var(--google-grey-refresh-100);
+ --cr-theme-icon-active-tab-color: white;
+ --cr-theme-icon-stroke-color: var(--google-grey-refresh-300);
+ }
+
+ #defaultTheme {
+ --cr-theme-icon-frame-color: rgb(222, 225, 230);
+ --cr-theme-icon-active-tab-color: white;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ #defaultTheme {
+ --cr-theme-icon-frame-color: rgb(var(--google-grey-900-rgb));
+ --cr-theme-icon-active-tab-color: rgb(50, 54, 57);
+ }
+ }
+
+</style>
+<div id="thirdPartyThemeContainer" hidden="[[!isThirdPartyTheme_(selectedTheme)]]">
+ <div id="thirdPartyTheme">
+ <div id="thirdPartyBrushIcon"></div>
+ <div id="thirdPartyThemeNameContainer">
+ <div id="thirdPartyThemeName" >
+ [[selectedTheme.info.thirdPartyThemeInfo.name]]
+ </div>
+ <div>[[i18n('thirdPartyThemeDescription')]]</div>
+ </div>
+ <cr-icon-button id="thirdPartyLink" class="icon-external" role="link"
+ on-click="onThirdPartyLinkButtonClick_">
+ </cr-icon-button>
+ <cr-button id="uninstallThirdPartyButton"
+ on-click="onUninstallThirdPartyThemeClick_">
+ [[i18n('uninstallThirdPartyThemeButton')]]
+ </cr-button>
+ </div>
+</div>
+<input id="colorPicker" type="color" on-change="onCustomFrameColorChange_"
+ hidden>
+</input>
+<cr-grid id="themesContainer" columns="6">
+ <div id="autogeneratedThemeContainer" title="[[i18n('colorPickerLabel')]]"
+ tabindex="0" on-click="onAutogeneratedThemeClick_">
+ <cr-theme-icon id="autogeneratedTheme"
+ selected$="[[isThemeIconSelected_('autogenerated', selectedTheme)]]">
+ </cr-theme-icon>
+ <div id="colorPickerIcon"></div>
+ </div>
+ <cr-theme-icon id="defaultTheme" title="[[i18n('defaultThemeLabel')]]"
+ on-click="onDefaultThemeClick_" tabindex="0"
+ selected$="[[isThemeIconSelected_('default', selectedTheme)]]">
+ </cr-theme-icon>
+ <template is="dom-repeat" id="themes" items="[[chromeThemes_]]">
+ <cr-theme-icon title="[[item.label]]" on-click="onChromeThemeClick_"
+ style="--cr-theme-icon-frame-color:
+ [[skColorToRgba_(item.colors.frame)]];
+ --cr-theme-icon-active-tab-color:
+ [[skColorToRgba_(item.colors.activeTab)]];"
+ tabindex="0"
+ selected$="[[isThemeIconSelected_(item.id, selectedTheme)]]">
+ </cr-theme-icon>
+ </template>
+</cr-grid>
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.js b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.js
new file mode 100644
index 00000000000..4862ee29f59
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.js
@@ -0,0 +1,197 @@
+// 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 '../../cr_elements/cr_button/cr_button.m.js';
+import '../../cr_elements/cr_icon_button/cr_icon_button.m.js';
+import '../../cr_elements/cr_icons_css.m.js';
+import '../../cr_elements/cr_grid/cr_grid.js';
+import '../../cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-lite.js';
+import './customize_themes.mojom-lite.js';
+import './theme_icon.js';
+
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assert} from '../../js/assert.m.js';
+import {hexColorToSkColor, skColorToRgba} from '../../js/color_utils.js';
+import {I18nBehavior} from '../../js/i18n_behavior.m.js';
+
+import {CustomizeThemesBrowserProxyImpl} from './browser_proxy.js';
+
+/**
+ * Element that lets the user configure the autogenerated theme.
+ * @polymer
+ * @extends {PolymerElement}
+ */
+export class CustomizeThemesElement extends mixinBehaviors
+([I18nBehavior], PolymerElement) {
+ static get is() {
+ return 'cr-customize-themes';
+ }
+
+ static get template() {
+ return html`{__html_template__}`;
+ }
+
+ static get properties() {
+ return {
+ /** @type {!customizeThemes.mojom.Theme} */
+ selectedTheme: {
+ type: Object,
+ observer: 'onThemeChange_',
+ notify: true,
+ },
+
+ /** @private {!Array<!customizeThemes.mojom.ChromeTheme>} */
+ chromeThemes_: Array,
+ };
+ }
+
+ constructor() {
+ super();
+ /** @private {customizeThemes.mojom.CustomizeThemesHandlerRemote} */
+ this.handler_ = CustomizeThemesBrowserProxyImpl.getInstance().handler();
+
+ /** @private {customizeThemes.mojom.CustomizeThemesClientCallbackRouter} */
+ this.callbackRouter_ =
+ CustomizeThemesBrowserProxyImpl.getInstance().callbackRouter();
+
+ /** @private {?number} */
+ this.setThemeListenerId_ = null;
+ }
+
+ /** @override */
+ connectedCallback() {
+ super.connectedCallback();
+ this.handler_.getChromeThemes().then(({chromeThemes}) => {
+ this.chromeThemes_ = chromeThemes;
+ });
+
+ this.setThemeListenerId_ =
+ this.callbackRouter_.setTheme.addListener(theme => {
+ this.selectedTheme = theme;
+ });
+ }
+
+ /** @override */
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.callbackRouter_.removeListener(assert(this.setThemeListenerId_));
+ }
+
+ confirmThemeChanges() {
+ this.handler_.confirmThemeChanges();
+ }
+
+ revertThemeChanges() {
+ this.handler_.revertThemeChanges();
+ }
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onCustomFrameColorChange_(e) {
+ this.handler_.applyAutogeneratedTheme(hexColorToSkColor(e.target.value));
+ }
+
+ /** @private */
+ onAutogeneratedThemeClick_() {
+ this.$.colorPicker.click();
+ }
+
+ /** @private */
+ onDefaultThemeClick_() {
+ this.handler_.applyDefaultTheme();
+ }
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onChromeThemeClick_(e) {
+ this.handler_.applyChromeTheme(this.$.themes.itemForElement(e.target).id);
+ }
+
+ /** @private */
+ onThemeChange_() {
+ if (this.selectedTheme.type !==
+ customizeThemes.mojom.ThemeType.kAutogenerated) {
+ return;
+ }
+ const rgbaFrameColor =
+ skColorToRgba(this.selectedTheme.info.autogeneratedThemeColors.frame);
+ const rgbaActiveTabColor = skColorToRgba(
+ this.selectedTheme.info.autogeneratedThemeColors.activeTab);
+ this.$.autogeneratedTheme.style.setProperty(
+ '--cr-theme-icon-frame-color', rgbaFrameColor);
+ this.$.autogeneratedTheme.style.setProperty(
+ '--cr-theme-icon-stroke-color', rgbaFrameColor);
+ this.$.autogeneratedTheme.style.setProperty(
+ '--cr-theme-icon-active-tab-color', rgbaActiveTabColor);
+ this.$.colorPickerIcon.style.setProperty(
+ 'background-color',
+ skColorToRgba(
+ this.selectedTheme.info.autogeneratedThemeColors.activeTabText));
+ }
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onUninstallThirdPartyThemeClick_(e) {
+ this.handler_.applyDefaultTheme();
+ this.handler_.confirmThemeChanges();
+ }
+
+ /** @private */
+ onThirdPartyLinkButtonClick_() {
+ CustomizeThemesBrowserProxyImpl.getInstance().open(
+ `https://chrome.google.com/webstore/detail/${
+ this.selectedTheme.info.thirdPartyThemeInfo.id}`);
+ }
+
+ /**
+ * @param {string|number} id
+ * @return {boolean}
+ * @private
+ */
+ isThemeIconSelected_(id) {
+ if (!this.selectedTheme) {
+ return false;
+ }
+ if (id === 'autogenerated') {
+ return this.selectedTheme.type ===
+ customizeThemes.mojom.ThemeType.kAutogenerated;
+ } else if (id === 'default') {
+ return this.selectedTheme.type ===
+ customizeThemes.mojom.ThemeType.kDefault;
+ } else {
+ return this.selectedTheme.type ===
+ customizeThemes.mojom.ThemeType.kChrome &&
+ id === this.selectedTheme.info.chromeThemeId;
+ }
+ }
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ isThirdPartyTheme_() {
+ return this.selectedTheme.type ===
+ customizeThemes.mojom.ThemeType.kThirdParty;
+ }
+
+ /**
+ * @param {skia.mojom.SkColor} skColor
+ * @return {string}
+ * @private
+ */
+ skColorToRgba_(skColor) {
+ return skColorToRgba(skColor);
+ }
+}
+
+customElements.define(CustomizeThemesElement.is, CustomizeThemesElement);
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom
new file mode 100644
index 00000000000..4d800e7b822
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/customize_themes.mojom
@@ -0,0 +1,83 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module customize_themes.mojom;
+
+import "skia/public/mojom/skcolor.mojom";
+
+struct ThemeColors {
+ // The theme's frame color.
+ skia.mojom.SkColor frame;
+ // The theme's active tab color.
+ skia.mojom.SkColor active_tab;
+ // The theme's text color that has enough contrast with `active_tab`.
+ skia.mojom.SkColor active_tab_text;
+};
+
+struct ChromeTheme {
+ // Theme identifier.
+ int32 id;
+ // Localized string of the theme name.
+ string label;
+ ThemeColors colors;
+};
+
+// Additional info for third-party themes.
+struct ThirdPartyThemeInfo {
+ // ID in the Chrome Web Store.
+ string id;
+ // Human-readable theme name.
+ string name;
+};
+
+enum ThemeType {
+ kDefault,
+ kAutogenerated,
+ kChrome,
+ kThirdParty
+};
+
+union ThemeInfo {
+ // Set if the theme type is `kChrome`.
+ int32 chrome_theme_id;
+ // Set if the theme type is `kAutogenerated`.
+ ThemeColors autogenerated_theme_colors;
+ // Set if the theme type is `kThirdParty`.
+ ThirdPartyThemeInfo third_party_theme_info;
+};
+
+struct Theme {
+ // The theme's type (e.g. default or third-party).
+ ThemeType type;
+ // Additional info about the theme depending on the type.
+ ThemeInfo info;
+};
+
+// Used by the component to bootstrap bidirectional communication.
+interface CustomizeThemesHandlerFactory {
+ // The component calls this method when it is first initialized.
+ CreateCustomizeThemesHandler(
+ pending_remote<CustomizeThemesClient> client,
+ pending_receiver<CustomizeThemesHandler> handler);
+};
+
+interface CustomizeThemesHandler {
+ // Applies the autogenerated theme for the given color.
+ ApplyAutogeneratedTheme(skia.mojom.SkColor frame_color);
+ // Applies the predefined Chrome theme with the given ID.
+ ApplyChromeTheme(int32 id);
+ // Applies the default theme.
+ ApplyDefaultTheme();
+ // Returns the pre-defined Chrome themes.
+ GetChromeThemes() => (array<ChromeTheme> chromeThemes);
+ // Confirms changes made to the theme.
+ ConfirmThemeChanges();
+ // Reverts changes made to the theme.
+ RevertThemeChanges();
+};
+
+interface CustomizeThemesClient {
+ // Sets the current theme.
+ SetTheme(Theme theme);
+};
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.html b/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.html
new file mode 100644
index 00000000000..7a56940625d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.html
@@ -0,0 +1,77 @@
+<style>
+ :host {
+ --cr-theme-icon-size: 72px;
+ }
+
+ :host,
+ svg {
+ height: var(--cr-theme-icon-size);
+ width: var(--cr-theme-icon-size);
+ }
+
+ #ring {
+ fill: rgba(var(--google-blue-600-rgb), 0.4);
+ visibility: hidden;
+ }
+
+ #checkMark {
+ visibility: hidden;
+ }
+
+ :host([selected]) #ring,
+ :host([selected]) #checkMark {
+ visibility: visible;
+ }
+
+ #circle {
+ fill: url(#gradient);
+ stroke: var(--cr-theme-icon-stroke-color,
+ var(--cr-theme-icon-frame-color));
+ stroke-width: 1;
+ }
+
+ #leftColor {
+ stop-color: var(--cr-theme-icon-active-tab-color);
+ }
+
+ #rightColor {
+ stop-color: var(--cr-theme-icon-frame-color);
+ }
+
+ #checkMark circle {
+ fill: var(--google-blue-600);
+ }
+
+ #checkMark path {
+ fill: white;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ #checkMark circle {
+ fill: var(--google-blue-refresh-300);
+ }
+
+ #checkMark path {
+ fill: var(--google-grey-900);
+ }
+ }
+</style>
+<svg viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="gradient" gradientUnits="objectBoundingBox"
+ x1="50%" y1="0" x2="50.01%" y2="0">
+ <stop id="leftColor" offset="0%"></stop>
+ <stop id="rightColor" offset="100%"></stop>
+ </linearGradient>
+ </defs>
+ <circle id="ring" cx="36" cy="36" r="36"></circle>
+ <circle id="circle" cx="36" cy="36" r="32"></circle>
+ <g id="checkMark" transform="translate(48.5, 3.5)">
+ <circle cx="10" cy="10" r="10"></circle>
+ <path d="m 2.9885708,9.99721 5.0109458,4.98792 0.00275,-0.003
+ 0.024151,0.0227 8.9741604,-9.01557 -1.431323,-1.42476 -7.5742214,7.6092
+ -3.6031671,-3.58665 z">
+ </path>
+ </g>
+</svg>
diff --git a/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.js b/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.js
new file mode 100644
index 00000000000..dd934076c54
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/customize_themes/theme_icon.js
@@ -0,0 +1,25 @@
+// 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 '../../cr_elements/shared_vars_css.m.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * Represents a theme. Displayed as a circle with each half colored based on
+ * the custom CSS properties |--cr-theme-icon-frame-color| and
+ * |--cr-theme-icon-active-tab-color|. Can be selected by setting the
+ * |selected| attribute.
+ */
+export class ThemeIconElement extends PolymerElement {
+ static get is() {
+ return 'cr-theme-icon';
+ }
+
+ static get template() {
+ return html`{__html_template__}`;
+ }
+}
+
+customElements.define(ThemeIconElement.is, ThemeIconElement);
diff --git a/chromium/ui/webui/resources/cr_elements/BUILD.gn b/chromium/ui/webui/resources/cr_elements/BUILD.gn
index b57828b74c0..a32aa0ea8f6 100644
--- a/chromium/ui/webui/resources/cr_elements/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/BUILD.gn
@@ -16,6 +16,7 @@ group("closure_compile") {
"cr_drawer:closure_compile",
"cr_expand_button:closure_compile",
"cr_fingerprint:closure_compile",
+ "cr_grid:closure_compile",
"cr_icon_button:closure_compile",
"cr_input:closure_compile",
"cr_link_row:closure_compile",
@@ -169,6 +170,7 @@ group("polymer3_elements") {
"cr_drawer:cr_drawer_module",
"cr_expand_button:cr_expand_button_module",
"cr_fingerprint:polymer3_elements",
+ "cr_grid:web_components",
"cr_icon_button:cr_icon_button_module",
"cr_input:polymer3_elements",
"cr_lazy_render:cr_lazy_render_module",
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/BUILD.gn b/chromium/ui/webui/resources/cr_elements/chromeos/BUILD.gn
index 33cc937da69..da86dbedc1d 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/BUILD.gn
@@ -19,7 +19,7 @@ group("closure_compile") {
group("polymer3_elements") {
public_deps = [
":cros_color_overrides_module",
- "cr_picture:modulize",
+ "cr_picture:polymer3_elements",
]
}
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn
index be7345ede35..ea1ba540ca8 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn
@@ -3,7 +3,9 @@
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
import("//ui/webui/resources/tools/js_modulizer.gni")
+import("../os_cr_elements.gni")
js_type_check("closure_compile") {
deps = [
@@ -44,12 +46,20 @@ js_library("png") {
}
js_modulizer("modulize") {
- input_files = [ "png.js" ]
+ input_files = [
+ "png.js",
+ "cr_picture_types.js",
+ ]
}
js_type_check("closure_compile_module") {
- uses_js_modules = true
- deps = [ ":png.m" ]
+ is_polymer3 = true
+ deps = [
+ ":cr_camera.m",
+ ":cr_picture_list.m",
+ ":cr_picture_pane.m",
+ ":png.m",
+ ]
}
js_library("png.m") {
@@ -63,3 +73,75 @@ js_library("png.m") {
]
extra_deps = [ ":modulize" ]
}
+
+js_library("cr_picture_types.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.m.js" ]
+ extra_deps = [ ":modulize" ]
+}
+
+group("polymer3_elements") {
+ public_deps = [
+ ":cr_camera_module",
+ ":cr_picture_list_module",
+ ":cr_picture_pane_module",
+ ":icons_module",
+ ":modulize",
+ ]
+}
+
+js_library("cr_picture_pane.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.m.js" ]
+ deps = [
+ ":cr_camera.m",
+ ":cr_picture_types.m",
+ ":png.m",
+ ]
+ extra_deps = [ ":cr_picture_pane_module" ]
+}
+
+js_library("cr_camera.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.m.js" ]
+ deps = [ ":png.m" ]
+ extra_deps = [ ":cr_camera_module" ]
+}
+
+js_library("cr_picture_list.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.m.js" ]
+ deps = [
+ ":cr_picture_types.m",
+ ":png.m",
+ "//third_party/polymer/v3_0/components-chromium/iron-selector:iron-selector",
+ "//ui/webui/resources/js:assert.m",
+ ]
+ extra_deps = [ ":cr_picture_list_module" ]
+}
+
+polymer_modulizer("cr_camera") {
+ js_file = "cr_camera.js"
+ html_file = "cr_camera.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_elements_chromeos_namespace_rewrites
+ auto_imports = cr_elements_chromeos_auto_imports
+}
+
+polymer_modulizer("cr_picture_list") {
+ js_file = "cr_picture_list.js"
+ html_file = "cr_picture_list.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_elements_chromeos_namespace_rewrites
+ auto_imports = cr_elements_chromeos_auto_imports
+}
+
+polymer_modulizer("cr_picture_pane") {
+ js_file = "cr_picture_pane.js"
+ html_file = "cr_picture_pane.html"
+ html_type = "dom-module"
+ namespace_rewrites = cr_elements_chromeos_namespace_rewrites
+ auto_imports = cr_elements_chromeos_auto_imports
+}
+
+polymer_modulizer("icons") {
+ js_file = "icons.m.js"
+ html_file = "icons.html"
+ html_type = "iron-iconset"
+}
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
index 11be58740ed..1b14deab74d 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
@@ -129,8 +129,11 @@ Polymer({
const interval = setInterval(() => {
/** Stop capturing frames when all allocated frames have been consumed. */
if (frames.length) {
- capturedFrames.push(
- this.captureFrame_(this.$.cameraVideo, frames.pop()));
+ capturedFrames.push(this.captureFrame_(
+ /**
+ * @type {!HTMLVideoElement}
+ */
+ (this.$.cameraVideo), frames.pop()));
} else {
clearInterval(interval);
this.fire(
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
index 3ce3dd7e099..6630fd546e0 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
@@ -120,7 +120,11 @@ Polymer({
if (!selected) {
return;
}
- this.setSelectedImage_(this.$.profileImage);
+ this.setSelectedImage_(
+ /**
+ * @type {!CrPicture.ImageElement}
+ */
+ (this.$.profileImage));
},
/**
@@ -131,7 +135,11 @@ Polymer({
return image.dataset.url === imageUrl;
});
if (image) {
- this.setSelectedImage_(image);
+ this.setSelectedImage_(
+ /**
+ * @type {!CrPicture.ImageElement}
+ */
+ (image));
this.selectedImageUrl_ = '';
} else {
this.selectedImageUrl_ = imageUrl;
@@ -159,9 +167,17 @@ Polymer({
} else if (
this.fallbackImage_ &&
this.fallbackImage_.dataset.type !== CrPicture.SelectionTypes.OLD) {
- this.selectImage_(this.fallbackImage_, true /* activate */);
+ this.selectImage_(
+ /**
+ * @type {!CrPicture.ImageElement}
+ */
+ (this.fallbackImage_), true /* activate */);
} else {
- this.selectImage_(this.$.profileImage, true /* activate */);
+ this.selectImage_(
+ /**
+ * @type {!CrPicture.ImageElement}
+ */
+ (this.$.profileImage), true /* activate */);
}
},
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.js b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.js
index 8afb666d23e..cf6c7f3f960 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.js
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-const CrPicture = {};
+/* #export */ const CrPicture = {};
/**
* Contains the possible types for picture list image elements.
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni b/chromium/ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni
new file mode 100644
index 00000000000..ad32b62a9c3
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni
@@ -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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+cr_elements_chromeos_namespace_rewrites = [
+ "cr.png.convertImageSequenceToPng|convertImageSequenceToPng",
+ "cr.png.isEncodedPngDataUrlAnimated|isEncodedPngDataUrlAnimated",
+]
+
+cr_elements_chromeos_auto_imports = [
+ "ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.html|CrPicture",
+ "ui/webui/resources/cr_elements/chromeos/cr_picture/png.html|convertImageSequenceToPng,isEncodedPngDataUrlAnimated",
+]
diff --git a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index cb1dc2e0360..302cff77fdc 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -11,7 +11,7 @@
:host dialog {
--drawer-width: 256px;
--transition-timing: 200ms ease;
- background-color: #fff;
+ background-color: var(--cr-drawer-background-color, #fff);
border: none;
bottom: 0;
left: calc(-1 * var(--drawer-width));
@@ -26,7 +26,7 @@
@media (prefers-color-scheme: dark) {
:host dialog {
- background: var(--google-grey-900)
+ background: var(--cr-drawer-background-color, var(--google-grey-900))
linear-gradient(rgba(255, 255, 255, .04), rgba(255, 255, 255, .04));
}
}
diff --git a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/BUILD.gn
index 7f9b6638be6..bf19ba93144 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/BUILD.gn
@@ -10,6 +10,7 @@ js_type_check("closure_compile") {
}
js_library("cr_fingerprint_progress_arc") {
+ deps = [ "../cr_lottie:cr_lottie" ]
}
group("polymer3_elements") {
@@ -39,6 +40,7 @@ js_type_check("closure_compile_module") {
js_library("cr_fingerprint_progress_arc.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.m.js" ]
deps = [
+ "../cr_lottie:cr_lottie.m",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
extra_deps = [ ":cr_fingerprint_progress_arc_module" ]
diff --git a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/OWNERS b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/OWNERS
new file mode 100644
index 00000000000..16332070ea9
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/OWNERS
@@ -0,0 +1 @@
+nsatragno@chromium.org
diff --git a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html
index d76130c087c..0373979a468 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html
@@ -1,5 +1,6 @@
<link rel="import" href="../../html/polymer.html">
<link rel="import" href="cr_fingerprint_icon.html">
+<link rel="import" href="../cr_lottie/cr_lottie.html">
<dom-module id="cr-fingerprint-progress-arc">
<template>
@@ -8,51 +9,34 @@
user-select: none;
}
- #canvasDiv {
- height: 240px;
- width: 460px;
- }
-
- /* Put the image in a separate div with 0 height, otherwise the div will
- take the height of the image, leaving us with a row of whitespace when
- we position the #image to be inside #arc. */
- #imageDiv {
- height: 0;
- }
-
- #scanningAnimation {
+ .translucent {
opacity: 0.3;
- position: relative;
}
- #enrollmentDone {
+ #canvasDiv {
+ height: 240px;
+ overflow: hidden;
position: relative;
+ width: 460px;
}
- #checkmarkDiv {
- height: 0;
+ cr-lottie {
+ display: inline-block;
+ position: absolute;
}
- #checkmarkAnimation {
- position: relative;
+ #enrollmentDone {
+ position: absolute;
}
</style>
<div id="canvasDiv">
<canvas id="canvas" height="240" width="460"></canvas>
- </div>
- <div id="imageDiv">
- <img id="scanningAnimation"
- src="chrome://theme/IDR_FINGERPRINT_ICON_ANIMATION"
- aria-hidden="true">
+ <cr-lottie id="scanningAnimation" aria-hidden="true" autoplay>
+ </cr-lottie>
<iron-icon id="enrollmentDone" icon="cr-fingerprint-icon:enrollment-done" hidden>
</iron-icon>
</div>
- <div id="checkmarkDiv" hidden>
- <img id="checkmarkAnimation"
- src="chrome://theme/IDR_FINGERPRINT_COMPLETE_TICK"
- aria-hidden="true">
- </div>
</template>
<script src="cr_fingerprint_progress_arc.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js
index 07a78baa633..1350b9dc6fe 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.js
@@ -2,6 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// #import {CrLottieElement} from '../cr_lottie/cr_lottie.m.js';
+
+/** @type {string} */
+/* #export */ const FINGEPRINT_TICK_DARK_URL =
+ 'chrome://theme/IDR_FINGERPRINT_COMPLETE_TICK_DARK';
+
+/** @type {string} */
+/* #export */ const FINGEPRINT_TICK_LIGHT_URL =
+ 'chrome://theme/IDR_FINGERPRINT_COMPLETE_TICK';
+
(function() {
/**
@@ -127,10 +137,36 @@ Polymer({
*/
updateTimerId_: undefined,
+ /**
+ * Media query for dark mode.
+ * @type {MediaQueryList|undefined}
+ * @private
+ */
+ darkModeQuery_: undefined,
+
+ /**
+ * Dark mode change listener callback.
+ * @type {function(MediaQueryList)|undefined}
+ * @private
+ */
+ darkModeListener_: undefined,
+
/** @override */
attached() {
this.scale_ = this.circleRadius / DEFAULT_CANVAS_CIRCLE_RADIUS;
this.updateImages_();
+
+ this.darkModeListener_ = this.updateAnimationAsset_.bind(this);
+ this.darkModeQuery_ = window.matchMedia('(prefers-color-scheme: dark)');
+ this.darkModeQuery_.addListener(this.darkModeListener_);
+ this.updateAnimationAsset_();
+ },
+
+ /** @override */
+ detached() {
+ this.darkModeQuery_.removeListener(
+ /** @type {function(MediaQueryList)} */ (this.darkModeListener_));
+ this.darkModeListener_ = undefined;
},
/**
@@ -227,6 +263,24 @@ Polymer({
}
},
+ /**
+ * Updates the lottie animation taking into account the current state and
+ * whether dark mode is enabled.
+ * @private
+ */
+ updateAnimationAsset_() {
+ const scanningAnimation =
+ /** @type {CrLottieElement} */ (this.$.scanningAnimation);
+ if (this.isComplete_) {
+ scanningAnimation.animationUrl = this.darkModeQuery_.matches ?
+ FINGEPRINT_TICK_DARK_URL :
+ FINGEPRINT_TICK_LIGHT_URL;
+ return;
+ }
+ scanningAnimation.animationUrl =
+ 'chrome://theme/IDR_FINGERPRINT_ICON_ANIMATION';
+ },
+
/*
* Cleans up any pending animation update created by setInterval().
* @private
@@ -247,10 +301,14 @@ Polymer({
* @private
*/
animateScanComplete_() {
- this.$.checkmarkDiv.hidden = false;
+ const scanningAnimation =
+ /** @type {CrLottieElement|HTMLElement} */ (this.$.scanningAnimation);
+ scanningAnimation.singleLoop = true;
+ scanningAnimation.classList.remove('translucent');
+ this.updateAnimationAsset_();
+ this.resizeCheckMark_(scanningAnimation);
+
this.$.enrollmentDone.hidden = false;
- this.$.scanningAnimation.hidden = true;
- this.$.enrollmentDone.style.opacity = 1;
},
/**
@@ -259,12 +317,8 @@ Polymer({
*/
animateScanProgress_() {
this.$.enrollmentDone.hidden = false;
- this.$.enrollmentDone.style.opacity = 0.3;
- this.$.scanningAnimation.hidden = true;
this.updateTimerId_ = window.setTimeout(() => {
this.$.enrollmentDone.hidden = true;
- this.$.enrollmentDone.style.opacity = 1;
- this.$.scanningAnimation.hidden = false;
}, FINGERPRINT_SCAN_SUCCESS_MS);
},
@@ -287,8 +341,14 @@ Polymer({
this.isComplete_ = false;
this.drawBackgroundCircle();
this.$.enrollmentDone.hidden = true;
- this.$.scanningAnimation.hidden = false;
- this.$.checkmarkDiv.hidden = true;
+
+ const scanningAnimation =
+ /** @type {CrLottieElement|HTMLElement} */ (this.$.scanningAnimation);
+ scanningAnimation.singleLoop = false;
+ scanningAnimation.classList.add('translucent');
+ this.updateAnimationAsset_();
+ this.resizeAndCenterIcon_(scanningAnimation);
+ scanningAnimation.hidden = false;
},
/**
@@ -300,8 +360,6 @@ Polymer({
/** @type {!HTMLElement} */ (this.$.scanningAnimation));
this.resizeAndCenterIcon_(
/** @type {!HTMLElement} */ (this.$.enrollmentDone));
- this.resizeCheckMark_(
- /** @type {!HTMLElement} */ (this.$.checkmarkAnimation));
},
/**
@@ -317,7 +375,7 @@ Polymer({
// Place in the center of the canvas.
const left = this.$.canvas.width / 2 - ICON_WIDTH * this.scale_ / 2;
- const top = 0 - ICON_HEIGHT * this.scale_ / 2 - this.$.canvas.height / 2;
+ const top = this.$.canvas.height / 2 - ICON_HEIGHT * this.scale_ / 2;
target.style.left = left + 'px';
target.style.top = top + 'px';
},
@@ -334,9 +392,8 @@ Polymer({
target.style.height = CHECK_MARK_SIZE * this.scale_ + 'px';
// Place it in the left bottom corner of fingerprint progress circle.
- const top = 0 -
- (CHECK_MARK_SIZE * this.scale_ + this.$.canvas.height / 2 -
- this.circleRadius);
+ const top = this.$.canvas.height / 2 + this.circleRadius -
+ CHECK_MARK_SIZE * this.scale_;
const left = this.$.canvas.width / 2 + this.circleRadius -
CHECK_MARK_SIZE * this.scale_;
target.style.left = left + 'px';
diff --git a/chromium/ui/webui/resources/cr_elements/cr_grid/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_grid/BUILD.gn
new file mode 100644
index 00000000000..161ddd82153
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_grid/BUILD.gn
@@ -0,0 +1,21 @@
+# 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("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+ js_files = [ "cr_grid.js" ]
+}
+
+js_type_check("closure_compile") {
+ is_polymer3 = true
+ deps = [ ":cr_grid" ]
+}
+
+js_library("cr_grid") {
+ deps = [
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+}
diff --git a/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.html b/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.html
new file mode 100644
index 00000000000..65e0208e395
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.html
@@ -0,0 +1,21 @@
+<style>
+ :host {
+ --cr-grid-gap: 0px;
+ }
+
+ #grid {
+ display: grid;
+ grid-column-gap: var(--cr-grid-gap);
+ grid-row-gap: var(--cr-grid-gap);
+ grid-template-columns: repeat(var(--cr-grid-columns), auto);
+ width: fit-content;
+ }
+
+ ::slotted(*) {
+ align-self: center;
+ justify-self: center;
+ }
+</style>
+<div id="grid" on-keydown="onKeyDown_">
+ <slot id="items"></slot>
+</div>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.js b/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.js
new file mode 100644
index 00000000000..03203ca71f7
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_grid/cr_grid.js
@@ -0,0 +1,88 @@
+// 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 {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+// Displays children in a two-dimensional grid and supports focusing children
+// with arrow keys.
+export class CrGridElement extends PolymerElement {
+ static get is() {
+ return 'cr-grid';
+ }
+
+ static get template() {
+ return html`{__html_template__}`;
+ }
+
+ static get properties() {
+ return {
+ /** @type {number} */
+ columns: {
+ type: Number,
+ value: 1,
+ observer: 'onColumnsChange_',
+ },
+ };
+ }
+
+ /** @private */
+ onColumnsChange_() {
+ this.updateStyles({'--cr-grid-columns': this.columns});
+ }
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onKeyDown_(e) {
+ if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
+ e.preventDefault();
+ const items = this.$.items.assignedElements().filter(
+ (el) =>
+ (!!(el.offsetWidth || el.offsetHeight ||
+ el.getClientRects().length)));
+ const currentIndex = items.indexOf(e.target);
+ const isRtl = window.getComputedStyle(this)['direction'] === 'rtl';
+ const bottomRowColumns = items.length % this.columns;
+ const direction = ['ArrowRight', 'ArrowDown'].includes(e.key) ? 1 : -1;
+ const inEdgeRow = direction === 1 ?
+ currentIndex >= items.length - bottomRowColumns :
+ currentIndex < this.columns;
+ let delta = 0;
+ switch (e.key) {
+ case 'ArrowLeft':
+ case 'ArrowRight':
+ delta = direction * (isRtl ? -1 : 1);
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ delta = direction * (inEdgeRow ? bottomRowColumns : this.columns);
+ break;
+ }
+ // Handle cases where we move to an empty space in a non-full bottom row
+ // and have to jump to the next row.
+ if (e.key === 'ArrowUp' && inEdgeRow &&
+ currentIndex >= bottomRowColumns) {
+ delta -= this.columns;
+ } else if (
+ e.key === 'ArrowDown' && !inEdgeRow &&
+ currentIndex + delta >= items.length) {
+ delta += bottomRowColumns;
+ }
+ const mod = function(m, n) {
+ return ((m % n) + n) % n;
+ };
+ const newIndex = mod(currentIndex + delta, items.length);
+ items[newIndex].focus();
+ }
+
+ if (['Enter', ' '].includes(e.key)) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.target.click();
+ }
+ }
+}
+
+customElements.define(CrGridElement.is, CrGridElement);
diff --git a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html
index 58ec0a3faff..20f893f5237 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html
@@ -107,7 +107,7 @@
}
#input[type='search']::-webkit-search-cancel-button {
- -webkit-appearance: none;
+ display: none;
}
:host-context([dir=rtl]) #input[type=url] {
diff --git a/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.html b/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.html
index b6510ba45bd..59ef33450b8 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.html
@@ -9,7 +9,7 @@
width: 100%;
}
</style>
- <canvas id="canvas"></canvas>
+ <canvas id="canvas" hidden="[[hidden]]"></canvas>
</template>
<script src="cr_lottie.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js b/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
index 7100161c006..58f4216022c 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
@@ -10,6 +10,7 @@
* initialized.
* Fires a 'cr-lottie-playing' event when the animation starts playing.
* Fires a 'cr-lottie-paused' event when the animation has paused.
+ * Fires a 'cr-lottie-stopped' event when animation has stopped.
* Fires a 'cr-lottie-resized' event when the canvas the animation is being
* drawn on is resized.
*/
@@ -18,7 +19,8 @@
* The resource url for the lottier web worker script.
* @const {string}
*/
-const LOTTIE_JS_URL = 'chrome://resources/lottie/lottie_worker.min.js';
+/* #export */ const LOTTIE_JS_URL =
+ 'chrome://resources/lottie/lottie_worker.min.js';
Polymer({
is: 'cr-lottie',
@@ -27,28 +29,49 @@ Polymer({
animationUrl: {
type: String,
value: '',
+ observer: 'animationUrlChanged_',
},
+
autoplay: {
type: Boolean,
value: false,
},
+
+ hidden: {
+ type: Boolean,
+ value: false,
+ },
+
+ singleLoop: {
+ type: Boolean,
+ value: false,
+ },
},
/** @private {?HTMLCanvasElement} */
canvasElement_: null,
- /** @private {boolean} True if the animation has loaded successfully */
+ /** @private {boolean} Whether the animation has loaded successfully */
isAnimationLoaded_: false,
/** @private {?OffscreenCanvas} */
offscreenCanvas_: null,
+ /**
+ * @private {boolean} Whether the canvas has been transferred to the worker
+ * thread.
+ */
+ hasTransferredCanvas_: false,
+
/** @private {?ResizeObserver} */
resizeObserver_: null,
/** @private {?Worker} */
worker_: null,
+ /** @private {?XMLHttpRequest} The current in-flight request. */
+ xhr_: null,
+
/** @override */
attached() {
// CORS blocks loading worker script from a different origin but
@@ -72,6 +95,10 @@ Polymer({
this.worker_.terminate();
this.worker_ = null;
}
+ if (this.xhr_) {
+ this.xhr_.abort();
+ this.xhr_ = null;
+ }
},
/**
@@ -110,6 +137,32 @@ Polymer({
},
/**
+ * Updates the animation that is being displayed.
+ * @param {string} animationUrl the new animation URL.
+ * @param {string} oldAnimationUrl the previous animation URL.
+ * @private
+ */
+ animationUrlChanged_(animationUrl, oldAnimationUrl) {
+ if (!this.worker_) {
+ // The worker hasn't loaded yet. We will load the new animation once the
+ // worker loads.
+ return;
+ }
+ if (this.xhr_) {
+ // There is an in-flight request to load the previous animation. Abort it
+ // before loading a new image.
+ this.xhr_.abort();
+ this.xhr_ = null;
+ }
+ if (this.isAnimationLoaded_) {
+ this.worker_.postMessage({control: {stop: true}});
+ this.isAnimationLoaded_ = false;
+ }
+ this.sendXmlHttpRequest_(
+ this.animationUrl, 'json', this.initAnimation_.bind(this));
+ },
+
+ /**
* Computes the draw buffer size for the canvas. This ensures that the
* rasterization is crisp and sharp rather than blurry.
* @return {Object} Size of the canvas draw buffer
@@ -152,13 +205,19 @@ Polymer({
*/
sendXmlHttpRequest_(url, responseType, successCallback) {
assert(this.isValidUrl_(url), 'Invalid scheme or data url used.');
- const xhr = new XMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = responseType;
- xhr.send();
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4 && xhr.status === 200) {
- successCallback(xhr.response);
+ assert(!this.xhr_);
+
+ this.xhr_ = new XMLHttpRequest();
+ this.xhr_.open('GET', url, true);
+ this.xhr_.responseType = responseType;
+ this.xhr_.send();
+ this.xhr_.onreadystatechange = () => {
+ if (this.xhr_.readyState === 4 && this.xhr_.status === 200) {
+ // |successCallback| might trigger another xhr, so we set to null before
+ // calling it.
+ const response = this.xhr_.response;
+ this.xhr_ = null;
+ successCallback(response);
}
};
},
@@ -181,14 +240,17 @@ Polymer({
* @private
*/
initAnimation_(animationData) {
- this.worker_.postMessage(
- {
- canvas: this.offscreenCanvas_,
- animationData: animationData,
- drawSize: this.getCanvasDrawBufferSize_(),
- params: {loop: true, autoplay: this.autoplay}
- },
- [this.offscreenCanvas_]);
+ const message = [{
+ animationData,
+ drawSize: this.getCanvasDrawBufferSize_(),
+ params: {loop: !this.singleLoop, autoplay: this.autoplay}
+ }];
+ if (!this.hasTransferredCanvas_) {
+ message[0].canvas = this.offscreenCanvas_;
+ message.push([this.offscreenCanvas_]);
+ this.hasTransferredCanvas_ = true;
+ }
+ this.worker_.postMessage(...message);
},
/**
@@ -204,6 +266,8 @@ Polymer({
this.fire('cr-lottie-playing');
} else if (event.data.name === 'paused') {
this.fire('cr-lottie-paused');
+ } else if (event.data.name === 'stopped') {
+ this.fire('cr-lottie-stopped');
} else if (event.data.name === 'resized') {
this.fire('cr-lottie-resized', event.data.size);
}
diff --git a/chromium/ui/webui/resources/cr_elements/cr_radio_button/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_radio_button/BUILD.gn
index 26a228344a2..c1855e35727 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_radio_button/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/cr_radio_button/BUILD.gn
@@ -7,13 +7,23 @@ import("//tools/polymer/polymer.gni")
import("../../tools/js_modulizer.gni")
js_type_check("closure_compile") {
- deps = [ ":cr_radio_button" ]
+ deps = [
+ ":cr_card_radio_button",
+ ":cr_radio_button",
+ ]
}
js_library("cr_radio_button") {
deps = [ ":cr_radio_button_behavior" ]
}
+js_library("cr_card_radio_button") {
+ deps = [
+ ":cr_radio_button_behavior",
+ "//third_party/polymer/v1_0/components-chromium/iron-icon:iron-icon-extracted",
+ ]
+}
+
js_library("cr_radio_button_behavior") {
deps = [ "//third_party/polymer/v1_0/components-chromium/paper-behaviors:paper-ripple-behavior-extracted" ]
}
@@ -22,6 +32,7 @@ js_library("cr_radio_button_behavior") {
group("polymer3_elements") {
public_deps = [
+ ":cr_card_radio_button_module",
":cr_radio_button_module",
":cr_radio_button_style_css_module",
":modulize",
@@ -41,6 +52,13 @@ polymer_modulizer("cr_radio_button") {
auto_imports = [ "ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.html|CrRadioButtonBehavior" ]
}
+polymer_modulizer("cr_card_radio_button") {
+ js_file = "cr_card_radio_button.js"
+ html_file = "cr_card_radio_button.html"
+ html_type = "dom-module"
+ auto_imports = [ "ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.html|CrRadioButtonBehavior" ]
+}
+
polymer_modulizer("cr_radio_button_style_css") {
js_file = "cr_radio_button_style_css.m.js"
html_file = "cr_radio_button_style_css.html"
@@ -50,6 +68,7 @@ polymer_modulizer("cr_radio_button_style_css") {
js_type_check("closure_compile_module") {
is_polymer3 = true
deps = [
+ ":cr_card_radio_button.m",
":cr_radio_button.m",
":cr_radio_button_behavior.m",
]
@@ -64,6 +83,16 @@ js_library("cr_radio_button.m") {
extra_deps = [ ":cr_radio_button_module" ]
}
+js_library("cr_card_radio_button.m") {
+ sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.m.js" ]
+ deps = [
+ ":cr_radio_button_behavior.m",
+ "//third_party/polymer/v3_0/components-chromium/iron-icon",
+ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+ ]
+ extra_deps = [ ":cr_card_radio_button_module" ]
+}
+
js_library("cr_radio_button_behavior.m") {
sources = [ "$root_gen_dir/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.m.js" ]
deps = [ "//third_party/polymer/v3_0/components-chromium/paper-behaviors:paper-ripple-behavior" ]
diff --git a/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html b/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html
new file mode 100644
index 00000000000..8f5ff9bb3c8
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html
@@ -0,0 +1,68 @@
+<link rel="import" href="../../html/polymer.html">
+
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="cr_radio_button_behavior.html">
+<link rel="import" href="cr_radio_button_style_css.html">
+<link rel="import" href="../icons.html">
+<link rel="import" href="../shared_vars_css.html">
+
+<dom-module id="cr-card-radio-button">
+ <template>
+ <style include="cr-radio-button-style">
+ :host {
+ background-color: var(--cr-card-background-color);
+ border: solid 2px;
+ border-radius: 8px;
+ box-shadow: var(--cr-elevation-1);
+ margin: var(--cr-card-radio-button-margin, 8px);
+ width: var(--cr-card-radio-button-width, 200px);
+ }
+
+ :host([checked]) {
+ border-color: var(--cr-checked-color);
+ }
+
+ :host(:not([checked])) {
+ border-color: var(--cr-card-background-color);
+ }
+
+ #button {
+ height: var(--cr-card-radio-button-height, auto);
+ padding: var(--cr-card-radio-button-padding, 24px);
+ position: relative;
+ width: 100%;
+ }
+
+ #checkMark {
+ position: absolute;
+ right: var(--cr-button-edge-spacing);
+ top: var(--cr-button-edge-spacing);
+ }
+
+ :host(:not([checked])) #checkMark {
+ display: none;
+ }
+
+ #slottedContent {
+ padding: var(--cr-card-radio-button-slotted-content-padding);
+ }
+
+ #checkMark {
+ fill: var(--cr-checked-color);
+ }
+ </style>
+ <div id="button" role="radio"
+ aria-checked$="[[getAriaChecked_(checked)]]"
+ aria-describedby="slotted-content"
+ aria-disabled$="[[getAriaDisabled_(disabled)]]"
+ tabindex$="[[buttonTabIndex_]]"
+ aria-labelledby="slotted-content"
+ on-keydown="onInputKeydown_">
+ <iron-icon id="checkMark" icon="cr:check-circle"></iron-icon>
+ <span id="slottedContent">
+ <slot></slot>
+ </span>
+ </div>
+ </template>
+ <script src="cr_card_radio_button.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js b/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js
new file mode 100644
index 00000000000..2de0052679c
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js
@@ -0,0 +1,16 @@
+// 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.
+
+/**
+ * @fileoverview
+ * 'cr-card-radio-button' is a radio button in the style of a card. A checkmark
+ * is displayed in the upper right hand corner if the radio button is selected.
+ */
+Polymer({
+ is: 'cr-card-radio-button',
+
+ behaviors: [
+ CrRadioButtonBehavior,
+ ],
+});
diff --git a/chromium/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js b/chromium/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
index 2d5e83c0279..115f054e170 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
@@ -32,7 +32,7 @@
selectableElements: {
type: String,
- value: 'cr-radio-button, controlled-radio-button',
+ value: 'cr-radio-button, cr-card-radio-button, controlled-radio-button',
},
/**
diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
index 86adfdd2267..99ec7838878 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
@@ -137,3 +137,37 @@
return true;
},
};
+
+/** @interface */
+/* #export */ class CrSearchFieldBehaviorInterface {
+ constructor() {
+ /** @type {string} */
+ this.label;
+
+ /** @type {string} */
+ this.clearLabel;
+
+ /** @type {boolean} */
+ this.hasSearchText;
+ }
+
+ /**
+ * @return {!HTMLInputElement} The input field element the behavior should
+ * use.
+ */
+ getSearchInput() {}
+
+ /** @return {string} The value of the search field. */
+ getValue() {}
+
+ /**
+ * @param {string} value
+ * @param {boolean=} opt_noEvent Whether to prevent a 'search-changed' event
+ * firing for this change.
+ */
+ setValue(value, opt_noEvent) {}
+
+ onSearchTermSearch() {}
+
+ onSearchTermInput() {}
+}
diff --git a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
index 80c589c23bb..06823f7b8f6 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
@@ -265,7 +265,7 @@ Polymer({
if (this.readonly) {
return;
}
- if (event.code === 'Enter') {
+ if (event.key === 'Enter') {
this.openDropdown_();
// Stop the default submit action.
event.preventDefault();
@@ -274,7 +274,7 @@ Polymer({
}
event.stopPropagation();
- switch (event.code) {
+ switch (event.key) {
case 'Tab':
// Pressing tab will cause the input field to lose focus. Since the
// dropdown visibility is tied to focus, close the dropdown.
@@ -287,7 +287,7 @@ Polymer({
if (items.length === 0) {
break;
}
- this.updateSelected_(items, selected, event.code === 'ArrowDown');
+ this.updateSelected_(items, selected, event.key === 'ArrowDown');
break;
}
case 'Enter': {
diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index 82e0d5f1481..da7259f3012 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -180,7 +180,7 @@
label="[[searchPrompt]]" clear-label="[[clearLabel]]"
spinner-active="[[spinnerActive]]"
showing-search="{{showingSearch_}}"
- autofocus>
+ autofocus$="[[autofocus]]">
</cr-toolbar-search-field>
<iron-media-query query="(max-width: [[narrowThreshold]]px)"
query-matches="{{narrow}}">
diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.js b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.js
index 70254a876c2..23640a9a3b3 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.js
@@ -38,6 +38,13 @@ Polymer({
// Controls whether the search field is shown.
showSearch: {type: Boolean, value: true},
+ // Controls whether the search field is autofocused.
+ autofocus: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
+
// True when the toolbar is displaying in narrow mode.
narrow: {
type: Boolean,
diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index 2854d6a9d85..86e320ea2a3 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -118,8 +118,7 @@
}
input[type='search']::-webkit-search-cancel-button {
- -webkit-appearance: none;
- margin: 0;
+ display: none;
}
:host([narrow]) {
diff --git a/chromium/ui/webui/resources/cr_elements/icons.html b/chromium/ui/webui/resources/cr_elements/icons.html
index 45ea1fcf770..5790b5860e6 100644
--- a/chromium/ui/webui/resources/cr_elements/icons.html
+++ b/chromium/ui/webui/resources/cr_elements/icons.html
@@ -30,6 +30,13 @@ blurry at 20 px). Please use 20 px icons when available.
<g id="menu">
<path d="M2 4h16v2H2zM2 9h16v2H2zM2 14h16v2H2z"></path>
</g>
+ <if expr="chromeos">
+ <g id="banner-warning">
+ <path fill-rule="evenodd" clip-rule="evenodd"
+ d="M9.13177 1.50386C9.51566 0.832046 10.4844 0.832046 10.8683 1.50386L18.8683 15.5039C19.2492 16.1705 18.7678 17 18 17H2.00001C1.23219 17 0.750823 16.1705 1.13177 15.5039L9.13177 1.50386ZM10 4.01556L3.72321 15H16.2768L10 4.01556ZM9 11H11V7H9V11ZM11 14H9V12H11V14Z" fill="#E8710A">
+ </path>
+ </g>
+ </if>
</svg>
</iron-iconset-svg>
@@ -80,6 +87,11 @@ blurry at 20 px). Please use 20 px icons when available.
d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z">
</path>
</g>
+ <g id="check-circle">
+ <path
+ d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
+ </path>
+ </g>
</if>
<g id="cancel">
<path
@@ -238,12 +250,6 @@ blurry at 20 px). Please use 20 px icons when available.
<g id="star">
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path>
</g>
- <g id="supervisor-account" viewBox="0 0 48 48">
- <path d="M0 0h48v48H0z" fill="none"></path>
- <path
- d="M33 24c2.76 0 4.98-2.24 4.98-5s-2.22-5-4.98-5c-2.76 0-5 2.24-5 5s2.24 5 5 5zm-15-2c3.31 0 5.98-2.69 5.98-6s-2.67-6-5.98-6c-3.31 0-6 2.69-6 6s2.69 6 6 6zm15 6c-3.67 0-11 1.84-11 5.5V38h22v-4.5c0-3.66-7.33-5.5-11-5.5zm-15-2c-4.67 0-14 2.34-14 7v5h14v-4.5c0-1.7.67-4.67 4.74-6.94C21 26.19 19.31 26 18 26z">
- </path>
- </g>
<g id="sync">
<path
d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z">
diff --git a/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn
index 22f8e5f9b25..0e61e9f13cc 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn
+++ b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn
@@ -14,12 +14,6 @@ js_type_check("closure_compile") {
":cr_policy_pref_indicator",
":cr_tooltip_icon",
]
- if (is_chromeos) {
- deps += [
- ":cr_policy_network_behavior_mojo",
- ":cr_policy_network_indicator_mojo",
- ]
- }
}
js_library("cr_policy_indicator") {
@@ -42,26 +36,6 @@ js_library("cr_policy_pref_indicator") {
externs_list = [ "$externs_path/settings_private.js" ]
}
-if (is_chromeos) {
- # TODO(jonmann): These cr_policy_network_* libs should be moved to cr_components.
- js_library("cr_policy_network_behavior_mojo") {
- deps = [
- ":cr_policy_indicator_behavior",
- "//chromeos/services/network_config/public/mojom:mojom_js_library_for_compile",
- "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider",
- "//ui/webui/resources/cr_components/chromeos/network:onc_mojo",
- ]
- }
-
- js_library("cr_policy_network_indicator_mojo") {
- deps = [
- ":cr_policy_indicator_behavior",
- ":cr_policy_network_behavior_mojo",
- ":cr_tooltip_icon",
- ]
- }
-}
-
js_library("cr_tooltip_icon") {
}
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.html
deleted file mode 100644
index 3a1174c3436..00000000000
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior_mojo.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<link rel="import" href="../../html/polymer.html">
-<link rel="import" href="cr_policy_indicator_behavior.html">
-<script src="cr_policy_network_behavior_mojo.js"></script>
diff --git a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
index 4f4c0b03cbd..6d0cf07bc83 100644
--- a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -29,6 +29,9 @@
--google-red-600-rgb: 217, 48, 37; /* #d93025 */
--google-red-600: rgb(var(--google-red-600-rgb));
+ --google-yellow-50-rgb: 254, 247, 224; /* #fef7e0 */
+ --google-yellow-50: rgb(var(--google-yellow-50-rgb));
+
/* -refresh differentiate from polymer's color.html. */
--google-blue-refresh-100-rgb: 210, 227, 252; /* #d2e3fc */
--google-blue-refresh-100: rgb(var(--google-blue-refresh-100-rgb));
@@ -155,16 +158,6 @@
--cr-section-indent-padding: calc(
var(--cr-section-padding) + var(--cr-section-indent-width));
- /* TODO(crbug.com/973674): Do not use this mixin. Use the class cr-row
- * from shared_style_css.html instead. */
- --cr-section: {
- align-items: center;
- border-top: var(--cr-separator-line);
- display: flex;
- min-height: var(--cr-section-min-height);
- padding: 0 var(--cr-section-padding);
- }
-
--cr-section-vertical-margin: 21px;
--cr-centered-card-max-width: 680px;
diff --git a/chromium/ui/webui/resources/cr_elements_resources.grdp b/chromium/ui/webui/resources/cr_elements_resources.grdp
index 60a58298ff0..b2f7c58f833 100644
--- a/chromium/ui/webui/resources/cr_elements_resources.grdp
+++ b/chromium/ui/webui/resources/cr_elements_resources.grdp
@@ -98,6 +98,12 @@
<structure name="IDR_CR_ELEMENTS_CR_RADIO_BUTTON_JS"
file="cr_elements/cr_radio_button/cr_radio_button.js"
type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_CR_CARD_RADIO_BUTTON_HTML"
+ file="cr_elements/cr_radio_button/cr_card_radio_button.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_CR_CARD_RADIO_BUTTON_JS"
+ file="cr_elements/cr_radio_button/cr_card_radio_button.js"
+ type="chrome_html" />
<structure name="IDR_CR_ELEMENTS_CR_RADIO_BUTTON_BEHAVIOR_HTML"
file="cr_elements/cr_radio_button/cr_radio_button_behavior.html"
type="chrome_html" />
@@ -189,20 +195,6 @@
<structure name="IDR_CR_ELEMENTS_CR_POLICY_INDICATOR_BEHAVIOR_JS"
file="cr_elements/policy/cr_policy_indicator_behavior.js"
type="chrome_html" />
- <if expr="chromeos">
- <structure name="IDR_CR_ELEMENTS_CR_POLICY_NETWORK_BEHAVIOR_MOJO_HTML"
- file="cr_elements/policy/cr_policy_network_behavior_mojo.html"
- type="chrome_html" />
- <structure name="IDR_CR_ELEMENTS_CR_POLICY_NETWORK_BEHAVIOR_MOJO_JS"
- file="cr_elements/policy/cr_policy_network_behavior_mojo.js"
- type="chrome_html" />
- <structure name="IDR_CR_ELEMENTS_CR_POLICY_NETWORK_INDICATOR_MOJO_JS"
- file="cr_elements/policy/cr_policy_network_indicator_mojo.js"
- type="chrome_html" />
- <structure name="IDR_CR_ELEMENTS_CR_POLICY_NETWORK_INDICATOR_MOJO_HTML"
- file="cr_elements/policy/cr_policy_network_indicator_mojo.html"
- type="chrome_html" />
- </if>
<structure name="IDR_CR_ELEMENTS_CR_POLICY_PREF_BEHAVIOR_HTML"
file="cr_elements/policy/cr_policy_pref_behavior.html"
type="chrome_html" />
diff --git a/chromium/ui/webui/resources/cr_elements_resources_v3.grdp b/chromium/ui/webui/resources/cr_elements_resources_v3.grdp
index d8c2e3d035a..45ded9f61e8 100644
--- a/chromium/ui/webui/resources/cr_elements_resources_v3.grdp
+++ b/chromium/ui/webui/resources/cr_elements_resources_v3.grdp
@@ -29,6 +29,10 @@
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.m.js"
use_base_dir="false"
type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CR_EXPAND_BUTTON_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
<include name="IDR_CR_ELEMENTS_CR_FINGERPRINT_ICON_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_icon.m.js"
use_base_dir="false"
@@ -37,8 +41,8 @@
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.m.js"
use_base_dir="false"
type="BINDATA" />
- <include name="IDR_CR_ELEMENTS_CR_EXPAND_BUTTON_M_JS"
- file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.m.js"
+ <include name="IDR_CR_ELEMENTS_CR_GRID_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_grid/cr_grid.js"
use_base_dir="false"
type="BINDATA" />
<include name="IDR_CR_ELEMENTS_CR_ICON_BUTTON_M_JS"
@@ -77,6 +81,10 @@
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.m.js"
use_base_dir="false"
type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CR_CARD_RADIO_BUTTON_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
<include name="IDR_CR_ELEMENTS_CR_RADIO_BUTTON_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button.m.js"
use_base_dir="false"
@@ -202,6 +210,27 @@
file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/png.m.js"
use_base_dir="false"
type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CAMERA_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_PICTURE_LIST_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_PICTURE_PANE_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_PICTURE_TYPES_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_types.m.js"
+ use_base_dir="false"
+ type="BINDATA" />
+ <include name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_ICONS_M_JS"
+ file="${root_gen_dir}/ui/webui/resources/cr_elements/chromeos/cr_picture/icons.m.js"
+ use_base_dir="false"
+ preprocess="true"
+ type="BINDATA" />
</if>
<include name="IDR_CR_ELEMENTS_LOTTIE_M_JS"
file="${root_gen_dir}/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.m.js"
diff --git a/chromium/ui/webui/resources/css/cros_colors.json5 b/chromium/ui/webui/resources/css/cros_colors.json5
deleted file mode 100644
index db08320259e..00000000000
--- a/chromium/ui/webui/resources/css/cros_colors.json5
+++ /dev/null
@@ -1,160 +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. */
-
-/*
- * Chrome OS semantic colors.
- *
- * Colors that are used across all Chrome OS WebUIS and System Web Apps should
- * be semantically represented here.
- */
-{
- colors: {
- /*
- * Core colors.
- */
- cros_default_text_color: {
- light: "$google_grey_900",
- dark: "$google_grey_200",
- },
- cros_default_text_color_secondary: {
- light: "$google_grey_700",
- dark: "$google_grey_500",
- },
-
- cros_default_bg_color: {
- light: "#ffffff",
- dark: "$google_grey_900",
- },
-
- cros_default_icon_color_primary: {
- light: "$google_grey_700",
- dark: "$google_grey_200",
- },
-
- cros_default_icon_color_prominent: {
- light: "$google_blue_600",
- dark: "$google_blue_300",
- },
-
- /*
- * Component colors.
- */
- cros_default_toolbar_bg_color: "#ffffff",
- cros_default_toolbar_search_bg_color: "$google_grey_100",
-
- cros_menu_button_bg_color_active: "$google_blue_50",
- cros_menu_button_bg_color_hover: "$google_grey_100",
- cros_menu_button_outline_color_focused: "$google_blue_600",
-
- cros_toggle_color: "$cros_default_icon_color_prominent",
- cros_toggle_bg_color_inactive: "$google_grey_400",
- cros_toggle_button_color_inactive: "#ffffff",
- cros_toggle_ripple_color: "rgba($cros_toggle_color_rgb, .2)",
- cros_toggle_ripple_color_inactive: "rgba($google_grey_600_rgb, .15)",
-
- cros_radio_button_color: "$cros_default_icon_color_prominent",
- cros_radio_button_ripple_color: "rgba($cros_radio_button_color_rgb, .2)",
- cros_radio_button_color_unchecked: "$google_grey_700",
- cros_radio_button_ripple_color_unchecked: "rgba($google_grey_600_rgb, .15)",
-
- cros_separator_color: "rgba(0, 0, 0, 0.12)",
-
- cros_link_color: "$google_blue_700",
-
- /* button-primary */
- cros_default_button_background_color_primary:
- "$cros_default_icon_color_prominent",
- cros_default_button_label_color_primary: {
- light: "$google_grey_200",
- dark: "$google_grey_900",
- },
- /* button-primary:hover */
- /* TODO(calamity): Generate a linear-gradient() to use for compositing
- backgrounds */
- cros_default_button_background_color_primary_hover_overlay: {
- light: "rgba(255, 255, 255, 0.08)",
- dark: "rgba(0, 0, 0, 0.08)",
- },
- /* button-primary[disabled] */
- cros_default_button_background_color_primary_disabled: {
- light: "$google_grey_100",
- dark: "$google_grey_800",
- },
- cros_default_button_label_color_primary_disabled: {
- light: "$google_grey_600",
- dark: "$google_grey_500",
- },
-
- /* button-secondary */
- cros_default_button_label_color_secondary:
- "$cros_default_icon_color_prominent",
- cros_default_button_stroke_color_secondary: {
- light: "$google_grey_300",
- dark: "$google_grey_700",
- },
- /* button-secondary:hover */
- cros_default_button_background_color_secondary_hover:
- "rgba($cros_default_icon_color_prominent_rgb, 0.04)",
- /* button-secondary[disabled] */
- cros_default_button_label_color_secondary_disabled: {
- light: "$google_grey_600",
- dark: "$google_grey_500",
- },
- cros_default_button_stroke_color_secondary_disabled: {
- light: "$google_grey_100",
- dark: "$google_grey_800",
- },
-
- /* textfield */
- default_textfield_background_color: {
- light: "$google_grey_100",
- dark: "rgba(0, 0, 0, 0.3)",
- },
- default_texfield_label_color: {
- light: "$google_grey_700",
- dark: "rgba(255, 255, 255, 0.6)",
- },
- default_texfield_input_color: {
- light: "$google_grey_900",
- dark: "rgba(255, 255, 255, 0.87)",
- },
- /* textfield:focus */
- default_texfield_label_color_focus: "$cros_default_icon_color_prominent",
- /* textfield[error] */
- default_texfield_label_color_error: {
- light: "$google_red_600",
- dark: "$google_red_300",
- },
- /* textfield[disabled] */
- default_textfield_background_color_disabled: {
- light: "rgba($google_grey_100_rgb, 0.38)",
- dark: "rgba(0, 0, 0, 0.11)",
- },
- default_texfield_label_color_disabled: {
- light: "rgba($google_grey_700_rgb, 0.38)",
- dark: "rgba(0, 0, 0, 0.23)",
- },
- default_texfield_input_color_disabled: {
- light: "rgba($google_grey_900_rgb, 0.38)",
- dark: "rgba(255, 255, 255, 0.33)",
- },
-
- /*
- * One_offs.
- *
- * List your project's one_off colors below here. If a semantic color comes
- * up repeatedly, consider speaking to UX about pulling out a common
- * variable above.
- */
-
- /* OS Settings */
- cros_error_color: "$google_red_700",
- cros_success_color: "$google_green_700",
-
- cros_search_page_question_icon_color: "$google_grey_500",
- cros_app_management_permission_icon_color: "$google_grey_600",
-
- cros_user_icon_color_secondary: "rgb(210, 210, 212)",
- },
-}
diff --git a/chromium/ui/webui/resources/css/cros_palette.json5 b/chromium/ui/webui/resources/css/cros_palette.json5
deleted file mode 100644
index b26d8170e8c..00000000000
--- a/chromium/ui/webui/resources/css/cros_palette.json5
+++ /dev/null
@@ -1,68 +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. */
-
-/*
- * Chrome OS color palette.
- *
- * All colors used in Chrome OS WebUIs and System Web Apps should be specified
- * here.
- */
-{
- colors: {
- google_blue_50: "#e8f0fe",
- google_blue_100: "#d2e3fc",
- google_blue_200: "#aecbfa",
- google_blue_300: "#8ab4f8",
- google_blue_400: "#669df6",
- google_blue_500: "#4285f4",
- google_blue_600: "#1a73e8",
- google_blue_700: "#1967d2",
- google_blue_800: "#185abc",
- google_blue_900: "#174ea6",
-
- google_green_50: "#e6f4ea",
- google_green_100: "#ceead6",
- google_green_200: "#a8dab5",
- google_green_300: "#81c995",
- google_green_400: "#5bb974",
- google_green_500: "#34a853",
- google_green_600: "#1e8e3e",
- google_green_700: "#188038",
- google_green_800: "#137333",
- google_green_900: "#0d652d",
-
- google_grey_50: "#f8f9fa",
- google_grey_100: "#f1f3f4",
- google_grey_200: "#e8eaed",
- google_grey_300: "#dadce0",
- google_grey_400: "#bdc1c6",
- google_grey_500: "#9aa0a6",
- google_grey_600: "#80868b",
- google_grey_700: "#5f6368",
- google_grey_800: "#3c4043",
- google_grey_900: "#202124",
-
- google_red_50: "#fce8e6",
- google_red_100: "#fad2cf",
- google_red_200: "#f6aea9",
- google_red_300: "#f28b82",
- google_red_400: "#ee675c",
- google_red_500: "#ea4335",
- google_red_600: "#d93025",
- google_red_700: "#c5221f",
- google_red_800: "#b31412",
- google_red_900: "#a50e0e",
-
- google_yellow_50: "#fef7e0",
- google_yellow_100: "#feefc3",
- google_yellow_200: "#fde293",
- google_yellow_300: "#fdd663",
- google_yellow_400: "#fcc934",
- google_yellow_500: "#fbbc04",
- google_yellow_600: "#f9ab00",
- google_yellow_700: "#f29900",
- google_yellow_800: "#ea8600",
- google_yellow_900: "#e37400",
- }
-}
diff --git a/chromium/ui/webui/resources/js/color_utils.js b/chromium/ui/webui/resources/js/color_utils.js
new file mode 100644
index 00000000000..e5b6c530cb7
--- /dev/null
+++ b/chromium/ui/webui/resources/js/color_utils.js
@@ -0,0 +1,38 @@
+// 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.
+
+/**
+ * @fileoverview Helper functions for color manipulations.
+ */
+
+import 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-lite.js';
+
+/**
+ * Converts an SkColor object to a string in the form
+ * "rgba(<red>, <green>, <blue>, <alpha>)".
+ * @param {skia.mojom.SkColor} skColor The input color.
+ * @return {string} The rgba string.
+ */
+export function skColorToRgba(skColor) {
+ const a = (skColor.value >> 24) & 0xff;
+ const r = (skColor.value >> 16) & 0xff;
+ const g = (skColor.value >> 8) & 0xff;
+ const b = skColor.value & 0xff;
+ return `rgba(${r}, ${g}, ${b}, ${(a / 255).toFixed(2)})`;
+}
+
+/**
+ * Converts a string of the form "#rrggbb" to an SkColor object.
+ * @param {string} hexColor The color string.
+ * @return {!skia.mojom.SkColor} The SkColor object,
+ */
+export function hexColorToSkColor(hexColor) {
+ if (!/^#[0-9a-f]{6}$/.test(hexColor)) {
+ return {value: 0};
+ }
+ const r = parseInt(hexColor.substring(1, 3), 16);
+ const g = parseInt(hexColor.substring(3, 5), 16);
+ const b = parseInt(hexColor.substring(5, 7), 16);
+ return {value: 0xff000000 + (r << 16) + (g << 8) + b};
+}
diff --git a/chromium/ui/webui/resources/js/cr/ui/bubble.js b/chromium/ui/webui/resources/js/cr/ui/bubble.js
index 62eb3469cdc..7623e1513b5 100644
--- a/chromium/ui/webui/resources/js/cr/ui/bubble.js
+++ b/chromium/ui/webui/resources/js/cr/ui/bubble.js
@@ -77,6 +77,18 @@ cr.define('cr.ui', function() {
*/
BubbleBase.MIN_VIEWPORT_EDGE_MARGIN = 2;
+ /**
+ * This is used to create TrustedHTML.
+ * @type {!TrustedTypePolicy}
+ */
+ const staticHtmlPolicy = trustedTypes.createPolicy('cr-ui-bubble-js-static', {
+ createHTML: () => {
+ return '<div class="bubble-content"></div>' +
+ '<div class="bubble-shadow"></div>' +
+ '<div class="bubble-arrow"></div>';
+ },
+ });
+
BubbleBase.prototype = {
// Set up the prototype chain.
__proto__: HTMLDivElement.prototype,
@@ -92,9 +104,10 @@ cr.define('cr.ui', function() {
*/
decorate() {
this.className = 'bubble';
- this.innerHTML = '<div class="bubble-content"></div>' +
- '<div class="bubble-shadow"></div>' +
- '<div class="bubble-arrow"></div>';
+ // TODO(Jun.Kokatsu@microsoft.com): remove an empty string argument
+ // once supported.
+ // https://github.com/w3c/webappsec-trusted-types/issues/278
+ this.innerHTML = staticHtmlPolicy.createHTML('');
this.hidden = true;
this.bubbleAlignment = cr.ui.BubbleAlignment.ENTIRELY_VISIBLE;
},
@@ -123,7 +136,7 @@ cr.define('cr.ui', function() {
}
const bubbleContent = this.querySelector('.bubble-content');
- bubbleContent.innerHTML = '';
+ bubbleContent.innerHTML = trustedTypes.emptyHTML;
bubbleContent.appendChild(node);
},
diff --git a/chromium/ui/webui/resources/js/cr/ui/tree.js b/chromium/ui/webui/resources/js/cr/ui/tree.js
index 87e16056481..7e8b2e91b15 100644
--- a/chromium/ui/webui/resources/js/cr/ui/tree.js
+++ b/chromium/ui/webui/resources/js/cr/ui/tree.js
@@ -305,16 +305,15 @@ cr.define('cr.ui', function() {
/**
* This is used to create TrustedHTML.
*
- * @type {TrustedTypePolicy}
+ * @type {!TrustedTypePolicy}
*/
- const staticHTMLPolicy =
- trustedTypes.createPolicy('cr-ui-tree-js-static', {
- createHTML: () => {
- return htmlString;
- },
- });
-
- treeItem.innerHTML = staticHTMLPolicy.createHTML('');
+ const staticHtmlPolicy = trustedTypes.createPolicy(
+ 'cr-ui-tree-js-static', {createHTML: () => htmlString});
+
+ // TODO(Jun.Kokatsu@microsoft.com): remove an empty string argument
+ // once supported.
+ // https://github.com/w3c/webappsec-trusted-types/issues/278
+ treeItem.innerHTML = staticHtmlPolicy.createHTML('');
} else {
treeItem.innerHTML = htmlString;
}
diff --git a/chromium/ui/webui/resources/js/i18n_template_no_process.js b/chromium/ui/webui/resources/js/i18n_template_no_process.js
index 9778a6fd3de..5a3f971386f 100644
--- a/chromium/ui/webui/resources/js/i18n_template_no_process.js
+++ b/chromium/ui/webui/resources/js/i18n_template_no_process.js
@@ -104,12 +104,6 @@ var i18nTemplate = (function() {
const prefixes = [''];
- // Only look through shadow DOM when it's supported. As of April 2015, iOS
- // Chrome doesn't support shadow DOM.
- if (Element.prototype.createShadowRoot) {
- prefixes.push('* /deep/ ');
- }
-
const attributeNames = Object.keys(handlers);
const selector = prefixes
.map(function(prefix) {
diff --git a/chromium/ui/webui/resources/js/parse_html_subset.js b/chromium/ui/webui/resources/js/parse_html_subset.js
index a1432098eb9..b5d25c37a42 100644
--- a/chromium/ui/webui/resources/js/parse_html_subset.js
+++ b/chromium/ui/webui/resources/js/parse_html_subset.js
@@ -81,19 +81,16 @@
const allowedOptionalTags = new Set(['IMG']);
/**
- * This is used to create TrustedHTML.
- * @type {TrustedTypePolicy|undefined}
+ * This policy maps a given string to a `TrustedHTML` object
+ * without performing any validation. Callsites must ensure
+ * that the resulting object will only be used in inert
+ * documents.
+ * @type {!TrustedTypePolicy}
*/
- let untrustedHTMLPolicy;
+ let unsanitizedPolicy;
if (window.trustedTypes) {
- untrustedHTMLPolicy = trustedTypes.createPolicy('parse-html-subset', {
- createHTML: untrustedHTML => {
- // This is safe because the untrusted HTML will be sanitized
- // later in this function. We are adding this so that
- // the sanitization will not cause a Trusted Types violation.
- return untrustedHTML;
- },
- });
+ unsanitizedPolicy = trustedTypes.createPolicy(
+ 'parse-html-subset', {createHTML: untrustedHTML => untrustedHTML});
}
/**
@@ -157,7 +154,7 @@
r.selectNode(doc.body);
if (window.trustedTypes) {
- s = untrustedHTMLPolicy.createHTML(s);
+ s = unsanitizedPolicy.createHTML(s);
}
// This does not execute any scripts because the document has no view.
diff --git a/chromium/ui/webui/resources/js/util.js b/chromium/ui/webui/resources/js/util.js
index d04b82440db..08680c98783 100644
--- a/chromium/ui/webui/resources/js/util.js
+++ b/chromium/ui/webui/resources/js/util.js
@@ -100,12 +100,13 @@
}
/**
- * Disables text selection and dragging, with optional whitelist callbacks.
+ * Disables text selection and dragging, with optional callbacks to specify
+ * overrides.
* @param {function(Event):boolean=} opt_allowSelectStart Unless this function
* is defined and returns true, the onselectionstart event will be
- * surpressed.
+ * suppressed.
* @param {function(Event):boolean=} opt_allowDragStart Unless this function
- * is defined and returns true, the ondragstart event will be surpressed.
+ * is defined and returns true, the ondragstart event will be suppressed.
*/
/* #export */ function disableTextSelectAndDrag(
opt_allowSelectStart, opt_allowDragStart) {
diff --git a/chromium/ui/webui/resources/js/webui_resource_test.js b/chromium/ui/webui/resources/js/webui_resource_test.js
index 0c6396ba60b..603f02713af 100644
--- a/chromium/ui/webui/resources/js/webui_resource_test.js
+++ b/chromium/ui/webui/resources/js/webui_resource_test.js
@@ -227,7 +227,14 @@ function startTesting() {
setTimeout(startTesting, 1000);
return;
}
- continueTesting();
+
+ if (window.HTMLImports && window.HTMLImports.whenReady) {
+ /// When there is a HTML Import polyfill wait all imports to finish before
+ // starting the test.
+ window.HTMLImports.whenReady(continueTesting);
+ } else {
+ continueTesting();
+ }
}
/**
diff --git a/chromium/ui/webui/resources/polymer_resources.grdp b/chromium/ui/webui/resources/polymer_resources.grdp
index 6ea0af91cc1..929c3700fe1 100644
--- a/chromium/ui/webui/resources/polymer_resources.grdp
+++ b/chromium/ui/webui/resources/polymer_resources.grdp
@@ -329,26 +329,6 @@
<structure name="IDR_POLYMER_1_0_PAPER_TOOLTIP_PAPER_TOOLTIP_HTML"
file="../../../third_party/polymer/v1_0/components-chromium/paper-tooltip/paper-tooltip.html"
type="chrome_html" />
- <if expr="chromeos">
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_EXTRACTED_JS"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-extracted.js"
- type="chrome_html" />
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_MICRO_EXTRACTED_JS"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-micro-extracted.js"
- type="chrome_html" />
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_MICRO_HTML"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-micro.html"
- type="chrome_html" />
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_MINI_EXTRACTED_JS"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-mini-extracted.js"
- type="chrome_html" />
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_MINI_HTML"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer-mini.html"
- type="chrome_html" />
- <structure name="IDR_POLYMER_1_0_POLYMER_POLYMER_HTML"
- file="../../../third_party/polymer/v1_0/components-chromium/polymer/polymer.html"
- type="chrome_html" />
- </if>
<structure name="IDR_POLYMER_1_0_POLYMER2_POLYMER_EXTRACTED_JS"
file="../../../third_party/polymer/v1_0/components-chromium/polymer2/polymer-extracted.js"
type="chrome_html" />
diff --git a/chromium/ui/webui/resources/webui_resources.grd b/chromium/ui/webui/resources/webui_resources.grd
index 8f3ae51103c..902d266f663 100644
--- a/chromium/ui/webui/resources/webui_resources.grd
+++ b/chromium/ui/webui/resources/webui_resources.grd
@@ -86,6 +86,8 @@ without changes to the corresponding grd file. -->
<include name="IDR_WEBUI_JS_ASSERT_M_JS"
file="${root_gen_dir}/ui/webui/resources/js/assert.m.js"
use_base_dir="false" type="BINDATA" />
+ <include name="IDR_WEBUI_JS_COLOR_UTILS_JS"
+ file="js/color_utils.js" type="BINDATA" />
<include name="IDR_WEBUI_JS_CR_EVENT_TARGET_M_JS"
file="${root_gen_dir}/ui/webui/resources/js/cr/event_target.m.js"
use_base_dir="false" type="BINDATA" />
@@ -209,8 +211,9 @@ without changes to the corresponding grd file. -->
file="html/promise_resolver.html" type="chrome_html" />
<if expr="chromeos">
- <structure name="IDR_WEBUI_CSS_CROS_COLORS"
- file="${root_gen_dir}/ui/webui/resources/css/cros_colors.generated.css" type="chrome_html"
+ <structure name="IDR_WEBUI_CROS_COLORS_CSS"
+ file="${root_gen_dir}/ui/chromeos/colors/cros_colors.generated.css"
+ type="chrome_html"
use_base_dir="false" />
</if>
diff --git a/chromium/ui/webui/webui_allowlist.cc b/chromium/ui/webui/webui_allowlist.cc
index 62b5ca69231..525848a9d2f 100644
--- a/chromium/ui/webui/webui_allowlist.cc
+++ b/chromium/ui/webui/webui_allowlist.cc
@@ -89,6 +89,13 @@ void WebUIAllowlist::RegisterAutoGrantedPermission(const url::Origin& origin,
}
}
+void WebUIAllowlist::RegisterAutoGrantedPermissions(
+ const url::Origin& origin,
+ std::initializer_list<ContentSettingsType> types) {
+ for (const ContentSettingsType& type : types)
+ RegisterAutoGrantedPermission(origin, type);
+}
+
void WebUIAllowlist::SetWebUIAllowlistProvider(
WebUIAllowlistProvider* provider) {
provider_ = provider;
diff --git a/chromium/ui/webui/webui_allowlist.h b/chromium/ui/webui/webui_allowlist.h
index 91aec49307d..b1623b89f5e 100644
--- a/chromium/ui/webui/webui_allowlist.h
+++ b/chromium/ui/webui/webui_allowlist.h
@@ -5,6 +5,7 @@
#ifndef UI_WEBUI_WEBUI_ALLOWLIST_H_
#define UI_WEBUI_WEBUI_ALLOWLIST_H_
+#include <initializer_list>
#include <map>
#include "base/supports_user_data.h"
@@ -32,11 +33,26 @@ class WebUIAllowlist : public base::SupportsUserData::Data {
~WebUIAllowlist() override;
// Register auto-granted |type| permission for |origin|.
+ //
+ // WebUIAllowlist comes with no permission by default. Users can deny
+ // permissions (e.g. Settings > Site Settings) unless they are registered
+ // here.
+ //
+ // Most WebUIs would want to declare these:
+ // COOKIES: use persistent storage (e.g. localStorage)
+ // JAVASCRIPT: run JavaScript
+ // IMAGES: show images
+ // SOUND: play sounds
void RegisterAutoGrantedPermission(
const url::Origin& origin,
ContentSettingsType type,
ContentSetting setting = CONTENT_SETTING_ALLOW);
+ // Register auto-granted |types| permissions for |origin|.
+ void RegisterAutoGrantedPermissions(
+ const url::Origin& origin,
+ std::initializer_list<ContentSettingsType> types);
+
std::unique_ptr<content_settings::RuleIterator> GetRuleIterator(
ContentSettingsType content_type) const;
diff --git a/chromium/ui/webui/webui_features.gni b/chromium/ui/webui/webui_features.gni
index 8ed14a40380..2c037396837 100644
--- a/chromium/ui/webui/webui_features.gni
+++ b/chromium/ui/webui/webui_features.gni
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/config/chrome_build.gni")
+
declare_args() {
# Optimize parts of Chrome's UI written with web technologies (HTML/CSS/JS)
# for runtime performance purposes. This does more work at compile time for
@@ -10,4 +12,10 @@ declare_args() {
# Enable the WebUI version of the browser's tab strip.
enable_webui_tab_strip = is_chromeos || is_linux || is_win
+
+ # Enable tab search only on a branded desktop build.
+ # In order to get commit queue coverage for C++ tests,
+ # this flag only gates the html/js webui resources, not the C++
+ # files
+ enable_tab_search = !is_android && !is_ios && is_chrome_branded
}
diff --git a/chromium/ui/wm/BUILD.gn b/chromium/ui/wm/BUILD.gn
index 65c85f5baad..3aefa26b766 100644
--- a/chromium/ui/wm/BUILD.gn
+++ b/chromium/ui/wm/BUILD.gn
@@ -2,11 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//testing/test.gni")
-jumbo_component("wm") {
+component("wm") {
sources = [
"core/accelerator_delegate.h",
"core/accelerator_filter.cc",
@@ -94,7 +93,7 @@ jumbo_component("wm") {
}
}
-jumbo_static_library("test_support") {
+static_library("test_support") {
testonly = true
sources = [
"test/testing_cursor_client_observer.cc",
@@ -119,6 +118,10 @@ jumbo_static_library("test_support") {
}
test("wm_unittests") {
+ if ((is_linux && !is_chromeos) || chromeos_is_browser_only) {
+ use_xvfb = true
+ }
+
sources = [
"core/capture_controller_unittest.cc",
"core/compound_event_filter_unittest.cc",
diff --git a/chromium/ui/wm/core/shadow_controller.cc b/chromium/ui/wm/core/shadow_controller.cc
index 395257e38c8..6b6712ca266 100644
--- a/chromium/ui/wm/core/shadow_controller.cc
+++ b/chromium/ui/wm/core/shadow_controller.cc
@@ -264,7 +264,7 @@ void ShadowController::Impl::HandlePossibleShadowVisibilityChange(
if (shadow) {
shadow->SetElevation(GetShadowElevationForActiveState(window));
shadow->layer()->SetVisible(should_show);
- } else if (should_show && !shadow) {
+ } else if (should_show) {
CreateShadowForWindow(window);
}
}
diff --git a/chromium/ui/wm/core/window_animations.cc b/chromium/ui/wm/core/window_animations.cc
index f55ee90238e..51d7944e859 100644
--- a/chromium/ui/wm/core/window_animations.cc
+++ b/chromium/ui/wm/core/window_animations.cc
@@ -9,10 +9,10 @@
#include <algorithm>
#include <memory>
+#include "base/bind.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
-#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
@@ -23,7 +23,7 @@
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_observer.h"
#include "ui/base/class_property.h"
-#include "ui/compositor/animation_metrics_reporter.h"
+#include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/compositor_observer.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
@@ -139,21 +139,24 @@ class HidingWindowAnimationObserverBase : public aura::WindowObserver {
DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserverBase);
};
-class HidingWindowMetricsReporter : public ui::AnimationMetricsReporter {
- public:
- HidingWindowMetricsReporter() = default;
- ~HidingWindowMetricsReporter() override = default;
-
- void Report(int value) override {
- UMA_HISTOGRAM_PERCENTAGE("Ash.Window.AnimationSmoothness.Hide", value);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HidingWindowMetricsReporter);
-};
+// TODO(crbug.com/1021774): Find a better home and merge with
+// ash::metris_util::ForSmoothness.
+using SmoothnessCallback = base::RepeatingCallback<void(int smoothness)>;
+ui::AnimationThroughputReporter::ReportCallback ForSmoothness(
+ SmoothnessCallback callback) {
+ return base::BindRepeating(
+ [](SmoothnessCallback callback,
+ cc::FrameSequenceMetrics::ThroughputData throughput) {
+ const int smoothness = std::floor(100.0f * throughput.frames_produced /
+ throughput.frames_expected);
+ callback.Run(smoothness);
+ },
+ std::move(callback));
+}
-base::LazyInstance<HidingWindowMetricsReporter>::Leaky g_reporter_hide =
- LAZY_INSTANCE_INITIALIZER;
+void ReportHideSmoothness(int smoothness) {
+ UMA_HISTOGRAM_PERCENTAGE("Ash.Window.AnimationSmoothness.Hide", smoothness);
+}
} // namespace
@@ -300,8 +303,12 @@ void AnimateHideWindowCommon(aura::Window* window,
// Property sets within this scope will be implicitly animated.
ScopedHidingAnimationSettings hiding_settings(window);
- hiding_settings.layer_animation_settings()->SetAnimationMetricsReporter(
- g_reporter_hide.Pointer());
+
+ // Report animation smoothness for animations created within this scope.
+ ui::AnimationThroughputReporter reporter(
+ hiding_settings.layer_animation_settings()->GetAnimator(),
+ ForSmoothness(base::BindRepeating(&ReportHideSmoothness)));
+
// Render surface caching may not provide a benefit when animating the opacity
// of a single layer.
if (!window->layer()->children().empty())
diff --git a/chromium/ui/wm/public/BUILD.gn b/chromium/ui/wm/public/BUILD.gn
index 799fb90a7b7..b4ae9394e3a 100644
--- a/chromium/ui/wm/public/BUILD.gn
+++ b/chromium/ui/wm/public/BUILD.gn
@@ -2,9 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
-
-jumbo_component("public") {
+component("public") {
output_name = "wm_public"
public = [