summaryrefslogtreecommitdiff
path: root/chromium/ui/views
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 15:28:34 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:54:51 +0000
commit2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch)
treeeb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/ui/views
parentb014812705fc80bff0a5c120dfcef88f349816dc (diff)
downloadqtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/views')
-rw-r--r--chromium/ui/views/BUILD.gn51
-rw-r--r--chromium/ui/views/DEPS4
-rw-r--r--chromium/ui/views/accessibility/DEPS2
-rw-r--r--chromium/ui/views/accessibility/ax_aura_obj_cache.cc52
-rw-r--r--chromium/ui/views/accessibility/ax_aura_obj_cache.h5
-rw-r--r--chromium/ui/views/accessibility/ax_aura_obj_wrapper.h3
-rw-r--r--chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc4
-rw-r--r--chromium/ui/views/accessibility/ax_tree_source_views.cc126
-rw-r--r--chromium/ui/views/accessibility/ax_tree_source_views.h61
-rw-r--r--chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc159
-rw-r--r--chromium/ui/views/accessibility/ax_view_obj_wrapper.cc8
-rw-r--r--chromium/ui/views/accessibility/ax_view_obj_wrapper.h3
-rw-r--r--chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc5
-rw-r--r--chromium/ui/views/accessibility/ax_widget_obj_wrapper.h1
-rw-r--r--chromium/ui/views/accessibility/ax_window_obj_wrapper.cc4
-rw-r--r--chromium/ui/views/accessibility/ax_window_obj_wrapper.h1
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility.h39
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_auralinux.h28
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_mac.h26
-rw-r--r--chromium/ui/views/accessibility/native_view_accessibility_win.h30
-rw-r--r--chromium/ui/views/accessibility/view_accessibility.cc3
-rw-r--r--chromium/ui/views/accessibility/view_accessibility.h31
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc (renamed from chromium/ui/views/accessibility/native_view_accessibility_base.cc)191
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate.h (renamed from chromium/ui/views/accessibility/native_view_accessibility_base.h)52
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc (renamed from chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc)102
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h29
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.h28
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm (renamed from chromium/ui/views/accessibility/native_view_accessibility_mac.mm)12
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc (renamed from chromium/ui/views/accessibility/native_view_accessibility_unittest.cc)73
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.cc (renamed from chromium/ui/views/accessibility/native_view_accessibility_win.cc)19
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.h32
-rw-r--r--chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc (renamed from chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc)62
-rw-r--r--chromium/ui/views/animation/bounds_animator.cc104
-rw-r--r--chromium/ui/views/animation/bounds_animator.h10
-rw-r--r--chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc5
-rw-r--r--chromium/ui/views/animation/ink_drop_host_view.cc3
-rw-r--r--chromium/ui/views/animation/ink_drop_impl.cc6
-rw-r--r--chromium/ui/views/animation/square_ink_drop_ripple.cc5
-rw-r--r--chromium/ui/views/bubble/bubble_border.cc4
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate.cc41
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate.h3
-rw-r--r--chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc18
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view.cc17
-rw-r--r--chromium/ui/views/bubble/bubble_frame_view_unittest.cc1
-rw-r--r--chromium/ui/views/bubble/tooltip_icon.cc13
-rw-r--r--chromium/ui/views/bubble/tooltip_icon.h16
-rw-r--r--chromium/ui/views/bubble/tray_bubble_view.cc7
-rw-r--r--chromium/ui/views/bubble/tray_bubble_view.h3
-rw-r--r--chromium/ui/views/cocoa/bridged_content_view.h5
-rw-r--r--chromium/ui/views/cocoa/bridged_content_view.mm195
-rw-r--r--chromium/ui/views/cocoa/bridged_native_widget.h59
-rw-r--r--chromium/ui/views/cocoa/bridged_native_widget.mm263
-rw-r--r--chromium/ui/views/cocoa/bridged_native_widget_unittest.mm281
-rw-r--r--chromium/ui/views/cocoa/cocoa_mouse_capture.mm8
-rw-r--r--chromium/ui/views/cocoa/drag_drop_client_mac.mm6
-rw-r--r--chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm3
-rw-r--r--chromium/ui/views/cocoa/native_widget_mac_nswindow.h29
-rw-r--r--chromium/ui/views/cocoa/native_widget_mac_nswindow.mm96
-rw-r--r--chromium/ui/views/cocoa/views_nswindow_delegate.mm71
-rw-r--r--chromium/ui/views/cocoa/window_touch_bar_delegate.h21
-rw-r--r--chromium/ui/views/controls/button/button.cc3
-rw-r--r--chromium/ui/views/controls/button/button.h5
-rw-r--r--chromium/ui/views/controls/button/checkbox.cc10
-rw-r--r--chromium/ui/views/controls/button/checkbox.h8
-rw-r--r--chromium/ui/views/controls/button/image_button.cc4
-rw-r--r--chromium/ui/views/controls/button/image_button.h2
-rw-r--r--chromium/ui/views/controls/button/menu_button_unittest.cc8
-rw-r--r--chromium/ui/views/controls/button/radio_button.cc4
-rw-r--r--chromium/ui/views/controls/focus_ring.cc6
-rw-r--r--chromium/ui/views/controls/focus_ring.h8
-rw-r--r--chromium/ui/views/controls/image_view.cc37
-rw-r--r--chromium/ui/views/controls/image_view.h6
-rw-r--r--chromium/ui/views/controls/label_unittest.cc9
-rw-r--r--chromium/ui/views/controls/menu/menu_config.cc14
-rw-r--r--chromium/ui/views/controls/menu/menu_config.h18
-rw-r--r--chromium/ui/views/controls/menu/menu_config_chromeos.cc2
-rw-r--r--chromium/ui/views/controls/menu/menu_config_mac.mm16
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.cc456
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.h28
-rw-r--r--chromium/ui/views/controls/menu/menu_controller_unittest.cc350
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.cc61
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.h5
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm7
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_unittest.cc45
-rw-r--r--chromium/ui/views/controls/menu/menu_scroll_view_container.cc1
-rw-r--r--chromium/ui/views/controls/menu/menu_separator.h3
-rw-r--r--chromium/ui/views/controls/menu/menu_types.h5
-rw-r--r--chromium/ui/views/controls/menu/submenu_view.cc3
-rw-r--r--chromium/ui/views/controls/native/native_view_host.cc10
-rw-r--r--chromium/ui/views/controls/native/native_view_host.h7
-rw-r--r--chromium/ui/views/controls/native/native_view_host_aura.cc35
-rw-r--r--chromium/ui/views/controls/native/native_view_host_aura.h5
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac.h6
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac.mm43
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac_unittest.mm26
-rw-r--r--chromium/ui/views/controls/native/native_view_host_wrapper.h8
-rw-r--r--chromium/ui/views/controls/prefix_selector.cc6
-rw-r--r--chromium/ui/views/controls/prefix_selector.h2
-rw-r--r--chromium/ui/views/controls/scroll_view_unittest.cc13
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar.cc3
-rw-r--r--chromium/ui/views/controls/scrollbar/base_scroll_bar.h3
-rw-r--r--chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h2
-rw-r--r--chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm6
-rw-r--r--chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc2
-rw-r--r--chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h2
-rw-r--r--chromium/ui/views/controls/styled_label_unittest.cc7
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc50
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.h2
-rw-r--r--chromium/ui/views/controls/textfield/textfield.cc70
-rw-r--r--chromium/ui/views/controls/textfield/textfield.h13
-rw-r--r--chromium/ui/views/controls/textfield/textfield_unittest.cc16
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu.cc2
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.cc17
-rw-r--r--chromium/ui/views/controls/views_text_services_context_menu_base.h3
-rw-r--r--chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm4
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.cc5
-rw-r--r--chromium/ui/views/controls/webview/web_dialog_view.h4
-rw-r--r--chromium/ui/views/controls/webview/webview.cc44
-rw-r--r--chromium/ui/views/controls/webview/webview.h23
-rw-r--r--chromium/ui/views/examples/BUILD.gn3
-rw-r--r--chromium/ui/views/examples/DEPS1
-rw-r--r--chromium/ui/views/examples/box_layout_example.cc4
-rw-r--r--chromium/ui/views/examples/checkbox_example.cc3
-rw-r--r--chromium/ui/views/examples/dialog_example.cc3
-rw-r--r--chromium/ui/views/examples/examples_main.cc5
-rw-r--r--chromium/ui/views/examples/label_example.cc9
-rw-r--r--chromium/ui/views/examples/multiline_example.cc6
-rw-r--r--chromium/ui/views/examples/radio_button_example.cc8
-rw-r--r--chromium/ui/views/examples/radio_button_example.h3
-rw-r--r--chromium/ui/views/examples/table_example.cc20
-rw-r--r--chromium/ui/views/examples/text_example.cc3
-rw-r--r--chromium/ui/views/focus/focus_manager.cc10
-rw-r--r--chromium/ui/views/focus/focus_manager_unittest.cc159
-rw-r--r--chromium/ui/views/layout/grid_layout.h16
-rw-r--r--chromium/ui/views/layout/layout_provider.cc20
-rw-r--r--chromium/ui/views/layout/layout_provider.h11
-rw-r--r--chromium/ui/views/linux_ui/linux_ui.h12
-rw-r--r--chromium/ui/views/mus/BUILD.gn38
-rw-r--r--chromium/ui/views/mus/DEPS3
-rw-r--r--chromium/ui/views/mus/OWNERS6
-rw-r--r--chromium/ui/views/mus/aura_init.cc127
-rw-r--r--chromium/ui/views/mus/aura_init.h58
-rw-r--r--chromium/ui/views/mus/ax_remote_host.cc196
-rw-r--r--chromium/ui/views/mus/ax_remote_host.h112
-rw-r--r--chromium/ui/views/mus/ax_remote_host_unittest.cc169
-rw-r--r--chromium/ui/views/mus/ax_tree_source_mus.cc44
-rw-r--r--chromium/ui/views/mus/ax_tree_source_mus.h41
-rw-r--r--chromium/ui/views/mus/ax_tree_source_mus_unittest.cc90
-rw-r--r--chromium/ui/views/mus/clipboard_mus.cc347
-rw-r--r--chromium/ui/views/mus/clipboard_mus.h89
-rw-r--r--chromium/ui/views/mus/clipboard_unittest.cc123
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus.cc86
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus.h1
-rw-r--r--chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc152
-rw-r--r--chromium/ui/views/mus/drag_interactive_uitest.cc3
-rw-r--r--chromium/ui/views/mus/interactive_ui_tests_manifest.json3
-rw-r--r--chromium/ui/views/mus/mus_client.cc75
-rw-r--r--chromium/ui/views/mus/mus_client.h30
-rw-r--r--chromium/ui/views/mus/mus_client_test_api.h30
-rw-r--r--chromium/ui/views/mus/mus_views_delegate.cc23
-rw-r--r--chromium/ui/views/mus/mus_views_delegate.h32
-rw-r--r--chromium/ui/views/mus/remote_view/BUILD.gn2
-rw-r--r--chromium/ui/views/mus/screen_mus.cc46
-rw-r--r--chromium/ui/views/mus/screen_mus.h13
-rw-r--r--chromium/ui/views/mus/views_mus_test_suite.cc24
-rw-r--r--chromium/ui/views/mus/window_manager_constants_converters.h2
-rw-r--r--chromium/ui/views/painter.cc30
-rw-r--r--chromium/ui/views/painter.h8
-rw-r--r--chromium/ui/views/run_all_unittests_main.cc4
-rw-r--r--chromium/ui/views/style/platform_style.cc8
-rw-r--r--chromium/ui/views/style/platform_style_mac.mm2
-rw-r--r--chromium/ui/views/style/typography_provider.cc2
-rw-r--r--chromium/ui/views/touchui/touch_selection_menu_runner_views.cc5
-rw-r--r--chromium/ui/views/view.cc18
-rw-r--r--chromium/ui/views/view_model.cc3
-rw-r--r--chromium/ui/views/views_delegate.cc8
-rw-r--r--chromium/ui/views/views_delegate.h4
-rw-r--r--chromium/ui/views/views_perftests.cc4
-rw-r--r--chromium/ui/views/widget/DEPS2
-rw-r--r--chromium/ui/views/widget/ax_native_widget_mac_unittest.mm (renamed from chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm)50
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc22
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc9
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc2
-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_platform.cc42
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc12
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h1
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc79
-rw-r--r--chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h15
-rw-r--r--chromium/ui/views/widget/desktop_aura/window_event_filter.cc2
-rw-r--r--chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc2
-rw-r--r--chromium/ui/views/widget/native_widget_aura.cc29
-rw-r--r--chromium/ui/views/widget/native_widget_aura.h1
-rw-r--r--chromium/ui/views/widget/native_widget_aura_unittest.cc2
-rw-r--r--chromium/ui/views/widget/native_widget_mac.h4
-rw-r--r--chromium/ui/views/widget/native_widget_mac.mm42
-rw-r--r--chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm35
-rw-r--r--chromium/ui/views/widget/native_widget_mac_unittest.mm165
-rw-r--r--chromium/ui/views/widget/native_widget_private.h1
-rw-r--r--chromium/ui/views/widget/util_mac.h17
-rw-r--r--chromium/ui/views/widget/widget.cc8
-rw-r--r--chromium/ui/views/widget/widget.h17
-rw-r--r--chromium/ui/views/widget/widget_delegate.cc2
-rw-r--r--chromium/ui/views/widget/widget_interactive_uitest.cc20
-rw-r--r--chromium/ui/views/widget/widget_unittest.cc71
-rw-r--r--chromium/ui/views/win/DEPS1
-rw-r--r--chromium/ui/views/win/hwnd_message_handler.cc102
-rw-r--r--chromium/ui/views/win/hwnd_message_handler.h16
-rw-r--r--chromium/ui/views/win/pen_event_processor.cc16
-rw-r--r--chromium/ui/views/win/pen_event_processor.h1
-rw-r--r--chromium/ui/views/win/pen_event_processor_unittest.cc33
-rw-r--r--chromium/ui/views/window/dialog_delegate.cc9
-rw-r--r--chromium/ui/views/window/dialog_delegate_unittest.cc4
-rw-r--r--chromium/ui/views/window/frame_background.cc78
-rw-r--r--chromium/ui/views/window/frame_background.h11
-rw-r--r--chromium/ui/views/window/non_client_view.cc33
-rw-r--r--chromium/ui/views/window/non_client_view.h3
-rw-r--r--chromium/ui/views/window/non_client_view_unittest.cc99
-rw-r--r--chromium/ui/views/window/window_resize_utils.cc106
-rw-r--r--chromium/ui/views/window/window_resize_utils.h56
-rw-r--r--chromium/ui/views/window/window_resize_utils_unittest.cc161
223 files changed, 5394 insertions, 2450 deletions
diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn
index c9ecb17ed42..961f2b06534 100644
--- a/chromium/ui/views/BUILD.gn
+++ b/chromium/ui/views/BUILD.gn
@@ -95,6 +95,7 @@ jumbo_component("views") {
"cocoa/views_nswindow_delegate.h",
"cocoa/views_scrollbar_bridge.h",
"cocoa/widget_owner_nswindow_adapter.h",
+ "cocoa/window_touch_bar_delegate.h",
"color_chooser/color_chooser_listener.h",
"color_chooser/color_chooser_view.h",
"context_menu_controller.h",
@@ -257,6 +258,7 @@ jumbo_component("views") {
"window/window_button_order_provider.h",
"window/window_resources.h",
"window/window_shape.h",
+ "window/window_resize_utils.h",
"word_lookup_client.h",
]
@@ -436,6 +438,7 @@ jumbo_component("views") {
"window/native_frame_view.cc",
"window/non_client_view.cc",
"window/window_button_order_provider.cc",
+ "window/window_resize_utils.cc",
"window/window_shape.cc",
]
@@ -575,6 +578,7 @@ jumbo_component("views") {
public += [
"accessibility/ax_aura_obj_cache.h",
"accessibility/ax_aura_obj_wrapper.h",
+ "accessibility/ax_tree_source_views.h",
"accessibility/ax_view_obj_wrapper.h",
"accessibility/ax_widget_obj_wrapper.h",
"accessibility/ax_window_obj_wrapper.h",
@@ -607,6 +611,7 @@ jumbo_component("views") {
sources += [
"accessibility/ax_aura_obj_cache.cc",
"accessibility/ax_aura_obj_wrapper.cc",
+ "accessibility/ax_tree_source_views.cc",
"accessibility/ax_view_obj_wrapper.cc",
"accessibility/ax_widget_obj_wrapper.cc",
"accessibility/ax_window_obj_wrapper.cc",
@@ -675,20 +680,21 @@ jumbo_component("views") {
]
if (use_atk) {
sources += [
- "accessibility/native_view_accessibility_auralinux.cc",
- "accessibility/native_view_accessibility_auralinux.h",
+ "accessibility/view_ax_platform_node_delegate_auralinux.cc",
+ "accessibility/view_ax_platform_node_delegate_auralinux.h",
]
configs += [ "//build/config/linux/atk" ]
}
} else if (is_win) {
+ public += [ "widget/desktop_aura/desktop_window_tree_host_win.h" ]
sources += [
"widget/desktop_aura/desktop_drag_drop_client_win.cc",
"widget/desktop_aura/desktop_drag_drop_client_win.h",
"widget/desktop_aura/desktop_screen_win.cc",
"widget/desktop_aura/desktop_screen_win.h",
"widget/desktop_aura/desktop_window_tree_host_win.cc",
- "widget/desktop_aura/desktop_window_tree_host_win.h",
]
+ deps += [ "//ui/events:dom_keyboard_layout" ]
} else if (use_ozone) {
public += [ "widget/desktop_aura/desktop_screen_ozone.h" ]
sources += [ "widget/desktop_aura/desktop_screen_ozone.cc" ]
@@ -726,12 +732,12 @@ jumbo_component("views") {
if (has_native_accessibility) {
sources += [
- "accessibility/native_view_accessibility_base.cc",
- "accessibility/native_view_accessibility_base.h",
- "accessibility/native_view_accessibility_mac.h",
- "accessibility/native_view_accessibility_mac.mm",
- "accessibility/native_view_accessibility_win.cc",
- "accessibility/native_view_accessibility_win.h",
+ "accessibility/view_ax_platform_node_delegate.cc",
+ "accessibility/view_ax_platform_node_delegate.h",
+ "accessibility/view_ax_platform_node_delegate_mac.h",
+ "accessibility/view_ax_platform_node_delegate_mac.mm",
+ "accessibility/view_ax_platform_node_delegate_win.cc",
+ "accessibility/view_ax_platform_node_delegate_win.h",
]
}
@@ -829,7 +835,7 @@ jumbo_source_set("test_support_internal") {
"//base/test:test_support",
"//gpu/ipc/service",
"//ipc:test_support",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//skia",
"//testing/gtest",
"//ui/base",
@@ -918,11 +924,10 @@ source_set("views_unittests_sources") {
"controls/button/label_button_label.h",
]
if (has_native_accessibility) {
- public += [ "accessibility/native_view_accessibility_base.h" ]
+ public += [ "accessibility/view_ax_platform_node_delegate.h" ]
}
sources = [
- "accessibility/native_view_accessibility_win_unittest.cc",
"accessible_pane_view_unittest.cc",
"animation/bounds_animator_unittest.cc",
"animation/flood_fill_ink_drop_ripple_unittest.cc",
@@ -991,7 +996,7 @@ source_set("views_unittests_sources") {
"view_tracker_unittest.cc",
"view_unittest.cc",
"view_unittest_mac.mm",
- "widget/native_widget_mac_accessibility_unittest.mm",
+ "widget/ax_native_widget_mac_unittest.mm",
"widget/native_widget_mac_unittest.mm",
"widget/native_widget_unittest.cc",
"widget/root_view_unittest.cc",
@@ -999,6 +1004,8 @@ source_set("views_unittests_sources") {
"window/custom_frame_view_unittest.cc",
"window/dialog_client_view_unittest.cc",
"window/dialog_delegate_unittest.cc",
+ "window/non_client_view_unittest.cc",
+ "window/window_resize_utils_unittest.cc",
]
configs += [ "//build/config:precompiled_headers" ]
@@ -1042,21 +1049,28 @@ source_set("views_unittests_sources") {
]
if (is_win) {
+ public += [ "accessibility/view_ax_platform_node_delegate_win.h" ]
+
public_deps += [
"//build/win:default_exe_manifest",
"//third_party/iaccessible2",
"//third_party/wtl",
]
+
libs = [
"imm32.lib",
"oleacc.lib",
"comctl32.lib",
]
- sources += [ "win/pen_event_processor_unittest.cc" ]
+
+ sources += [
+ "accessibility/view_ax_platform_node_delegate_win_unittest.cc",
+ "win/pen_event_processor_unittest.cc",
+ ]
}
if (has_native_accessibility) {
- sources += [ "accessibility/native_view_accessibility_unittest.cc" ]
+ sources += [ "accessibility/view_ax_platform_node_delegate_unittest.cc" ]
}
if (use_x11) {
@@ -1074,6 +1088,7 @@ source_set("views_unittests_sources") {
if (use_aura) {
sources += [
"accessibility/ax_aura_obj_cache_unittest.cc",
+ "accessibility/ax_tree_source_views_unittest.cc",
"controls/native/native_view_host_aura_unittest.cc",
"touchui/touch_selection_menu_runner_views_unittest.cc",
"view_unittest_aura.cc",
@@ -1144,7 +1159,7 @@ test("views_unittests") {
deps = [
":views_unittests_sources",
- "//mojo/edk",
+ "//mojo/core/embedder",
]
}
@@ -1162,7 +1177,7 @@ source_set("views_interactive_ui_tests") {
":views",
"//base",
"//base/test:test_support",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//skia",
"//testing/gtest",
"//ui/base:test_support",
@@ -1221,7 +1236,7 @@ test("views_perftests") {
":test_support",
"//base/test:test_support",
"//cc/base:base",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//testing/perf",
"//ui/resources:ui_test_pak",
]
diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS
index e6f8d868ebf..39d2ec74e37 100644
--- a/chromium/ui/views/DEPS
+++ b/chromium/ui/views/DEPS
@@ -31,10 +31,10 @@ specific_include_rules = {
"+ui/wm/test"
],
"run_all_unittests_main\.cc": [
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
],
"views_perftests\.cc": [
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
],
"view_unittest\.cc": [
"+cc/playback"
diff --git a/chromium/ui/views/accessibility/DEPS b/chromium/ui/views/accessibility/DEPS
index 62e6a668d8a..35ddbecff9b 100644
--- a/chromium/ui/views/accessibility/DEPS
+++ b/chromium/ui/views/accessibility/DEPS
@@ -4,6 +4,6 @@ include_rules = [
specific_include_rules = {
"ax_system_caret_win_interactive_uitest\.cc": [
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
]
}
diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc
index 3fc1975935b..519d09c2959 100644
--- a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc
+++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc
@@ -4,7 +4,6 @@
#include "ui/views/accessibility/ax_aura_obj_cache.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/singleton.h"
#include "base/strings/string_util.h"
#include "ui/aura/client/aura_constants.h"
@@ -16,6 +15,7 @@
#include "ui/views/accessibility/ax_window_obj_wrapper.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
namespace views {
@@ -69,8 +69,9 @@ void AXAuraObjCache::Remove(Widget* widget) {
// When an entire widget is deleted, it doesn't always send a notification
// on each of its views, so we need to explore them recursively.
- if (widget->GetRootView())
- RemoveViewSubtree(widget->GetRootView());
+ auto* view = widget->GetRootView();
+ if (view)
+ RemoveViewSubtree(view);
}
void AXAuraObjCache::Remove(aura::Window* window, aura::Window* parent) {
@@ -83,28 +84,14 @@ void AXAuraObjCache::Remove(aura::Window* window, aura::Window* parent) {
AXAuraObjWrapper* AXAuraObjCache::Get(int32_t id) {
auto it = cache_.find(id);
-
- if (it == cache_.end())
- return nullptr;
-
- return it->second.get();
-}
-
-void AXAuraObjCache::Remove(int32_t id) {
- AXAuraObjWrapper* obj = Get(id);
-
- if (id == -1 || !obj)
- return;
-
- cache_.erase(id);
+ return it != cache_.end() ? it->second.get() : nullptr;
}
void AXAuraObjCache::GetTopLevelWindows(
std::vector<AXAuraObjWrapper*>* children) {
- for (auto it = window_to_id_map_.begin(); it != window_to_id_map_.end();
- ++it) {
- if (!it->first->parent())
- children->push_back(GetOrCreate(it->first));
+ for (const auto& it : window_to_id_map_) {
+ if (!it.first->parent())
+ children->push_back(GetOrCreate(it.first));
}
}
@@ -168,7 +155,15 @@ View* AXAuraObjCache::GetFocusedView() {
if (focused_window->GetProperty(
aura::client::kAccessibilityFocusFallsbackToWidgetKey)) {
- // If no view is focused, falls back to root view.
+ // If focused widget has non client view, falls back to first child view of
+ // its client view. We don't expect that non client view gets keyboard
+ // focus.
+ if (focused_widget->non_client_view() &&
+ focused_widget->non_client_view()->client_view() &&
+ focused_widget->non_client_view()->client_view()->has_children()) {
+ return focused_widget->non_client_view()->client_view()->child_at(0);
+ }
+
return focused_widget->GetRootView();
}
@@ -204,11 +199,11 @@ AXAuraObjWrapper* AXAuraObjCache::CreateInternal(
if (it != aura_view_to_id_map.end())
return Get(it->second);
- AXAuraObjWrapper* wrapper = new AuraViewWrapper(aura_view);
+ auto wrapper = std::make_unique<AuraViewWrapper>(aura_view);
int32_t id = wrapper->GetUniqueId().Get();
aura_view_to_id_map[aura_view] = id;
- cache_[id] = base::WrapUnique(wrapper);
- return wrapper;
+ cache_[id] = std::move(wrapper);
+ return cache_[id].get();
}
template <typename AuraView>
@@ -219,10 +214,7 @@ int32_t AXAuraObjCache::GetIDInternal(
return -1;
auto it = aura_view_to_id_map.find(aura_view);
- if (it != aura_view_to_id_map.end())
- return it->second;
-
- return -1;
+ return it != aura_view_to_id_map.end() ? it->second : -1;
}
template <typename AuraView>
@@ -233,7 +225,7 @@ void AXAuraObjCache::RemoveInternal(
if (id == -1)
return;
aura_view_to_id_map.erase(aura_view);
- Remove(id);
+ cache_.erase(id);
}
} // namespace views
diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.h b/chromium/ui/views/accessibility/ax_aura_obj_cache.h
index 48be286a40c..f798bdb059e 100644
--- a/chromium/ui/views/accessibility/ax_aura_obj_cache.h
+++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.h
@@ -38,6 +38,8 @@ class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver {
class Delegate {
public:
+ virtual ~Delegate() {}
+
virtual void OnChildWindowRemoved(AXAuraObjWrapper* parent) = 0;
virtual void OnEvent(AXAuraObjWrapper* aura_obj,
ax::mojom::Event event_type) = 0;
@@ -67,9 +69,6 @@ class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver {
// Lookup a cached entry based on an id.
AXAuraObjWrapper* Get(int32_t id);
- // Remove a cached entry based on an id.
- void Remove(int32_t id);
-
// Get all top level windows this cache knows about.
void GetTopLevelWindows(std::vector<AXAuraObjWrapper*>* children);
diff --git a/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h
index 74ffaacc3b0..fa0f7954116 100644
--- a/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h
+++ b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h
@@ -28,6 +28,9 @@ class VIEWS_EXPORT AXAuraObjWrapper {
public:
virtual ~AXAuraObjWrapper() {}
+ // See ViewAccessibility for details.
+ virtual bool IsIgnored() = 0;
+
// Traversal and serialization.
virtual AXAuraObjWrapper* GetParent() = 0;
virtual void GetChildren(
diff --git a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
index d3821559c0a..84b893fca7f 100644
--- a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
+++ b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc
@@ -10,7 +10,7 @@
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_variant.h"
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/core/embedder/embedder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
@@ -36,7 +36,7 @@ class AXSystemCaretWinTest : public test::WidgetTest {
~AXSystemCaretWinTest() override {}
void SetUp() override {
- mojo::edk::Init();
+ mojo::core::Init();
gl::GLSurfaceTestSupport::InitializeOneOff();
ui::RegisterPathProvider();
base::FilePath ui_test_pak_path;
diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.cc b/chromium/ui/views/accessibility/ax_tree_source_views.cc
new file mode 100644
index 00000000000..cffc41f79b3
--- /dev/null
+++ b/chromium/ui/views/accessibility/ax_tree_source_views.cc
@@ -0,0 +1,126 @@
+// Copyright 2018 The Chromium Authors. 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/ax_tree_source_views.h"
+
+#include <vector>
+
+#include "ui/accessibility/ax_action_data.h"
+#include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/transform.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+
+namespace views {
+
+void AXTreeSourceViews::HandleAccessibleAction(const ui::AXActionData& action) {
+ int id = action.target_node_id;
+
+ // In Views, we only support setting the selection within a single node,
+ // not across multiple nodes like on the web.
+ if (action.action == ax::mojom::Action::kSetSelection) {
+ if (action.anchor_node_id != action.focus_node_id) {
+ NOTREACHED();
+ return;
+ }
+ id = action.anchor_node_id;
+ }
+
+ AXAuraObjWrapper* obj = AXAuraObjCache::GetInstance()->Get(id);
+ if (obj)
+ obj->HandleAccessibleAction(action);
+}
+
+bool AXTreeSourceViews::GetTreeData(ui::AXTreeData* tree_data) const {
+ tree_data->loaded = true;
+ tree_data->loading_progress = 1.0;
+ AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus();
+ if (focus)
+ tree_data->focus_id = focus->GetUniqueId().Get();
+ return true;
+}
+
+AXAuraObjWrapper* AXTreeSourceViews::GetFromId(int32_t id) const {
+ AXAuraObjWrapper* root = GetRoot();
+ // Root might not be in the cache.
+ if (id == root->GetUniqueId().Get())
+ return root;
+ return AXAuraObjCache::GetInstance()->Get(id);
+}
+
+int32_t AXTreeSourceViews::GetId(AXAuraObjWrapper* node) const {
+ return node->GetUniqueId().Get();
+}
+
+void AXTreeSourceViews::GetChildren(
+ AXAuraObjWrapper* node,
+ std::vector<AXAuraObjWrapper*>* out_children) const {
+ node->GetChildren(out_children);
+}
+
+AXAuraObjWrapper* AXTreeSourceViews::GetParent(AXAuraObjWrapper* node) const {
+ AXAuraObjWrapper* root = GetRoot();
+ // The root has no parent by definition.
+ if (node->GetUniqueId() == root->GetUniqueId())
+ return nullptr;
+ AXAuraObjWrapper* parent = node->GetParent();
+ // A top-level widget doesn't have a parent, so return the root.
+ if (!parent)
+ return root;
+ return parent;
+}
+
+bool AXTreeSourceViews::IsValid(AXAuraObjWrapper* node) const {
+ return node && !node->IsIgnored();
+}
+
+bool AXTreeSourceViews::IsEqual(AXAuraObjWrapper* node1,
+ AXAuraObjWrapper* node2) const {
+ return node1 && node2 && node1->GetUniqueId() == node2->GetUniqueId();
+}
+
+AXAuraObjWrapper* AXTreeSourceViews::GetNull() const {
+ return nullptr;
+}
+
+void AXTreeSourceViews::SerializeNode(AXAuraObjWrapper* node,
+ ui::AXNodeData* out_data) const {
+ node->Serialize(out_data);
+
+ // Converts the global coordinates reported by each AXAuraObjWrapper
+ // into parent-relative coordinates to be used in the accessibility
+ // tree. That way when any Window, Widget, or View moves (and fires
+ // a location changed event), its descendants all move relative to
+ // it by default.
+ AXAuraObjWrapper* parent = node->GetParent();
+ if (!parent)
+ return;
+ ui::AXNodeData parent_data;
+ parent->Serialize(&parent_data);
+ out_data->location.Offset(-parent_data.location.OffsetFromOrigin());
+ out_data->offset_container_id = parent->GetUniqueId().Get();
+}
+
+std::string AXTreeSourceViews::ToString(AXAuraObjWrapper* root,
+ std::string prefix) {
+ ui::AXNodeData data;
+ root->Serialize(&data);
+ std::string output = prefix + data.ToString() + '\n';
+
+ std::vector<AXAuraObjWrapper*> children;
+ root->GetChildren(&children);
+
+ prefix += prefix[0];
+ for (AXAuraObjWrapper* child : children)
+ output += ToString(child, prefix);
+
+ return output;
+}
+
+AXTreeSourceViews::AXTreeSourceViews() = default;
+
+AXTreeSourceViews::~AXTreeSourceViews() = default;
+
+} // namespace views
diff --git a/chromium/ui/views/accessibility/ax_tree_source_views.h b/chromium/ui/views/accessibility/ax_tree_source_views.h
new file mode 100644
index 00000000000..1f50c3adddb
--- /dev/null
+++ b/chromium/ui/views/accessibility/ax_tree_source_views.h
@@ -0,0 +1,61 @@
+// Copyright 2018 The Chromium Authors. 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_ACCESSIBILITY_AX_TREE_SOURCE_VIEWS_H_
+#define UI_VIEWS_ACCESSIBILITY_AX_TREE_SOURCE_VIEWS_H_
+
+#include "base/macros.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_data.h"
+#include "ui/accessibility/ax_tree_source.h"
+#include "ui/views/views_export.h"
+
+namespace ui {
+struct AXActionData;
+}
+
+namespace views {
+
+class AXAuraObjWrapper;
+
+// This class exposes the views hierarchy as an accessibility tree permitting
+// use with other accessibility classes. Subclasses must implement GetRoot().
+// The root can be an existing object in the Widget/View hierarchy or a new node
+// (for example to create the "desktop" node for the extension API call
+// chrome.automation.getDesktop()).
+class VIEWS_EXPORT AXTreeSourceViews
+ : public ui::
+ AXTreeSource<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData> {
+ public:
+ // Invokes an action on an Aura object.
+ void HandleAccessibleAction(const ui::AXActionData& action);
+
+ // AXTreeSource:
+ bool GetTreeData(ui::AXTreeData* data) const override;
+ // GetRoot() must be implemented by subclasses.
+ AXAuraObjWrapper* GetFromId(int32_t id) const override;
+ int32_t GetId(AXAuraObjWrapper* node) const override;
+ void GetChildren(AXAuraObjWrapper* node,
+ std::vector<AXAuraObjWrapper*>* out_children) const override;
+ AXAuraObjWrapper* GetParent(AXAuraObjWrapper* node) const override;
+ bool IsValid(AXAuraObjWrapper* node) const override;
+ bool IsEqual(AXAuraObjWrapper* node1, AXAuraObjWrapper* node2) const override;
+ AXAuraObjWrapper* GetNull() const override;
+ void SerializeNode(AXAuraObjWrapper* node,
+ ui::AXNodeData* out_data) const override;
+
+ // Useful for debugging.
+ std::string ToString(views::AXAuraObjWrapper* root, std::string prefix);
+
+ protected:
+ AXTreeSourceViews();
+ ~AXTreeSourceViews() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AXTreeSourceViews);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ACCESSIBILITY_AX_TREE_SOURCE_VIEWS_H_
diff --git a/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc b/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc
new file mode 100644
index 00000000000..82f6f53a8e5
--- /dev/null
+++ b/chromium/ui/views/accessibility/ax_tree_source_views_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/accessibility/ax_tree_source_views.h"
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+namespace {
+
+// TestAXTreeSourceViews provides a root object for testing.
+class TestAXTreeSourceViews : public AXTreeSourceViews {
+ public:
+ TestAXTreeSourceViews(AXAuraObjWrapper* root) : root_(root) {}
+ ~TestAXTreeSourceViews() override = default;
+
+ // AXTreeSource:
+ AXAuraObjWrapper* GetRoot() const override { return root_; }
+
+ private:
+ AXAuraObjWrapper* root_;
+ DISALLOW_COPY_AND_ASSIGN(TestAXTreeSourceViews);
+};
+
+class AXTreeSourceViewsTest : public ViewsTestBase {
+ public:
+ AXTreeSourceViewsTest() = default;
+ ~AXTreeSourceViewsTest() override = default;
+
+ // testing::Test:
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ widget_ = std::make_unique<Widget>();
+ Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = gfx::Rect(11, 22, 333, 444);
+ params.context = GetContext();
+ widget_->Init(params);
+ widget_->SetContentsView(new View());
+
+ label1_ = new Label(base::ASCIIToUTF16("Label 1"));
+ label1_->SetBounds(1, 1, 111, 111);
+ widget_->GetContentsView()->AddChildView(label1_);
+
+ label2_ = new Label(base::ASCIIToUTF16("Label 2"));
+ label2_->SetBounds(2, 2, 222, 222);
+ widget_->GetContentsView()->AddChildView(label2_);
+
+ textfield_ = new Textfield();
+ textfield_->SetBounds(222, 2, 20, 200);
+ widget_->GetContentsView()->AddChildView(textfield_);
+ }
+
+ void TearDown() override {
+ widget_.reset();
+ ViewsTestBase::TearDown();
+ }
+
+ std::unique_ptr<Widget> widget_;
+ Label* label1_ = nullptr; // Owned by views hierarchy.
+ Label* label2_ = nullptr; // Owned by views hierarchy.
+ Textfield* textfield_ = nullptr; // Owned by views hierarchy.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AXTreeSourceViewsTest);
+};
+
+TEST_F(AXTreeSourceViewsTest, Basics) {
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+
+ // Start the tree at the Widget's contents view.
+ AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView());
+ TestAXTreeSourceViews tree(root);
+ EXPECT_EQ(root, tree.GetRoot());
+
+ // The root has no parent.
+ EXPECT_FALSE(tree.GetParent(root));
+
+ // The root has the right children.
+ std::vector<AXAuraObjWrapper*> children;
+ tree.GetChildren(root, &children);
+ ASSERT_EQ(3u, children.size());
+
+ // The labels are the children.
+ AXAuraObjWrapper* label1 = children[0];
+ AXAuraObjWrapper* label2 = children[1];
+ AXAuraObjWrapper* textfield = children[2];
+ EXPECT_EQ(label1, cache->GetOrCreate(label1_));
+ EXPECT_EQ(label2, cache->GetOrCreate(label2_));
+ EXPECT_EQ(textfield, cache->GetOrCreate(textfield_));
+
+ // The parents is correct.
+ EXPECT_EQ(root, tree.GetParent(label1));
+ EXPECT_EQ(root, tree.GetParent(label2));
+ EXPECT_EQ(root, tree.GetParent(textfield));
+
+ // IDs match the ones in the cache.
+ EXPECT_EQ(root->GetUniqueId().Get(), tree.GetId(root));
+ EXPECT_EQ(label1->GetUniqueId().Get(), tree.GetId(label1));
+ EXPECT_EQ(label2->GetUniqueId().Get(), tree.GetId(label2));
+ EXPECT_EQ(textfield->GetUniqueId().Get(), tree.GetId(textfield));
+
+ // Reverse ID lookups work.
+ EXPECT_EQ(root, tree.GetFromId(root->GetUniqueId().Get()));
+ EXPECT_EQ(label1, tree.GetFromId(label1->GetUniqueId().Get()));
+ EXPECT_EQ(label2, tree.GetFromId(label2->GetUniqueId().Get()));
+ EXPECT_EQ(textfield, tree.GetFromId(textfield->GetUniqueId().Get()));
+
+ // Validity.
+ EXPECT_TRUE(tree.IsValid(root));
+ EXPECT_FALSE(tree.IsValid(nullptr));
+
+ // Comparisons.
+ EXPECT_TRUE(tree.IsEqual(label1, label1));
+ EXPECT_FALSE(tree.IsEqual(label1, label2));
+ EXPECT_FALSE(tree.IsEqual(label1, nullptr));
+ EXPECT_FALSE(tree.IsEqual(nullptr, label1));
+
+ // Null pointers is the null value.
+ EXPECT_EQ(nullptr, tree.GetNull());
+}
+
+TEST_F(AXTreeSourceViewsTest, GetTreeDataWithFocus) {
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+ TestAXTreeSourceViews tree(cache->GetOrCreate(widget_.get()));
+ textfield_->RequestFocus();
+
+ ui::AXTreeData tree_data;
+ tree.GetTreeData(&tree_data);
+ EXPECT_TRUE(tree_data.loaded);
+ EXPECT_EQ(cache->GetID(textfield_), tree_data.focus_id);
+}
+
+TEST_F(AXTreeSourceViewsTest, IgnoredView) {
+ View* ignored_view = new View();
+ ignored_view->GetViewAccessibility().set_is_ignored(true);
+ widget_->GetContentsView()->AddChildView(ignored_view);
+
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+ TestAXTreeSourceViews tree(cache->GetOrCreate(widget_.get()));
+ EXPECT_FALSE(tree.IsValid(cache->GetOrCreate(ignored_view)));
+}
+
+} // namespace
+} // namespace views
diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc
index 2645da529ef..4d1a9867730 100644
--- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc
+++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc
@@ -4,10 +4,8 @@
#include "ui/views/accessibility/ax_view_obj_wrapper.h"
-#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_node_data.h"
-#include "ui/events/event_utils.h"
#include "ui/views/accessibility/ax_aura_obj_cache.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/view.h"
@@ -22,6 +20,10 @@ AXViewObjWrapper::AXViewObjWrapper(View* view) : view_(view) {
AXViewObjWrapper::~AXViewObjWrapper() {}
+bool AXViewObjWrapper::IsIgnored() {
+ return view_->GetViewAccessibility().is_ignored();
+}
+
AXAuraObjWrapper* AXViewObjWrapper::GetParent() {
AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
if (view_->parent())
@@ -30,7 +32,7 @@ AXAuraObjWrapper* AXViewObjWrapper::GetParent() {
if (view_->GetWidget())
return cache->GetOrCreate(view_->GetWidget());
- return NULL;
+ return nullptr;
}
void AXViewObjWrapper::GetChildren(
diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.h b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h
index c14058a497c..eff142fc74b 100644
--- a/chromium/ui/views/accessibility/ax_view_obj_wrapper.h
+++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h
@@ -22,6 +22,7 @@ class AXViewObjWrapper : public AXAuraObjWrapper {
View* view() { return view_; }
// AXAuraObjWrapper overrides.
+ bool IsIgnored() override;
AXAuraObjWrapper* GetParent() override;
void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) override;
void Serialize(ui::AXNodeData* out_node_data) override;
@@ -29,7 +30,7 @@ class AXViewObjWrapper : public AXAuraObjWrapper {
bool HandleAccessibleAction(const ui::AXActionData& action) override;
private:
- View* view_;
+ View* const view_;
DISALLOW_COPY_AND_ASSIGN(AXViewObjWrapper);
};
diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc
index 43eb649aa24..0192ff9f743 100644
--- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc
+++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc
@@ -8,7 +8,6 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/accessibility/ax_aura_obj_cache.h"
#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
-#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -27,6 +26,10 @@ AXWidgetObjWrapper::~AXWidgetObjWrapper() {
widget_ = NULL;
}
+bool AXWidgetObjWrapper::IsIgnored() {
+ return false;
+}
+
AXAuraObjWrapper* AXWidgetObjWrapper::GetParent() {
return AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetNativeView());
}
diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h
index 6cc11bc27aa..5110cfe5981 100644
--- a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h
+++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h
@@ -25,6 +25,7 @@ class AXWidgetObjWrapper : public AXAuraObjWrapper,
~AXWidgetObjWrapper() override;
// AXAuraObjWrapper overrides.
+ bool IsIgnored() override;
AXAuraObjWrapper* GetParent() override;
void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) override;
void Serialize(ui::AXNodeData* out_node_data) override;
diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc
index 5a5ca2dd36c..e7bbe189e43 100644
--- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -58,6 +58,10 @@ AXWindowObjWrapper::~AXWindowObjWrapper() {
window_ = NULL;
}
+bool AXWindowObjWrapper::IsIgnored() {
+ return false;
+}
+
AXAuraObjWrapper* AXWindowObjWrapper::GetParent() {
if (!window_->parent())
return NULL;
diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h
index 9904dc471d7..44f3f64eb3c 100644
--- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h
+++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h
@@ -32,6 +32,7 @@ class AXWindowObjWrapper : public AXAuraObjWrapper,
void set_is_alert(bool is_alert) { is_alert_ = is_alert; }
// AXAuraObjWrapper overrides.
+ bool IsIgnored() override;
AXAuraObjWrapper* GetParent() override;
void GetChildren(std::vector<AXAuraObjWrapper*>* out_children) override;
void Serialize(ui::AXNodeData* out_node_data) override;
diff --git a/chromium/ui/views/accessibility/native_view_accessibility.h b/chromium/ui/views/accessibility/native_view_accessibility.h
deleted file mode 100644
index d2ed8c176f9..00000000000
--- a/chromium/ui/views/accessibility/native_view_accessibility.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_H_
-#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/views/views_export.h"
-
-namespace views {
-
-class View;
-
-// Abstract base for that allows native platform accessibility toolkits to
-// interface with a View.
-class VIEWS_EXPORT NativeViewAccessibility {
- public:
- static std::unique_ptr<NativeViewAccessibility> Create(View* view);
-
- virtual ~NativeViewAccessibility() {}
-
- virtual gfx::NativeViewAccessible GetNativeObject() = 0;
- virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type) = 0;
-
- protected:
- NativeViewAccessibility() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibility);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.h b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.h
deleted file mode 100644
index b42fc06133d..00000000000
--- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_
-#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_
-
-#include "base/macros.h"
-#include "ui/views/accessibility/native_view_accessibility_base.h"
-#include "ui/views/view.h"
-
-namespace views {
-
-class NativeViewAccessibilityAuraLinux : public NativeViewAccessibilityBase {
- public:
- NativeViewAccessibilityAuraLinux(View* view);
- ~NativeViewAccessibilityAuraLinux() override;
-
- // NativeViewAccessibilityBase:
- gfx::NativeViewAccessible GetParent() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityAuraLinux);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_AURALINUX_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_mac.h b/chromium/ui/views/accessibility/native_view_accessibility_mac.h
deleted file mode 100644
index 32b02b40b01..00000000000
--- a/chromium/ui/views/accessibility/native_view_accessibility_mac.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_MAC_H_
-#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_MAC_H_
-
-#include "base/macros.h"
-#include "ui/views/accessibility/native_view_accessibility_base.h"
-
-namespace views {
-
-// Mac-specific accessibility class for NativeViewAccessibility.
-class NativeViewAccessibilityMac : public NativeViewAccessibilityBase {
- public:
- explicit NativeViewAccessibilityMac(View* view);
-
- // NativeViewAccessibilityBase:
- gfx::NativeViewAccessible GetParent() override;
-
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityMac);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_MAC_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.h b/chromium/ui/views/accessibility/native_view_accessibility_win.h
deleted file mode 100644
index 8f64f68877a..00000000000
--- a/chromium/ui/views/accessibility/native_view_accessibility_win.h
+++ /dev/null
@@ -1,30 +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_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_
-#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_
-
-#include "base/macros.h"
-#include "ui/views/accessibility/native_view_accessibility_base.h"
-#include "ui/views/view.h"
-
-namespace views {
-
-class NativeViewAccessibilityWin : public NativeViewAccessibilityBase {
- public:
- NativeViewAccessibilityWin(View* view);
- ~NativeViewAccessibilityWin() override;
-
- // NativeViewAccessibilityBase:
- gfx::NativeViewAccessible GetParent() override;
- gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
- gfx::Rect GetClippedScreenBoundsRect() const override;
- gfx::Rect GetUnclippedScreenBoundsRect() const override;
-
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityWin);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_
diff --git a/chromium/ui/views/accessibility/view_accessibility.cc b/chromium/ui/views/accessibility/view_accessibility.cc
index 91a0db6df8a..db7109d7113 100644
--- a/chromium/ui/views/accessibility/view_accessibility.cc
+++ b/chromium/ui/views/accessibility/view_accessibility.cc
@@ -5,6 +5,7 @@
#include "ui/views/accessibility/view_accessibility.h"
#include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/base/ui_features.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
@@ -42,7 +43,7 @@ std::unique_ptr<ViewAccessibility> ViewAccessibility::Create(View* view) {
ViewAccessibility::ViewAccessibility(View* view)
: owner_view_(view), is_leaf_(false) {}
-ViewAccessibility::~ViewAccessibility() {}
+ViewAccessibility::~ViewAccessibility() = default;
const ui::AXUniqueId& ViewAccessibility::GetUniqueId() const {
return unique_id_;
diff --git a/chromium/ui/views/accessibility/view_accessibility.h b/chromium/ui/views/accessibility/view_accessibility.h
index 6a366b4cb6f..903c4e5d2c6 100644
--- a/chromium/ui/views/accessibility/view_accessibility.h
+++ b/chromium/ui/views/accessibility/view_accessibility.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "build/build_config.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/ax_unique_id.h"
@@ -20,14 +21,13 @@ class View;
// An object that manages the accessibility interface for a View.
//
-// The default accessibility properties of a View is determined by
-// calling View::GetAccessibleNodeData(), which is overridden by many
-// View subclasses. ViewAccessibility lets you override these for a
-// particular view.
+// The default accessibility properties of a View is determined by calling
+// |View::GetAccessibleNodeData()|, which is overridden by many |View|
+// subclasses. |ViewAccessibility| lets you override these for a particular
+// view.
//
-// On some platforms, subclasses of ViewAccessibility own the
-// AXPlatformNode that implements the native accessibility APIs on that
-// platform.
+// In most cases, subclasses of |ViewAccessibility| own the |AXPlatformNode|
+// that implements the native accessibility APIs on a specific platform.
class VIEWS_EXPORT ViewAccessibility {
public:
static std::unique_ptr<ViewAccessibility> Create(View* view);
@@ -51,16 +51,19 @@ class VIEWS_EXPORT ViewAccessibility {
void OverrideDescription(const std::string& description);
void OverrideIsLeaf(); // Force this node to be treated as a leaf node.
- virtual void OnAutofillShown(){};
- virtual void OnAutofillHidden(){};
-
virtual gfx::NativeViewAccessible GetNativeObject();
virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type) {}
+#if defined(OS_MACOSX)
+ virtual void AnnounceText(base::string16& text) {}
+#endif
virtual const ui::AXUniqueId& GetUniqueId() const;
bool IsLeaf() const;
+ bool is_ignored() const { return is_ignored_; }
+ void set_is_ignored(bool ignored) { is_ignored_ = ignored; }
+
protected:
explicit ViewAccessibility(View* view);
@@ -78,6 +81,14 @@ class VIEWS_EXPORT ViewAccessibility {
bool is_leaf_;
+ // When true the view is ignored when generating the AX node hierarchy, but
+ // its children are included. For example, if you created a custom table with
+ // the digits 1 - 9 arranged in a 3 x 3 grid, marking the table and rows
+ // "ignored" would mean that the digits 1 - 9 would appear as if they were
+ // immediate children of the root. Likewise "internal" container views can be
+ // ignored, like a Widget's RootView, ClientView, etc.
+ bool is_ignored_ = false;
+
DISALLOW_COPY_AND_ASSIGN(ViewAccessibility);
};
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc
index d22d3778b41..10cc9408045 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_base.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+
#include <map>
#include <memory>
-#include "ui/views/accessibility/native_view_accessibility_base.h"
-
#include "base/lazy_instance.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/accessibility/platform/ax_platform_node.h"
@@ -99,9 +99,9 @@ void FlushQueue() {
} // namespace
// static
-int32_t NativeViewAccessibilityBase::fake_focus_view_id_ = 0;
+int ViewAXPlatformNodeDelegate::menu_depth_ = 0;
-NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view)
+ViewAXPlatformNodeDelegate::ViewAXPlatformNodeDelegate(View* view)
: ViewAccessibility(view) {
ax_node_ = ui::AXPlatformNode::Create(this);
DCHECK(ax_node_);
@@ -116,16 +116,19 @@ NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view)
g_unique_id_to_ax_platform_node.Get()[GetUniqueId().Get()] = ax_node_;
}
-NativeViewAccessibilityBase::~NativeViewAccessibilityBase() {
+ViewAXPlatformNodeDelegate::~ViewAXPlatformNodeDelegate() {
+ if (ui::AXPlatformNode::GetPopupFocusOverride() == GetNativeObject())
+ ui::AXPlatformNode::SetPopupFocusOverride(nullptr);
+
g_unique_id_to_ax_platform_node.Get().erase(GetUniqueId().Get());
ax_node_->Destroy();
}
-gfx::NativeViewAccessible NativeViewAccessibilityBase::GetNativeObject() {
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetNativeObject() {
return ax_node_->GetNativeViewAccessible();
}
-void NativeViewAccessibilityBase::NotifyAccessibilityEvent(
+void ViewAXPlatformNodeDelegate::NotifyAccessibilityEvent(
ax::mojom::Event event_type) {
if (g_is_queueing_events) {
g_event_queue.Get().push_back({event_type, GetUniqueId().Get()});
@@ -134,20 +137,63 @@ void NativeViewAccessibilityBase::NotifyAccessibilityEvent(
ax_node_->NotifyAccessibilityEvent(event_type);
- // A focus context event is intended to send a focus event and a delay
- // before the next focus event. It makes sense to delay the entire next
- // synchronous batch of next events so that ordering remains the same.
- if (event_type == ax::mojom::Event::kFocusContext) {
- // Begin queueing subsequent events and flush queue asynchronously.
- g_is_queueing_events = true;
- base::OnceCallback<void()> cb = base::BindOnce(&FlushQueue);
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(cb));
+ // Some events have special handling.
+ switch (event_type) {
+ case ax::mojom::Event::kMenuStart:
+ OnMenuStart();
+ break;
+ case ax::mojom::Event::kMenuEnd:
+ OnMenuEnd();
+ break;
+ case ax::mojom::Event::kSelection:
+ if (menu_depth_ && GetData().role == ax::mojom::Role::kMenuItem)
+ OnMenuItemActive();
+ break;
+ case ax::mojom::Event::kFocusContext: {
+ // A focus context event is intended to send a focus event and a delay
+ // before the next focus event. It makes sense to delay the entire next
+ // synchronous batch of next events so that ordering remains the same.
+ // Begin queueing subsequent events and flush queue asynchronously.
+ g_is_queueing_events = true;
+ base::OnceCallback<void()> cb = base::BindOnce(&FlushQueue);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(cb));
+ break;
+ }
+ default:
+ break;
}
}
+#if defined(OS_MACOSX)
+void ViewAXPlatformNodeDelegate::AnnounceText(base::string16& text) {
+ ax_node_->AnnounceText(text);
+}
+#endif
+
+void ViewAXPlatformNodeDelegate::OnMenuItemActive() {
+ // When a native menu is shown and has an item selected, treat it and the
+ // currently selected item as focused, even though the actual focus is in the
+ // browser's currently focused textfield.
+ ui::AXPlatformNode::SetPopupFocusOverride(
+ ax_node_->GetNativeViewAccessible());
+}
+
+void ViewAXPlatformNodeDelegate::OnMenuStart() {
+ ++menu_depth_;
+}
+
+void ViewAXPlatformNodeDelegate::OnMenuEnd() {
+ // When a native menu is hidden, restore accessibility focus to the current
+ // focus in the document.
+ if (menu_depth_ >= 1)
+ --menu_depth_;
+ if (menu_depth_ == 0)
+ ui::AXPlatformNode::SetPopupFocusOverride(nullptr);
+}
+
// ui::AXPlatformNodeDelegate
-const ui::AXNodeData& NativeViewAccessibilityBase::GetData() const {
+const ui::AXNodeData& ViewAXPlatformNodeDelegate::GetData() const {
// Clear it, then populate it.
data_ = ui::AXNodeData();
GetAccessibleNodeData(&data_);
@@ -175,12 +221,7 @@ const ui::AXNodeData& NativeViewAccessibilityBase::GetData() const {
return data_;
}
-const ui::AXTreeData& NativeViewAccessibilityBase::GetTreeData() const {
- CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
- return empty_data;
-}
-
-int NativeViewAccessibilityBase::GetChildCount() {
+int ViewAXPlatformNodeDelegate::GetChildCount() {
if (IsLeaf())
return 0;
int child_count = view()->child_count();
@@ -192,7 +233,7 @@ int NativeViewAccessibilityBase::GetChildCount() {
return child_count;
}
-gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) {
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::ChildAtIndex(int index) {
if (IsLeaf())
return nullptr;
@@ -211,13 +252,13 @@ gfx::NativeViewAccessible NativeViewAccessibilityBase::ChildAtIndex(int index) {
return nullptr;
}
-gfx::NativeWindow NativeViewAccessibilityBase::GetTopLevelWidget() {
+gfx::NativeWindow ViewAXPlatformNodeDelegate::GetTopLevelWidget() {
if (view()->GetWidget())
return view()->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
return nullptr;
}
-gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() {
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetParent() {
if (view()->parent())
return view()->parent()->GetNativeViewAccessible();
@@ -230,17 +271,17 @@ gfx::NativeViewAccessible NativeViewAccessibilityBase::GetParent() {
return nullptr;
}
-gfx::Rect NativeViewAccessibilityBase::GetClippedScreenBoundsRect() const {
+gfx::Rect ViewAXPlatformNodeDelegate::GetClippedScreenBoundsRect() const {
// We could optionally add clipping here if ever needed.
return view()->GetBoundsInScreen();
}
-gfx::Rect NativeViewAccessibilityBase::GetUnclippedScreenBoundsRect() const {
+gfx::Rect ViewAXPlatformNodeDelegate::GetUnclippedScreenBoundsRect() const {
return view()->GetBoundsInScreen();
}
-gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x,
- int y) {
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::HitTestSync(int x,
+ int y) {
if (!view() || !view()->GetWidget())
return nullptr;
@@ -281,110 +322,42 @@ gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x,
return GetNativeObject();
}
-void NativeViewAccessibilityBase::OnAutofillShown() {
- // When the autofill is shown, treat it and the currently selected item as
- // focused, even though the actual focus is in the browser's currently
- // focused textfield.
- DCHECK(!fake_focus_view_id_) << "Cannot have more that one fake focus.";
- fake_focus_view_id_ = GetUniqueId().Get();
- ui::AXPlatformNode::OnAutofillShown();
-}
-
-void NativeViewAccessibilityBase::OnAutofillHidden() {
- DCHECK(fake_focus_view_id_) << "No autofill fake focus set.";
- DCHECK_EQ(fake_focus_view_id_, GetUniqueId().Get())
- << "Cannot clear autofill fake focus on an object that did not have it.";
- fake_focus_view_id_ = 0;
- ui::AXPlatformNode::OnAutofillHidden();
-}
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetFocus() {
+ gfx::NativeViewAccessible focus_override =
+ ui::AXPlatformNode::GetPopupFocusOverride();
+ if (focus_override)
+ return focus_override;
-gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() {
FocusManager* focus_manager = view()->GetFocusManager();
View* focused_view =
focus_manager ? focus_manager->GetFocusedView() : nullptr;
- if (fake_focus_view_id_) {
- ui::AXPlatformNode* ax_node = PlatformNodeFromNodeID(fake_focus_view_id_);
- if (ax_node)
- return ax_node->GetNativeViewAccessible();
- }
+
return focused_view ? focused_view->GetNativeViewAccessible() : nullptr;
}
-ui::AXPlatformNode* NativeViewAccessibilityBase::GetFromNodeID(int32_t id) {
+ui::AXPlatformNode* ViewAXPlatformNodeDelegate::GetFromNodeID(int32_t id) {
return PlatformNodeFromNodeID(id);
}
-int NativeViewAccessibilityBase::GetIndexInParent() const {
- return -1;
-}
-
-gfx::AcceleratedWidget
-NativeViewAccessibilityBase::GetTargetForNativeAccessibilityEvent() {
- return gfx::kNullAcceleratedWidget;
-}
-
-int NativeViewAccessibilityBase::GetTableRowCount() const {
- return 0;
-}
-
-int NativeViewAccessibilityBase::GetTableColCount() const {
- return 0;
-}
-
-std::vector<int32_t> NativeViewAccessibilityBase::GetColHeaderNodeIds(
- int32_t col_index) const {
- return std::vector<int32_t>();
-}
-
-std::vector<int32_t> NativeViewAccessibilityBase::GetRowHeaderNodeIds(
- int32_t row_index) const {
- return std::vector<int32_t>();
-}
-
-int32_t NativeViewAccessibilityBase::GetCellId(int32_t row_index,
- int32_t col_index) const {
- return 0;
-}
-
-int32_t NativeViewAccessibilityBase::CellIdToIndex(int32_t cell_id) const {
- return -1;
-}
-
-int32_t NativeViewAccessibilityBase::CellIndexToId(int32_t cell_index) const {
- return 0;
-}
-
-bool NativeViewAccessibilityBase::AccessibilityPerformAction(
+bool ViewAXPlatformNodeDelegate::AccessibilityPerformAction(
const ui::AXActionData& data) {
return view()->HandleAccessibleAction(data);
}
-bool NativeViewAccessibilityBase::ShouldIgnoreHoveredStateForTesting() {
+bool ViewAXPlatformNodeDelegate::ShouldIgnoreHoveredStateForTesting() {
return false;
}
-bool NativeViewAccessibilityBase::IsOffscreen() const {
+bool ViewAXPlatformNodeDelegate::IsOffscreen() const {
// TODO: need to implement.
return false;
}
-std::set<int32_t> NativeViewAccessibilityBase::GetReverseRelations(
- ax::mojom::IntAttribute attr,
- int32_t dst_id) {
- return std::set<int32_t>();
-}
-
-std::set<int32_t> NativeViewAccessibilityBase::GetReverseRelations(
- ax::mojom::IntListAttribute attr,
- int32_t dst_id) {
- return std::set<int32_t>();
-}
-
-const ui::AXUniqueId& NativeViewAccessibilityBase::GetUniqueId() const {
+const ui::AXUniqueId& ViewAXPlatformNodeDelegate::GetUniqueId() const {
return ViewAccessibility::GetUniqueId();
}
-void NativeViewAccessibilityBase::PopulateChildWidgetVector(
+void ViewAXPlatformNodeDelegate::PopulateChildWidgetVector(
std::vector<Widget*>* result_child_widgets) {
// Only attach child widgets to the root view.
Widget* widget = view()->GetWidget();
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h
index 11f0d7e778b..f3829054acc 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_base.h
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.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_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_BASE_H_
-#define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_BASE_H_
+#ifndef UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_H_
+#define UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_H_
#include <memory>
@@ -13,7 +13,7 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/platform/ax_platform_node.h"
-#include "ui/accessibility/platform/ax_platform_node_delegate.h"
+#include "ui/accessibility/platform/ax_platform_node_delegate_base.h"
#include "ui/accessibility/platform/ax_unique_id.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/accessibility/view_accessibility.h"
@@ -26,23 +26,24 @@ class View;
class Widget;
// Shared base class for platforms that require an implementation of
-// NativeViewAccessibility to interface with the native accessibility toolkit.
-// This class owns the AXPlatformNode, which implements those native APIs.
-class VIEWS_EXPORT NativeViewAccessibilityBase
+// |ViewAXPlatformNodeDelegate| to interface with the native accessibility
+// toolkit. This class owns the |AXPlatformNode|, which implements those native
+// APIs.
+class VIEWS_EXPORT ViewAXPlatformNodeDelegate
: public ViewAccessibility,
- public ui::AXPlatformNodeDelegate {
+ public ui::AXPlatformNodeDelegateBase {
public:
- ~NativeViewAccessibilityBase() override;
+ ~ViewAXPlatformNodeDelegate() override;
// ViewAccessibility:
gfx::NativeViewAccessible GetNativeObject() override;
void NotifyAccessibilityEvent(ax::mojom::Event event_type) override;
- void OnAutofillShown() override;
- void OnAutofillHidden() override;
+#if defined(OS_MACOSX)
+ void AnnounceText(base::string16& text) override;
+#endif
// ui::AXPlatformNodeDelegate
const ui::AXNodeData& GetData() const override;
- const ui::AXTreeData& GetTreeData() const override;
int GetChildCount() override;
gfx::NativeViewAccessible ChildAtIndex(int index) override;
gfx::NativeWindow GetTopLevelWidget() override;
@@ -52,45 +53,34 @@ class VIEWS_EXPORT NativeViewAccessibilityBase
gfx::NativeViewAccessible HitTestSync(int x, int y) override;
gfx::NativeViewAccessible GetFocus() override;
ui::AXPlatformNode* GetFromNodeID(int32_t id) override;
- int GetIndexInParent() const override;
- gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
- int GetTableRowCount() const override;
- int GetTableColCount() const override;
- std::vector<int32_t> GetColHeaderNodeIds(int32_t col_index) const override;
- std::vector<int32_t> GetRowHeaderNodeIds(int32_t row_index) const override;
- int32_t GetCellId(int32_t row_index, int32_t col_index) const override;
- int32_t CellIdToIndex(int32_t cell_id) const override;
- int32_t CellIndexToId(int32_t cell_index) const override;
bool AccessibilityPerformAction(const ui::AXActionData& data) override;
bool ShouldIgnoreHoveredStateForTesting() override;
bool IsOffscreen() const override;
const ui::AXUniqueId& GetUniqueId()
const override; // Also in ViewAccessibility
- std::set<int32_t> GetReverseRelations(ax::mojom::IntAttribute attr,
- int32_t dst_id) override;
- std::set<int32_t> GetReverseRelations(ax::mojom::IntListAttribute attr,
- int32_t dst_id) override;
protected:
- explicit NativeViewAccessibilityBase(View* view);
+ explicit ViewAXPlatformNodeDelegate(View* view);
private:
void PopulateChildWidgetVector(std::vector<Widget*>* result_child_widgets);
+ void OnMenuItemActive();
+ void OnMenuStart();
+ void OnMenuEnd();
+
// We own this, but it is reference-counted on some platforms so we can't use
// a scoped_ptr. It is dereferenced in the destructor.
ui::AXPlatformNode* ax_node_;
mutable ui::AXNodeData data_;
- // This allows UI popups like autofill to act as if they are focused in the
- // exposed platform accessibility API, even though true focus remains in
- // underlying content.
- static int32_t fake_focus_view_id_;
+ // Levels of menu are currently open, e.g. 0: none, 1: top, 2: submenu ...
+ static int32_t menu_depth_;
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityBase);
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegate);
};
} // namespace views
-#endif // UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_BASE_H_
+#endif // UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index a098c1b5877..4a0df2df5cc 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.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/views/accessibility/native_view_accessibility_auralinux.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h"
#include <algorithm>
#include <memory>
@@ -15,8 +15,9 @@
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/ax_platform_node_auralinux.h"
-#include "ui/accessibility/platform/ax_platform_node_delegate.h"
+#include "ui/accessibility/platform/ax_platform_node_delegate_base.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/views/view.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
@@ -31,9 +32,8 @@ namespace {
// object. Every time we create an accessibility object for a View, we add its
// top-level widget to a vector so we can return the list of all top-level
// windows as children of this application object.
-class AuraLinuxApplication
- : public ui::AXPlatformNodeDelegate,
- public WidgetObserver {
+class AuraLinuxApplication : public ui::AXPlatformNodeDelegateBase,
+ public WidgetObserver {
public:
// Get the single instance of this class.
static AuraLinuxApplication* GetInstance() {
@@ -73,20 +73,7 @@ class AuraLinuxApplication
const ui::AXNodeData& GetData() const override { return data_; }
- const ui::AXTreeData& GetTreeData() const override {
- CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ());
- return empty_data;
- }
-
- gfx::NativeWindow GetTopLevelWidget() override { return nullptr; }
-
- gfx::NativeViewAccessible GetParent() override {
- return nullptr;
- }
-
- int GetChildCount() override {
- return static_cast<int>(widgets_.size());
- }
+ int GetChildCount() override { return static_cast<int>(widgets_.size()); }
gfx::NativeViewAccessible ChildAtIndex(int index) override {
if (index < 0 || index >= GetChildCount())
@@ -97,68 +84,6 @@ class AuraLinuxApplication
return widget->GetRootView()->GetNativeViewAccessible();
}
- gfx::Rect GetClippedScreenBoundsRect() const override { return gfx::Rect(); }
- gfx::Rect GetUnclippedScreenBoundsRect() const override {
- return gfx::Rect();
- }
-
- gfx::NativeViewAccessible HitTestSync(int x, int y) override {
- return nullptr;
- }
-
- gfx::NativeViewAccessible GetFocus() override {
- return nullptr;
- }
-
- bool IsOffscreen() const override {
- // TODO: need to implement.
- return false;
- }
-
- int GetIndexInParent() const override { return -1; }
-
- ui::AXPlatformNode* GetFromNodeID(int32_t id) override { return nullptr; }
-
- gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override {
- return gfx::kNullAcceleratedWidget;
- }
-
- int GetTableRowCount() const override { return 0; }
-
- int GetTableColCount() const override { return 0; }
-
- std::vector<int32_t> GetColHeaderNodeIds(int32_t col_index) const override {
- return std::vector<int32_t>();
- }
-
- std::vector<int32_t> GetRowHeaderNodeIds(int32_t row_index) const override {
- return std::vector<int32_t>();
- }
-
- int32_t GetCellId(int32_t row_index, int32_t col_index) const override {
- return -1;
- }
-
- int32_t CellIdToIndex(int32_t cell_id) const override { return -1; }
-
- int32_t CellIndexToId(int32_t cell_index) const override { return -1; }
-
- bool AccessibilityPerformAction(const ui::AXActionData& data) override {
- return false;
- }
-
- bool ShouldIgnoreHoveredStateForTesting() override { return false; }
-
- std::set<int32_t> GetReverseRelations(ax::mojom::IntAttribute attr,
- int32_t dst_id) override {
- return std::set<int32_t>();
- }
-
- std::set<int32_t> GetReverseRelations(ax::mojom::IntListAttribute attr,
- int32_t dst_id) override {
- return std::set<int32_t>();
- }
-
private:
friend struct base::DefaultSingletonTraits<AuraLinuxApplication>;
@@ -194,17 +119,18 @@ class AuraLinuxApplication
// static
std::unique_ptr<ViewAccessibility> ViewAccessibility::Create(View* view) {
AuraLinuxApplication::GetInstance()->RegisterWidget(view->GetWidget());
- return std::make_unique<NativeViewAccessibilityAuraLinux>(view);
+ return std::make_unique<ViewAXPlatformNodeDelegateAuraLinux>(view);
}
-NativeViewAccessibilityAuraLinux::NativeViewAccessibilityAuraLinux(View* view)
- : NativeViewAccessibilityBase(view) {}
+ViewAXPlatformNodeDelegateAuraLinux::ViewAXPlatformNodeDelegateAuraLinux(
+ View* view)
+ : ViewAXPlatformNodeDelegate(view) {}
-NativeViewAccessibilityAuraLinux::~NativeViewAccessibilityAuraLinux() {
-}
+ViewAXPlatformNodeDelegateAuraLinux::~ViewAXPlatformNodeDelegateAuraLinux() =
+ default;
-gfx::NativeViewAccessible NativeViewAccessibilityAuraLinux::GetParent() {
- gfx::NativeViewAccessible parent = NativeViewAccessibilityBase::GetParent();
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegateAuraLinux::GetParent() {
+ gfx::NativeViewAccessible parent = ViewAXPlatformNodeDelegate::GetParent();
if (!parent)
parent = AuraLinuxApplication::GetInstance()->GetNativeViewAccessible();
return parent;
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h
new file mode 100644
index 00000000000..d7bac7f22b2
--- /dev/null
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_AURALINUX_H_
+#define UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_AURALINUX_H_
+
+#include "base/macros.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+
+namespace views {
+
+class View;
+
+class ViewAXPlatformNodeDelegateAuraLinux : public ViewAXPlatformNodeDelegate {
+ public:
+ explicit ViewAXPlatformNodeDelegateAuraLinux(View* view);
+ ~ViewAXPlatformNodeDelegateAuraLinux() override;
+
+ // |ViewAXPlatformNodeDelegate| overrides:
+ gfx::NativeViewAccessible GetParent() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateAuraLinux);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_AURALINUX_H_
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.h
new file mode 100644
index 00000000000..75f679979b2
--- /dev/null
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_MAC_H_
+#define UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_MAC_H_
+
+#include "base/macros.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+
+namespace views {
+
+// Mac-specific accessibility class for |ViewAXPlatformNodeDelegate|.
+class ViewAXPlatformNodeDelegateMac : public ViewAXPlatformNodeDelegate {
+ public:
+ explicit ViewAXPlatformNodeDelegateMac(View* view);
+ ~ViewAXPlatformNodeDelegateMac() override;
+
+ // |ViewAXPlatformNodeDelegate| overrides:
+ gfx::NativeViewAccessible GetParent() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateMac);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_MAC_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_mac.mm b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
index be5b1978fde..993119fb012 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_mac.mm
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
@@ -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/views/accessibility/native_view_accessibility_mac.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate_mac.h"
#include <memory>
@@ -13,13 +13,15 @@ namespace views {
// static
std::unique_ptr<ViewAccessibility> ViewAccessibility::Create(View* view) {
- return std::make_unique<NativeViewAccessibilityMac>(view);
+ return std::make_unique<ViewAXPlatformNodeDelegateMac>(view);
}
-NativeViewAccessibilityMac::NativeViewAccessibilityMac(View* view)
- : NativeViewAccessibilityBase(view) {}
+ViewAXPlatformNodeDelegateMac::ViewAXPlatformNodeDelegateMac(View* view)
+ : ViewAXPlatformNodeDelegate(view) {}
-gfx::NativeViewAccessible NativeViewAccessibilityMac::GetParent() {
+ViewAXPlatformNodeDelegateMac::~ViewAXPlatformNodeDelegateMac() = default;
+
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetParent() {
if (view()->parent())
return view()->parent()->GetNativeViewAccessible();
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index c4904034e0c..8a702690827 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/views/accessibility/ax_aura_obj_cache.h"
#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
#include "ui/views/accessibility/ax_widget_obj_wrapper.h"
-#include "ui/views/accessibility/native_view_accessibility_base.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/test/views_test_base.h"
@@ -17,21 +18,23 @@
namespace views {
namespace test {
-class NativeViewAccessibilityTest;
-
namespace {
class TestButton : public Button {
public:
TestButton() : Button(NULL) {}
+ ~TestButton() override = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestButton);
};
} // namespace
-class NativeViewAccessibilityTest : public ViewsTestBase {
+class ViewAXPlatformNodeDelegateTest : public ViewsTestBase {
public:
- NativeViewAccessibilityTest() {}
- ~NativeViewAccessibilityTest() override {}
+ ViewAXPlatformNodeDelegateTest() = default;
+ ~ViewAXPlatformNodeDelegateTest() override = default;
void SetUp() override {
ViewsTestBase::SetUp();
@@ -57,33 +60,33 @@ class NativeViewAccessibilityTest : public ViewsTestBase {
ViewsTestBase::TearDown();
}
- NativeViewAccessibilityBase* button_accessibility() {
- return static_cast<NativeViewAccessibilityBase*>(
+ ViewAXPlatformNodeDelegate* button_accessibility() {
+ return static_cast<ViewAXPlatformNodeDelegate*>(
&button_->GetViewAccessibility());
}
- NativeViewAccessibilityBase* label_accessibility() {
- return static_cast<NativeViewAccessibilityBase*>(
+ ViewAXPlatformNodeDelegate* label_accessibility() {
+ return static_cast<ViewAXPlatformNodeDelegate*>(
&label_->GetViewAccessibility());
}
- bool SetFocused(NativeViewAccessibilityBase* view_accessibility,
- bool focused) {
+ bool SetFocused(ViewAXPlatformNodeDelegate* ax_delegate, bool focused) {
ui::AXActionData data;
data.action =
focused ? ax::mojom::Action::kFocus : ax::mojom::Action::kBlur;
- return view_accessibility->AccessibilityPerformAction(data);
+ return ax_delegate->AccessibilityPerformAction(data);
}
protected:
- Widget* widget_;
- TestButton* button_;
- Label* label_;
+ Widget* widget_ = nullptr;
+ Button* button_ = nullptr;
+ Label* label_ = nullptr;
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityTest);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateTest);
};
-TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) {
+TEST_F(ViewAXPlatformNodeDelegateTest, RoleShouldMatch) {
EXPECT_EQ(ax::mojom::Role::kButton, button_accessibility()->GetData().role);
// Since the label is a subview of |button_|, and the button is keyboard
// focusable, the label is assumed to form part of the button and not have a
@@ -97,7 +100,7 @@ TEST_F(NativeViewAccessibilityTest, RoleShouldMatch) {
label_accessibility()->GetData().role);
}
-TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) {
+TEST_F(ViewAXPlatformNodeDelegateTest, BoundsShouldMatch) {
gfx::Rect bounds =
gfx::ToEnclosingRect(button_accessibility()->GetData().location);
gfx::Rect screen_bounds =
@@ -107,7 +110,7 @@ TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) {
EXPECT_EQ(screen_bounds, bounds);
}
-TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
+TEST_F(ViewAXPlatformNodeDelegateTest, LabelIsChildOfButton) {
// |button_| is focusable, so |label_| (as its child) should be ignored.
EXPECT_EQ(View::FocusBehavior::ACCESSIBLE_ONLY, button_->focus_behavior());
EXPECT_EQ(1, button_accessibility()->GetChildCount());
@@ -126,7 +129,7 @@ TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
}
// Verify Views with invisible ancestors have ax::mojom::State::kInvisible.
-TEST_F(NativeViewAccessibilityTest, InvisibleViews) {
+TEST_F(ViewAXPlatformNodeDelegateTest, InvisibleViews) {
EXPECT_TRUE(widget_->IsVisible());
EXPECT_FALSE(
button_accessibility()->GetData().HasState(ax::mojom::State::kInvisible));
@@ -139,8 +142,9 @@ TEST_F(NativeViewAccessibilityTest, InvisibleViews) {
label_accessibility()->GetData().HasState(ax::mojom::State::kInvisible));
}
-TEST_F(NativeViewAccessibilityTest, WritableFocus) {
- // Make |button_| focusable, and focus/unfocus it via NativeViewAccessibility.
+TEST_F(ViewAXPlatformNodeDelegateTest, WritableFocus) {
+ // Make |button_| focusable, and focus/unfocus it via
+ // ViewAXPlatformNodeDelegate.
button_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
EXPECT_EQ(nullptr, button_->GetFocusManager()->GetFocusedView());
EXPECT_EQ(nullptr, button_accessibility()->GetFocus());
@@ -157,28 +161,19 @@ TEST_F(NativeViewAccessibilityTest, WritableFocus) {
EXPECT_FALSE(SetFocused(button_accessibility(), true));
}
-// Subclass of NativeViewAccessibility that destroys itself when its
-// parent widget is destroyed, for the purposes of making sure this
-// doesn't lead to a crash.
-class TestNativeViewAccessibility : public NativeViewAccessibilityBase {
- public:
- explicit TestNativeViewAccessibility(View* view)
- : NativeViewAccessibilityBase(view) {}
-};
-
#if defined(USE_AURA)
class DerivedTestView : public View {
public:
DerivedTestView() : View() {}
- ~DerivedTestView() override {}
+ ~DerivedTestView() override = default;
void OnBlur() override { SetVisible(false); }
};
class AxTestViewsDelegate : public TestViewsDelegate {
public:
- AxTestViewsDelegate() {}
- ~AxTestViewsDelegate() override {}
+ AxTestViewsDelegate() = default;
+ ~AxTestViewsDelegate() override = default;
void NotifyAccessibilityEvent(View* view,
ax::mojom::Event event_type) override {
@@ -192,10 +187,10 @@ class AxTestViewsDelegate : public TestViewsDelegate {
DISALLOW_COPY_AND_ASSIGN(AxTestViewsDelegate);
};
-class AXViewTest : public ViewsTestBase {
+class ViewAccessibilityTest : public ViewsTestBase {
public:
- AXViewTest() {}
- ~AXViewTest() override {}
+ ViewAccessibilityTest() = default;
+ ~ViewAccessibilityTest() override = default;
void SetUp() override {
std::unique_ptr<TestViewsDelegate> views_delegate(
new AxTestViewsDelegate());
@@ -206,7 +201,7 @@ class AXViewTest : public ViewsTestBase {
// Check if the destruction of the widget ends successfully if |view|'s
// visibility changed during destruction.
-TEST_F(AXViewTest, LayoutCalledInvalidateRootView) {
+TEST_F(ViewAccessibilityTest, LayoutCalledInvalidateRootView) {
std::unique_ptr<Widget> widget(new Widget);
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.cc
index 7491a60f6f0..68b4118be9d 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_win.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.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/views/accessibility/native_view_accessibility_win.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate_win.h"
#include <oleacc.h>
@@ -24,6 +24,7 @@
#include "ui/base/win/atl_module.h"
#include "ui/display/win/screen_win.h"
#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/win/hwnd_util.h"
#include "ui/wm/core/window_util.h"
@@ -48,15 +49,15 @@ aura::Window* GetWindowParentIncludingTransient(aura::Window* window) {
// static
std::unique_ptr<ViewAccessibility> ViewAccessibility::Create(View* view) {
- return std::make_unique<NativeViewAccessibilityWin>(view);
+ return std::make_unique<ViewAXPlatformNodeDelegateWin>(view);
}
-NativeViewAccessibilityWin::NativeViewAccessibilityWin(View* view)
- : NativeViewAccessibilityBase(view) {}
+ViewAXPlatformNodeDelegateWin::ViewAXPlatformNodeDelegateWin(View* view)
+ : ViewAXPlatformNodeDelegate(view) {}
-NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {}
+ViewAXPlatformNodeDelegateWin::~ViewAXPlatformNodeDelegateWin() = default;
-gfx::NativeViewAccessible NativeViewAccessibilityWin::GetParent() {
+gfx::NativeViewAccessible ViewAXPlatformNodeDelegateWin::GetParent() {
// If the View has a parent View, return that View's IAccessible.
if (view()->parent())
return view()->parent()->GetNativeViewAccessible();
@@ -96,16 +97,16 @@ gfx::NativeViewAccessible NativeViewAccessibilityWin::GetParent() {
}
gfx::AcceleratedWidget
-NativeViewAccessibilityWin::GetTargetForNativeAccessibilityEvent() {
+ViewAXPlatformNodeDelegateWin::GetTargetForNativeAccessibilityEvent() {
return HWNDForView(view());
}
-gfx::Rect NativeViewAccessibilityWin::GetClippedScreenBoundsRect() const {
+gfx::Rect ViewAXPlatformNodeDelegateWin::GetClippedScreenBoundsRect() const {
// We could optionally add clipping here if ever needed.
return GetUnclippedScreenBoundsRect();
}
-gfx::Rect NativeViewAccessibilityWin::GetUnclippedScreenBoundsRect() const {
+gfx::Rect ViewAXPlatformNodeDelegateWin::GetUnclippedScreenBoundsRect() const {
gfx::Rect bounds = view()->GetBoundsInScreen();
return display::win::ScreenWin::DIPToScreenRect(HWNDForView(view()), bounds);
}
diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.h
new file mode 100644
index 00000000000..2715eae70bd
--- /dev/null
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win.h
@@ -0,0 +1,32 @@
+// 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_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_WIN_H_
+#define UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_WIN_H_
+
+#include "base/macros.h"
+#include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+
+namespace views {
+
+class View;
+
+class ViewAXPlatformNodeDelegateWin : public ViewAXPlatformNodeDelegate {
+ public:
+ explicit ViewAXPlatformNodeDelegateWin(View* view);
+ ~ViewAXPlatformNodeDelegateWin() override;
+
+ // |ViewAXPlatformNodeDelegate| overrides:
+ gfx::NativeViewAccessible GetParent() override;
+ gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
+ gfx::Rect GetClippedScreenBoundsRect() const override;
+ gfx::Rect GetUnclippedScreenBoundsRect() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateWin);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ACCESSIBILITY_VIEW_AX_PLATFORM_NODE_DELEGATE_WIN_H_
diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc
index 0b1c5dc7b55..b279a468af7 100644
--- a/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc
+++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate_win_unittest.cc
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ui/views/accessibility/view_ax_platform_node_delegate_win.h"
+
#include <oleacc.h>
#include <wrl/client.h>
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_variant.h"
#include "third_party/iaccessible2/ia2_api_all.h"
-#include "ui/views/accessibility/native_view_accessibility_base.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h"
@@ -43,25 +44,23 @@ bool IsSameObject(T* left, U* right) {
} // namespace
-class NativeViewAccessibilityWinTest : public ViewsTestBase {
+class ViewAXPlatformNodeDelegateWinTest : public ViewsTestBase {
public:
- NativeViewAccessibilityWinTest() {}
- ~NativeViewAccessibilityWinTest() override {}
+ ViewAXPlatformNodeDelegateWinTest() = default;
+ ~ViewAXPlatformNodeDelegateWinTest() override = default;
protected:
void GetIAccessible2InterfaceForView(View* view, IAccessible2_2** result) {
ComPtr<IAccessible> view_accessible(view->GetNativeViewAccessible());
ComPtr<IServiceProvider> service_provider;
ASSERT_EQ(S_OK, view_accessible.CopyTo(service_provider.GetAddressOf()));
- ASSERT_EQ(S_OK,
- service_provider->QueryService(IID_IAccessible2_2, result));
+ ASSERT_EQ(S_OK, service_provider->QueryService(IID_IAccessible2_2, result));
}
};
-TEST_F(NativeViewAccessibilityWinTest, TextfieldAccessibility) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, TextfieldAccessibility) {
Widget widget;
- Widget::InitParams init_params =
- CreateParams(Widget::InitParams::TYPE_POPUP);
+ Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(init_params);
@@ -88,13 +87,13 @@ TEST_F(NativeViewAccessibilityWinTest, TextfieldAccessibility) {
ScopedBstr name;
ScopedVariant childid_self(CHILDID_SELF);
- ASSERT_EQ(S_OK, textfield_accessible->get_accName(
- childid_self, name.Receive()));
+ ASSERT_EQ(S_OK,
+ textfield_accessible->get_accName(childid_self, name.Receive()));
ASSERT_STREQ(L"Name", name);
ScopedBstr value;
- ASSERT_EQ(S_OK, textfield_accessible->get_accValue(
- childid_self, value.Receive()));
+ ASSERT_EQ(S_OK,
+ textfield_accessible->get_accValue(childid_self, value.Receive()));
ASSERT_STREQ(L"Value", value);
ScopedBstr new_value(L"New value");
@@ -103,7 +102,7 @@ TEST_F(NativeViewAccessibilityWinTest, TextfieldAccessibility) {
ASSERT_STREQ(L"New value", textfield->text().c_str());
}
-TEST_F(NativeViewAccessibilityWinTest, TextfieldAssociatedLabel) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, TextfieldAssociatedLabel) {
Widget widget;
Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -153,25 +152,25 @@ TEST_F(NativeViewAccessibilityWinTest, TextfieldAssociatedLabel) {
EXPECT_EQ(ROLE_SYSTEM_STATICTEXT, V_I4(role.ptr()));
}
-// A subclass of NativeViewAccessibilityWinTest that we run twice,
+// A subclass of ViewAXPlatformNodeDelegateWinTest that we run twice,
// first where we create an transient child widget (child = false), the second
// time where we create a child widget (child = true).
-class NativeViewAccessibilityWinTestWithBoolChildFlag
- : public NativeViewAccessibilityWinTest,
+class ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag
+ : public ViewAXPlatformNodeDelegateWinTest,
public testing::WithParamInterface<bool> {
public:
- NativeViewAccessibilityWinTestWithBoolChildFlag() {}
- ~NativeViewAccessibilityWinTestWithBoolChildFlag() override {}
+ ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag() {}
+ ~ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag() override {}
private:
- DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityWinTestWithBoolChildFlag);
+ DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag);
};
INSTANTIATE_TEST_CASE_P(,
- NativeViewAccessibilityWinTestWithBoolChildFlag,
+ ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag,
testing::Bool());
-TEST_P(NativeViewAccessibilityWinTestWithBoolChildFlag, AuraChildWidgets) {
+TEST_P(ViewAXPlatformNodeDelegateWinTestWithBoolChildFlag, AuraChildWidgets) {
// Create the parent widget.
Widget widget;
Widget::InitParams init_params =
@@ -251,10 +250,9 @@ TEST_P(NativeViewAccessibilityWinTestWithBoolChildFlag, AuraChildWidgets) {
}
// Flaky on Windows: https://crbug.com/461837.
-TEST_F(NativeViewAccessibilityWinTest, DISABLED_RetrieveAllAlerts) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, DISABLED_RetrieveAllAlerts) {
Widget widget;
- Widget::InitParams init_params =
- CreateParams(Widget::InitParams::TYPE_POPUP);
+ Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(init_params);
@@ -285,7 +283,7 @@ TEST_F(NativeViewAccessibilityWinTest, DISABLED_RetrieveAllAlerts) {
IUnknown** targets;
long n_targets;
ASSERT_EQ(S_FALSE, root_view_accessible->get_relationTargetsOfType(
- alerts_bstr, 0, &targets, &n_targets));
+ alerts_bstr, 0, &targets, &n_targets));
ASSERT_EQ(0, n_targets);
// Fire alert events on the infobars.
@@ -294,7 +292,7 @@ TEST_F(NativeViewAccessibilityWinTest, DISABLED_RetrieveAllAlerts) {
// Now calling get_relationTargetsOfType should retrieve the alerts.
ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
- alerts_bstr, 0, &targets, &n_targets));
+ alerts_bstr, 0, &targets, &n_targets));
ASSERT_EQ(2, n_targets);
ASSERT_TRUE(IsSameObject(infobar_accessible.Get(), targets[0]));
ASSERT_TRUE(IsSameObject(infobar2_accessible.Get(), targets[1]));
@@ -302,7 +300,7 @@ TEST_F(NativeViewAccessibilityWinTest, DISABLED_RetrieveAllAlerts) {
// If we set max_targets to 1, we should only get the first one.
ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
- alerts_bstr, 1, &targets, &n_targets));
+ alerts_bstr, 1, &targets, &n_targets));
ASSERT_EQ(1, n_targets);
ASSERT_TRUE(IsSameObject(infobar_accessible.Get(), targets[0]));
CoTaskMemFree(targets);
@@ -310,14 +308,14 @@ TEST_F(NativeViewAccessibilityWinTest, DISABLED_RetrieveAllAlerts) {
// If we delete the first view, we should only get the second one now.
delete infobar;
ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
- alerts_bstr, 0, &targets, &n_targets));
+ alerts_bstr, 0, &targets, &n_targets));
ASSERT_EQ(1, n_targets);
ASSERT_TRUE(IsSameObject(infobar2_accessible.Get(), targets[0]));
CoTaskMemFree(targets);
}
// Test trying to retrieve child widgets during window close does not crash.
-TEST_F(NativeViewAccessibilityWinTest, GetAllOwnedWidgetsCrash) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, GetAllOwnedWidgetsCrash) {
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
@@ -332,7 +330,7 @@ TEST_F(NativeViewAccessibilityWinTest, GetAllOwnedWidgetsCrash) {
EXPECT_EQ(1L, child_count);
}
-TEST_F(NativeViewAccessibilityWinTest, WindowHasRoleApplication) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, WindowHasRoleApplication) {
// We expect that our internal window object does not expose
// ROLE_SYSTEM_WINDOW, but ROLE_SYSTEM_PANE instead.
Widget widget;
@@ -350,7 +348,7 @@ TEST_F(NativeViewAccessibilityWinTest, WindowHasRoleApplication) {
EXPECT_EQ(ROLE_SYSTEM_PANE, V_I4(role.ptr()));
}
-TEST_F(NativeViewAccessibilityWinTest, Overrides) {
+TEST_F(ViewAXPlatformNodeDelegateWinTest, Overrides) {
// We expect that our internal window object does not expose
// ROLE_SYSTEM_WINDOW, but ROLE_SYSTEM_PANE instead.
Widget widget;
diff --git a/chromium/ui/views/animation/bounds_animator.cc b/chromium/ui/views/animation/bounds_animator.cc
index 13180c3f2e2..fc4e04c1854 100644
--- a/chromium/ui/views/animation/bounds_animator.cc
+++ b/chromium/ui/views/animation/bounds_animator.cc
@@ -11,21 +11,10 @@
#include "ui/views/animation/bounds_animator_observer.h"
#include "ui/views/view.h"
-// Duration in milliseconds for animations.
-static const int kDefaultAnimationDuration = 200;
-
-using gfx::Animation;
-using gfx::AnimationContainer;
-using gfx::SlideAnimation;
-using gfx::Tween;
-
namespace views {
BoundsAnimator::BoundsAnimator(View* parent)
- : parent_(parent),
- container_(new AnimationContainer()),
- animation_duration_ms_(kDefaultAnimationDuration),
- tween_type_(Tween::EASE_OUT) {
+ : parent_(parent), container_(new gfx::AnimationContainer()) {
container_->set_observer(this);
}
@@ -36,8 +25,8 @@ BoundsAnimator::~BoundsAnimator() {
// Delete all the animations, but don't remove any child views. We assume the
// view owns us and is going to be deleted anyway.
- for (ViewToDataMap::iterator i = data_.begin(); i != data_.end(); ++i)
- CleanupData(false, &(i->second), i->first);
+ for (auto& entry : data_)
+ CleanupData(false, &entry.second);
}
void BoundsAnimator::AnimateViewTo(View* view, const gfx::Rect& target) {
@@ -47,8 +36,8 @@ void BoundsAnimator::AnimateViewTo(View* view, const gfx::Rect& target) {
Data existing_data;
if (IsAnimating(view)) {
- // Don't immediatly delete the animation, that might trigger a callback from
- // the animationcontainer.
+ // Don't immediately delete the animation, that might trigger a callback
+ // from the animation container.
existing_data = RemoveFromMaps(view);
}
@@ -66,38 +55,37 @@ void BoundsAnimator::AnimateViewTo(View* view, const gfx::Rect& target) {
data.animation->Show();
- CleanupData(true, &existing_data, nullptr);
+ CleanupData(true, &existing_data);
}
void BoundsAnimator::SetTargetBounds(View* view, const gfx::Rect& target) {
- if (!IsAnimating(view)) {
+ const auto i = data_.find(view);
+ if (i == data_.end())
AnimateViewTo(view, target);
- return;
- }
-
- data_[view].target_bounds = target;
+ else
+ i->second.target_bounds = target;
}
-gfx::Rect BoundsAnimator::GetTargetBounds(View* view) {
- if (!IsAnimating(view))
- return view->bounds();
- return data_[view].target_bounds;
+gfx::Rect BoundsAnimator::GetTargetBounds(const View* view) const {
+ const auto i = data_.find(view);
+ return (i == data_.end()) ? view->bounds() : i->second.target_bounds;
}
void BoundsAnimator::SetAnimationForView(
View* view,
- std::unique_ptr<SlideAnimation> animation) {
+ std::unique_ptr<gfx::SlideAnimation> animation) {
DCHECK(animation);
- if (!IsAnimating(view))
+ const auto i = data_.find(view);
+ if (i == data_.end())
return;
// We delay deleting the animation until the end so that we don't prematurely
// send out notification that we're done.
- std::unique_ptr<Animation> old_animation = ResetAnimationForView(view);
+ std::unique_ptr<gfx::Animation> old_animation = ResetAnimationForView(view);
- SlideAnimation* animation_ptr = animation.get();
- data_[view].animation = std::move(animation);
+ gfx::SlideAnimation* animation_ptr = animation.get();
+ i->second.animation = std::move(animation);
animation_to_view_[animation_ptr] = view;
animation_ptr->set_delegate(this);
@@ -105,23 +93,24 @@ void BoundsAnimator::SetAnimationForView(
animation_ptr->Show();
}
-const SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) {
- return !IsAnimating(view) ? nullptr : data_[view].animation.get();
+const gfx::SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) {
+ const auto i = data_.find(view);
+ return (i == data_.end()) ? nullptr : i->second.animation.get();
}
void BoundsAnimator::SetAnimationDelegate(
View* view,
std::unique_ptr<AnimationDelegate> delegate) {
- DCHECK(IsAnimating(view));
+ const auto i = data_.find(view);
+ DCHECK(i != data_.end());
- data_[view].delegate = std::move(delegate);
+ i->second.delegate = std::move(delegate);
}
void BoundsAnimator::StopAnimatingView(View* view) {
- if (!IsAnimating(view))
- return;
-
- data_[view].animation->Stop();
+ const auto i = data_.find(view);
+ if (i != data_.end())
+ i->second.animation->Stop();
}
bool BoundsAnimator::IsAnimating(View* view) const {
@@ -156,8 +145,7 @@ void BoundsAnimator::RemoveObserver(BoundsAnimatorObserver* observer) {
}
std::unique_ptr<gfx::SlideAnimation> BoundsAnimator::CreateAnimation() {
- std::unique_ptr<gfx::SlideAnimation> animation =
- std::make_unique<SlideAnimation>(this);
+ auto animation = std::make_unique<gfx::SlideAnimation>(this);
animation->SetContainer(container_.get());
animation->SetSlideDuration(animation_duration_ms_);
animation->SetTweenType(tween_type_);
@@ -170,16 +158,17 @@ BoundsAnimator::Data& BoundsAnimator::Data::operator=(Data&&) = default;
BoundsAnimator::Data::~Data() = default;
BoundsAnimator::Data BoundsAnimator::RemoveFromMaps(View* view) {
- DCHECK(data_.count(view) > 0);
- DCHECK(animation_to_view_.count(data_[view].animation.get()) > 0);
+ const auto i = data_.find(view);
+ DCHECK(i != data_.end());
+ DCHECK(animation_to_view_.count(i->second.animation.get()) > 0);
- Data old_data = std::move(data_[view]);
+ Data old_data = std::move(i->second);
data_.erase(view);
animation_to_view_.erase(old_data.animation.get());
return old_data;
}
-void BoundsAnimator::CleanupData(bool send_cancel, Data* data, View* view) {
+void BoundsAnimator::CleanupData(bool send_cancel, Data* data) {
if (send_cancel && data->delegate)
data->delegate->AnimationCanceled(data->animation.get());
@@ -191,11 +180,14 @@ void BoundsAnimator::CleanupData(bool send_cancel, Data* data, View* view) {
}
}
-std::unique_ptr<Animation> BoundsAnimator::ResetAnimationForView(View* view) {
- if (!IsAnimating(view))
+std::unique_ptr<gfx::Animation> BoundsAnimator::ResetAnimationForView(
+ View* view) {
+ const auto i = data_.find(view);
+ if (i == data_.end())
return nullptr;
- std::unique_ptr<Animation> old_animation = std::move(data_[view].animation);
+ std::unique_ptr<gfx::Animation> old_animation =
+ std::move(i->second.animation);
animation_to_view_.erase(old_animation.get());
// Reset the delegate so that we don't attempt any processing when the
// animation calls us back.
@@ -203,7 +195,7 @@ std::unique_ptr<Animation> BoundsAnimator::ResetAnimationForView(View* view) {
return old_animation;
}
-void BoundsAnimator::AnimationEndedOrCanceled(const Animation* animation,
+void BoundsAnimator::AnimationEndedOrCanceled(const gfx::Animation* animation,
AnimationEndType type) {
DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());
@@ -222,10 +214,10 @@ void BoundsAnimator::AnimationEndedOrCanceled(const Animation* animation,
}
}
- CleanupData(false, &data, view);
+ CleanupData(false, &data);
}
-void BoundsAnimator::AnimationProgressed(const Animation* animation) {
+void BoundsAnimator::AnimationProgressed(const gfx::Animation* animation) {
DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());
View* view = animation_to_view_[animation];
@@ -247,16 +239,16 @@ void BoundsAnimator::AnimationProgressed(const Animation* animation) {
data.delegate->AnimationProgressed(animation);
}
-void BoundsAnimator::AnimationEnded(const Animation* animation) {
+void BoundsAnimator::AnimationEnded(const gfx::Animation* animation) {
AnimationEndedOrCanceled(animation, ANIMATION_ENDED);
}
-void BoundsAnimator::AnimationCanceled(const Animation* animation) {
+void BoundsAnimator::AnimationCanceled(const gfx::Animation* animation) {
AnimationEndedOrCanceled(animation, ANIMATION_CANCELED);
}
void BoundsAnimator::AnimationContainerProgressed(
- AnimationContainer* container) {
+ gfx::AnimationContainer* container) {
if (!repaint_bounds_.IsEmpty()) {
// Adjust for rtl.
repaint_bounds_.set_x(parent_->GetMirroredXWithWidthInView(
@@ -276,7 +268,7 @@ void BoundsAnimator::AnimationContainerProgressed(
}
}
-void BoundsAnimator::AnimationContainerEmpty(AnimationContainer* container) {
-}
+void BoundsAnimator::AnimationContainerEmpty(
+ gfx::AnimationContainer* container) {}
} // namespace views
diff --git a/chromium/ui/views/animation/bounds_animator.h b/chromium/ui/views/animation/bounds_animator.h
index 5fdb04dd713..c0a6501fc0d 100644
--- a/chromium/ui/views/animation/bounds_animator.h
+++ b/chromium/ui/views/animation/bounds_animator.h
@@ -56,7 +56,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
// Returns the target bounds for the specified view. If |view| is not
// animating its current bounds is returned.
- gfx::Rect GetTargetBounds(View* view);
+ gfx::Rect GetTargetBounds(const View* view) const;
// Sets the animation for the specified view.
void SetAnimationForView(View* view,
@@ -127,7 +127,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
ANIMATION_CANCELED
};
- typedef std::map<View*, Data> ViewToDataMap;
+ typedef std::map<const View*, Data> ViewToDataMap;
typedef std::map<const gfx::Animation*, View*> AnimationToViewMap;
@@ -137,7 +137,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
// Does the necessary cleanup for |data|. If |send_cancel| is true and a
// delegate has been installed on |data| AnimationCanceled is invoked on it.
- void CleanupData(bool send_cancel, Data* data, View* view);
+ void CleanupData(bool send_cancel, Data* data);
// Used when changing the animation for a view. This resets the maps for
// the animation used by view and returns the current animation. Ownership
@@ -178,9 +178,9 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
// to repaint these bounds.
gfx::Rect repaint_bounds_;
- int animation_duration_ms_;
+ int animation_duration_ms_ = 200;
- gfx::Tween::Type tween_type_;
+ gfx::Tween::Type tween_type_ = gfx::Tween::EASE_OUT;
DISALLOW_COPY_AND_ASSIGN(BoundsAnimator);
};
diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc
index 220240b02cb..1ce501ed80f 100644
--- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc
+++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc
@@ -15,6 +15,7 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/views/animation/ink_drop_util.h"
+#include "ui/views/style/platform_style.h"
namespace {
@@ -447,8 +448,10 @@ float FloodFillInkDropRipple::MaxDistanceToCorners(
// Returns the InkDropState sub animation duration for the given |state|.
base::TimeDelta FloodFillInkDropRipple::GetAnimationDuration(int state) {
- if (!gfx::Animation::ShouldRenderRichAnimation())
+ if (!PlatformStyle::kUseRipples ||
+ !gfx::Animation::ShouldRenderRichAnimation()) {
return base::TimeDelta();
+ }
int state_override = state;
// Override the requested state if needed.
diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc
index 1d024628715..f21c750bd1f 100644
--- a/chromium/ui/views/animation/ink_drop_host_view.cc
+++ b/chromium/ui/views/animation/ink_drop_host_view.cc
@@ -118,8 +118,7 @@ gfx::Size InkDropHostView::CalculateLargeInkDropSize(
InkDropHostView::InkDropHostView()
: ink_drop_mode_(InkDropMode::OFF),
ink_drop_(nullptr),
- ink_drop_visible_opacity_(
- PlatformStyle::kUseRipples ? kInkDropVisibleOpacity : 0),
+ ink_drop_visible_opacity_(kInkDropVisibleOpacity),
ink_drop_small_corner_radius_(kInkDropSmallCornerRadius),
ink_drop_large_corner_radius_(kInkDropLargeCornerRadius),
old_paint_to_layer_(false),
diff --git a/chromium/ui/views/animation/ink_drop_impl.cc b/chromium/ui/views/animation/ink_drop_impl.cc
index 96328205c27..7e00a268fd2 100644
--- a/chromium/ui/views/animation/ink_drop_impl.cc
+++ b/chromium/ui/views/animation/ink_drop_impl.cc
@@ -286,7 +286,7 @@ class InkDropImpl::HideHighlightOnRippleHiddenState
// The timer used to delay the highlight fade in after an ink drop ripple
// animation.
- std::unique_ptr<base::Timer> highlight_after_ripple_timer_;
+ std::unique_ptr<base::OneShotTimer> highlight_after_ripple_timer_;
DISALLOW_COPY_AND_ASSIGN(HideHighlightOnRippleHiddenState);
};
@@ -621,9 +621,7 @@ void InkDropImpl::SetAutoHighlightMode(AutoHighlightMode auto_highlight_mode) {
}
void InkDropImpl::SetAutoHighlightModeForPlatform() {
- SetAutoHighlightMode(PlatformStyle::kUseRipples
- ? AutoHighlightMode::HIDE_ON_RIPPLE
- : AutoHighlightMode::SHOW_ON_RIPPLE);
+ SetAutoHighlightMode(AutoHighlightMode::HIDE_ON_RIPPLE);
}
void InkDropImpl::HostSizeChanged(const gfx::Size& new_size) {
diff --git a/chromium/ui/views/animation/square_ink_drop_ripple.cc b/chromium/ui/views/animation/square_ink_drop_ripple.cc
index 276b5d5f732..c4a25252b0e 100644
--- a/chromium/ui/views/animation/square_ink_drop_ripple.cc
+++ b/chromium/ui/views/animation/square_ink_drop_ripple.cc
@@ -17,6 +17,7 @@
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
+#include "ui/views/style/platform_style.h"
#include "ui/views/view.h"
namespace views {
@@ -129,8 +130,10 @@ int kAnimationDurationInMs[] = {
// Returns the InkDropState sub animation duration for the given |state|.
base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) {
- if (!gfx::Animation::ShouldRenderRichAnimation())
+ if (!PlatformStyle::kUseRipples ||
+ !gfx::Animation::ShouldRenderRichAnimation()) {
return base::TimeDelta();
+ }
return base::TimeDelta::FromMilliseconds(
(InkDropRipple::UseFastAnimations()
diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc
index 6961a92ef6f..b746bb4c231 100644
--- a/chromium/ui/views/bubble/bubble_border.cc
+++ b/chromium/ui/views/bubble/bubble_border.cc
@@ -20,6 +20,7 @@
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_paint_util.h"
#include "ui/resources/grit/ui_resources.h"
+#include "ui/views/layout/layout_provider.h"
#include "ui/views/painter.h"
#include "ui/views/resources/grit/views_resources.h"
#include "ui/views/view.h"
@@ -423,8 +424,7 @@ const gfx::ShadowValues& BubbleBorder::GetShadowValues(
gfx::ShadowValues shadows;
if (elevation.has_value()) {
DCHECK(elevation.value() >= 0);
- shadows = gfx::ShadowValues(
- gfx::ShadowValue::MakeMdShadowValues(elevation.value()));
+ shadows = LayoutProvider::Get()->MakeShadowValues(elevation.value());
} else {
constexpr int kSmallShadowVerticalOffset = 2;
constexpr int kSmallShadowBlur = 4;
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc
index eb5a9fd9b42..0bfe54962a2 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate.cc
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc
@@ -136,15 +136,17 @@ NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView(
Widget* widget) {
BubbleFrameView* frame = new BubbleDialogFrameView(title_margins_);
+ LayoutProvider* provider = LayoutProvider::Get();
frame->set_footnote_margins(
- LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
+ provider->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
frame->SetFootnoteView(CreateFootnoteView());
BubbleBorder::Arrow adjusted_arrow = arrow();
if (base::i18n::IsRTL() && mirror_arrow_in_rtl_)
adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow);
- frame->SetBubbleBorder(std::unique_ptr<BubbleBorder>(
- new BubbleBorder(adjusted_arrow, shadow(), color())));
+ std::unique_ptr<BubbleBorder> border =
+ std::make_unique<BubbleBorder>(adjusted_arrow, shadow(), color());
+ frame->SetBubbleBorder(std::move(border));
return frame;
}
@@ -323,24 +325,32 @@ void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) {
// change as well.
if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) {
if (anchor_widget()) {
+ if (GetWidget() && GetWidget()->IsVisible())
+ UpdateAnchorWidgetRenderState(false);
anchor_widget_->RemoveObserver(this);
anchor_widget_ = NULL;
}
if (anchor_view) {
anchor_widget_ = anchor_view->GetWidget();
- if (anchor_widget_)
+ if (anchor_widget_) {
anchor_widget_->AddObserver(this);
+ UpdateAnchorWidgetRenderState(GetWidget() && GetWidget()->IsVisible());
+ }
}
}
anchor_view_tracker_->SetView(anchor_view);
- // Do not update anchoring for NULL views; this could indicate that our
- // NativeWindow is being destroyed, so it would be dangerous for us to update
- // our anchor bounds at that point. (It's safe to skip this, since if we were
- // to update the bounds when |anchor_view| is NULL, the bubble won't move.)
- if (anchor_view && GetWidget())
+ if (anchor_view && GetWidget()) {
+ // Do not update anchoring for NULL views; this could indicate
+ // that our NativeWindow is being destroyed, so it would be
+ // dangerous for us to update our anchor bounds at that
+ // point. (It's safe to skip this, since if we were to update the
+ // bounds when |anchor_view| is NULL, the bubble won't move.)
OnAnchorBoundsChanged();
+
+ EnableFocusTraversalFromAnchorView();
+ }
}
void BubbleDialogDelegateView::SetAnchorRect(const gfx::Rect& rect) {
@@ -385,10 +395,8 @@ void BubbleDialogDelegateView::UpdateColorsFromTheme(
void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget,
bool visible) {
- if (widget == GetWidget() && anchor_widget() &&
- anchor_widget()->GetTopLevelWidget()) {
- anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible);
- }
+ if (widget == GetWidget())
+ UpdateAnchorWidgetRenderState(visible);
// Fire ax::mojom::Event::kAlert for bubbles marked as
// ax::mojom::Role::kAlertDialog; this instructs accessibility tools to read
@@ -408,4 +416,11 @@ void BubbleDialogDelegateView::OnDeactivate() {
GetWidget()->Close();
}
+void BubbleDialogDelegateView::UpdateAnchorWidgetRenderState(bool visible) {
+ if (!anchor_widget() || !anchor_widget()->GetTopLevelWidget())
+ return;
+
+ anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible);
+}
+
} // namespace views
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h
index c564cb85dcf..216578e0961 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate.h
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h
@@ -187,6 +187,9 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
// Called when a deactivation is detected.
void OnDeactivate();
+ // When a bubble is visible, the anchor widget should always render as active.
+ void UpdateAnchorWidgetRenderState(bool visible);
+
// A flag controlling bubble closure on deactivation.
bool close_on_deactivate_;
diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc
index 129caea4902..6b0e5d89b70 100644
--- a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc
+++ b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc
@@ -36,6 +36,8 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
}
~TestBubbleDialogDelegateView() override {}
+ using BubbleDialogDelegateView::SetAnchorView;
+
// BubbleDialogDelegateView overrides:
View* GetInitiallyFocusedView() override { return view_; }
gfx::Size CalculatePreferredSize() const override {
@@ -476,4 +478,20 @@ TEST_F(BubbleDialogDelegateTest, StyledLabelTitle) {
bubble_widget->GetWindowBoundsInScreen().height());
}
+TEST_F(BubbleDialogDelegateTest, VisibleAnchorChanges) {
+ std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
+ TestBubbleDialogDelegateView* bubble_delegate =
+ new TestBubbleDialogDelegateView(nullptr);
+ bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
+
+ Widget* bubble_widget =
+ BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+ bubble_widget->Show();
+ EXPECT_FALSE(anchor_widget->IsAlwaysRenderAsActive());
+ bubble_delegate->SetAnchorView(anchor_widget->GetContentsView());
+ EXPECT_TRUE(anchor_widget->IsAlwaysRenderAsActive());
+
+ bubble_widget->Hide();
+}
+
} // namespace views
diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc
index 5021792e327..50fce2ddc25 100644
--- a/chromium/ui/views/bubble/bubble_frame_view.cc
+++ b/chromium/ui/views/bubble/bubble_frame_view.cc
@@ -17,6 +17,7 @@
#include "ui/compositor/paint_recorder.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
+#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/path.h"
#include "ui/gfx/skia_util.h"
@@ -41,12 +42,6 @@ namespace views {
namespace {
-// Background color of the footnote view.
-constexpr SkColor kFootnoteBackgroundColor = SkColorSetRGB(250, 250, 250);
-
-// Color of the top border of the footnote.
-constexpr SkColor kFootnoteBorderColor = SkColorSetRGB(235, 235, 235);
-
// Get the |vertical| or horizontal amount that |available_bounds| overflows
// |window_bounds|.
int GetOffScreenLength(const gfx::Rect& available_bounds,
@@ -273,7 +268,8 @@ void BubbleFrameView::UpdateWindowIcon() {
void BubbleFrameView::UpdateWindowTitle() {
if (default_title_) {
const WidgetDelegate* delegate = GetWidget()->widget_delegate();
- default_title_->SetVisible(delegate->ShouldShowWindowTitle());
+ default_title_->SetVisible(delegate->ShouldShowWindowTitle() &&
+ !delegate->GetWindowTitle().empty());
default_title_->SetText(delegate->GetWindowTitle());
} // custom_title_'s updates are handled by its creator.
}
@@ -413,6 +409,9 @@ void BubbleFrameView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
void BubbleFrameView::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
+ if (details.is_add && details.child == this)
+ OnThemeChanged();
+
if (!details.is_add && details.parent == footnote_container_ &&
footnote_container_->child_count() == 1 &&
details.child == footnote_container_->child_at(0)) {
@@ -464,9 +463,9 @@ void BubbleFrameView::SetFootnoteView(View* view) {
footnote_container_->SetLayoutManager(
std::make_unique<BoxLayout>(BoxLayout::kVertical, footnote_margins_, 0));
footnote_container_->SetBackground(
- CreateSolidBackground(kFootnoteBackgroundColor));
+ CreateSolidBackground(gfx::kGoogleGrey050));
footnote_container_->SetBorder(
- CreateSolidSidedBorder(1, 0, 0, 0, kFootnoteBorderColor));
+ CreateSolidSidedBorder(1, 0, 0, 0, gfx::kGoogleGrey200));
footnote_container_->AddChildView(view);
footnote_container_->SetVisible(view->visible());
AddChildView(footnote_container_);
diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
index 9b16c5a3247..bd891dc540b 100644
--- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -789,6 +789,7 @@ TEST_F(BubbleFrameViewTest, LayoutWithIcon) {
delegate.SetAnchorView(anchor.widget().GetContentsView());
SkBitmap bitmap;
bitmap.allocN32Pixels(20, 80);
+ bitmap.eraseColor(SK_ColorYELLOW);
delegate.set_icon(gfx::ImageSkia::CreateFrom1xBitmap(bitmap));
Widget* widget = BubbleDialogDelegateView::CreateBubble(&delegate);
diff --git a/chromium/ui/views/bubble/tooltip_icon.cc b/chromium/ui/views/bubble/tooltip_icon.cc
index b25fd527510..2b88baf6279 100644
--- a/chromium/ui/views/bubble/tooltip_icon.cc
+++ b/chromium/ui/views/bubble/tooltip_icon.cc
@@ -14,8 +14,9 @@
namespace views {
-TooltipIcon::TooltipIcon(const base::string16& tooltip)
+TooltipIcon::TooltipIcon(const base::string16& tooltip, int tooltip_icon_size)
: tooltip_(tooltip),
+ tooltip_icon_size_(tooltip_icon_size),
mouse_inside_(false),
bubble_(nullptr),
preferred_width_(0),
@@ -69,10 +70,10 @@ void TooltipIcon::MouseMovedOutOfHost() {
}
void TooltipIcon::SetDrawAsHovered(bool hovered) {
- SetImage(gfx::CreateVectorIcon(vector_icons::kInfoOutlineIcon, 18,
- hovered
- ? SkColorSetARGB(0xBD, 0, 0, 0)
- : SkColorSetARGB(0xBD, 0x44, 0x44, 0x44)));
+ SetImage(
+ gfx::CreateVectorIcon(vector_icons::kInfoOutlineIcon, tooltip_icon_size_,
+ hovered ? SkColorSetARGB(0xBD, 0, 0, 0)
+ : SkColorSetARGB(0xBD, 0x44, 0x44, 0x44)));
}
void TooltipIcon::ShowBubble() {
@@ -83,7 +84,7 @@ void TooltipIcon::ShowBubble() {
bubble_ = new InfoBubble(this, tooltip_);
bubble_->set_preferred_width(preferred_width_);
- bubble_->set_arrow(BubbleBorder::TOP_RIGHT);
+ bubble_->set_arrow(anchor_point_arrow_);
// When shown due to a gesture event, close on deactivate (i.e. don't use
// "focusless").
bubble_->set_can_activate(!mouse_inside_);
diff --git a/chromium/ui/views/bubble/tooltip_icon.h b/chromium/ui/views/bubble/tooltip_icon.h
index 327bdd505b2..97dbe043b45 100644
--- a/chromium/ui/views/bubble/tooltip_icon.h
+++ b/chromium/ui/views/bubble/tooltip_icon.h
@@ -11,6 +11,7 @@
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "base/timer/timer.h"
+#include "ui/views/bubble/bubble_border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/mouse_watcher.h"
#include "ui/views/widget/widget_observer.h"
@@ -24,7 +25,8 @@ class VIEWS_EXPORT TooltipIcon : public ImageView,
public MouseWatcherListener,
public WidgetObserver {
public:
- explicit TooltipIcon(const base::string16& tooltip);
+ explicit TooltipIcon(const base::string16& tooltip,
+ int tooltip_icon_size = 16);
~TooltipIcon() override;
// ImageView:
@@ -45,6 +47,10 @@ class VIEWS_EXPORT TooltipIcon : public ImageView,
preferred_width_ = preferred_width;
}
+ void set_anchor_point_arrow(BubbleBorder::Arrow arrow) {
+ anchor_point_arrow_ = arrow;
+ }
+
private:
// Changes the color to reflect the hover node_data.
void SetDrawAsHovered(bool hovered);
@@ -59,6 +65,14 @@ class VIEWS_EXPORT TooltipIcon : public ImageView,
// The text to show in a bubble when hovered.
base::string16 tooltip_;
+ // The size of the tooltip icon, in dip.
+ // Must be set in the constructor, otherwise the pre-hovered icon will show
+ // the default size.
+ int tooltip_icon_size_;
+
+ // The point at which to anchor the tooltip.
+ BubbleBorder::Arrow anchor_point_arrow_ = BubbleBorder::TOP_RIGHT;
+
// Whether the mouse is inside this tooltip.
bool mouse_inside_;
diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc
index 80ae22b84af..397b49e9b04 100644
--- a/chromium/ui/views/bubble/tray_bubble_view.cc
+++ b/chromium/ui/views/bubble/tray_bubble_view.cc
@@ -274,7 +274,6 @@ void TrayBubbleView::InitializeAndShowBubble() {
void TrayBubbleView::UpdateBubble() {
if (GetWidget()) {
SizeToContents();
- bubble_content_mask_->layer()->SetBounds(GetBubbleBounds());
GetWidget()->GetRootView()->SchedulePaint();
// When extra keyboard accessibility is enabled, focus the default item if
@@ -313,6 +312,10 @@ void TrayBubbleView::ResetDelegate() {
delegate_ = nullptr;
}
+void TrayBubbleView::ChangeAnchorView(views::View* anchor_view) {
+ BubbleDialogDelegateView::SetAnchorView(anchor_view);
+}
+
int TrayBubbleView::GetDialogButtons() const {
return ui::DIALOG_BUTTON_NONE;
}
@@ -326,7 +329,7 @@ ax::mojom::Role TrayBubbleView::GetAccessibleWindowRole() const {
void TrayBubbleView::SizeToContents() {
BubbleDialogDelegateView::SizeToContents();
- bubble_content_mask_->layer()->SetBounds(GetBubbleBounds());
+ bubble_content_mask_->layer()->SetBounds(layer()->parent()->bounds());
}
void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params,
diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h
index bd78fbc3509..346641e9ad5 100644
--- a/chromium/ui/views/bubble/tray_bubble_view.h
+++ b/chromium/ui/views/bubble/tray_bubble_view.h
@@ -130,6 +130,9 @@ class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView,
// ResetDelegate.
void ResetDelegate();
+ // Anchors the bubble to |anchor_view|.
+ void ChangeAnchorView(views::View* anchor_view);
+
Delegate* delegate() { return delegate_; }
void set_gesture_dragging(bool dragging) { is_gesture_dragging_ = dragging; }
diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views/cocoa/bridged_content_view.h
index 5bfbe297e5c..a5a9b690ef2 100644
--- a/chromium/ui/views/cocoa/bridged_content_view.h
+++ b/chromium/ui/views/cocoa/bridged_content_view.h
@@ -33,6 +33,11 @@ class View;
// hierarchy rooted at |hostedView_|. Owned by the focused View.
ui::TextInputClient* textInputClient_;
+ // The TextInputClient about to be set. Requests for a new -inputContext will
+ // use this, but while the input is changing, |self| still needs to service
+ // IME requests using the old |textInputClient_|.
+ ui::TextInputClient* pendingTextInputClient_;
+
// A tracking area installed to enable mouseMoved events.
ui::ScopedCrTrackingArea cursorTrackingArea_;
diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm
index 17f7339f7d8..445d42c389a 100644
--- a/chromium/ui/views/cocoa/bridged_content_view.mm
+++ b/chromium/ui/views/cocoa/bridged_content_view.mm
@@ -80,6 +80,13 @@ bool IsTextRTL(const ui::TextInputClient* client) {
return client && client->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
}
+// Returns true if |event| may have triggered dismissal of an IME and would
+// otherwise be ignored by a ui::TextInputClient when inserted.
+bool IsImeTriggerEvent(NSEvent* event) {
+ ui::KeyboardCode key = ui::KeyboardCodeFromNSEvent(event);
+ return key == ui::VKEY_RETURN || key == ui::VKEY_TAB;
+}
+
// Returns the boundary rectangle for composition characters in the
// |requested_range|. Sets |actual_range| corresponding to the returned
// rectangle. For cases, where there is no composition text or the
@@ -240,6 +247,11 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
domCode:(ui::DomCode)domCode
eventFlags:(int)eventFlags;
+// ui::EventLocationFromNative() assumes the event hit the contentView.
+// Adjust |event| if that's not the case (e.g. for reparented views).
+- (void)adjustUiEventLocation:(ui::LocatedEvent*)event
+ fromNativeEvent:(NSEvent*)nativeEvent;
+
// Notification handler invoked when the Full Keyboard Access mode is changed.
- (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification;
@@ -302,14 +314,83 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
return self;
}
+- (void)dealloc {
+ // By the time |self| is dealloc'd, it should never be in an NSWindow, and it
+ // should never be the current input context.
+ DCHECK_EQ(nil, [self window]);
+ // Sanity check: NSView always provides an -inputContext.
+ DCHECK_NE(nil, [super inputContext]);
+ DCHECK_NE([NSTextInputContext currentInputContext], [super inputContext]);
+ [super dealloc];
+}
+
- (void)clearView {
- textInputClient_ = nullptr;
+ [self setTextInputClient:nullptr];
hostedView_ = nullptr;
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
[cursorTrackingArea_.get() clearOwner];
[self removeTrackingArea:cursorTrackingArea_.get()];
}
+- (void)setTextInputClient:(ui::TextInputClient*)newTextInputClient {
+ if (pendingTextInputClient_ == newTextInputClient)
+ return;
+
+ // This method may cause the IME window to dismiss, which may cause it to
+ // insert text (e.g. to replace marked text with "real" text). That should
+ // happen in the old -inputContext (which AppKit stores a reference to).
+ // Unfortunately, the only way to invalidate the the old -inputContext is to
+ // invoke -[NSApp updateWindows], which also wants a reference to the _new_
+ // -inputContext. So put the new inputContext in |pendingTextInputClient_| and
+ // only use it for -inputContext.
+ ui::TextInputClient* oldInputClient = textInputClient_;
+
+ // Since dismissing an IME may insert text, a misbehaving IME or a
+ // ui::TextInputClient that acts on InsertChar() to change focus a second time
+ // may invoke -setTextInputClient: recursively; with [NSApp updateWindows]
+ // still on the stack. Calling [NSApp updateWindows] recursively may upset
+ // an IME. Since the rest of this method is only to decide whether to call
+ // updateWindows, and we're already calling it, just bail out.
+ if (textInputClient_ != pendingTextInputClient_) {
+ pendingTextInputClient_ = newTextInputClient;
+ return;
+ }
+
+ // Start by assuming no need to invoke -updateWindows.
+ textInputClient_ = newTextInputClient;
+ pendingTextInputClient_ = newTextInputClient;
+
+ // If |self| was being used for the input context, and would now report a
+ // different input context, manually invoke [NSApp updateWindows]. This is
+ // necessary because AppKit holds on to a raw pointer to a NSTextInputContext
+ // (which may have been the one returned by [self inputContext]) that is only
+ // updated by -updateWindows. And although AppKit invokes that on each
+ // iteration through most runloop modes, it does not call it when running
+ // NSEventTrackingRunLoopMode, and not _within_ a run loop iteration, where
+ // the inputContext may change before further event processing.
+ NSTextInputContext* current = [NSTextInputContext currentInputContext];
+ if (!current)
+ return;
+
+ NSTextInputContext* newContext = [self inputContext];
+ // If the newContext is non-nil, then it can only be [super inputContext]. So
+ // the input context is either not changing, or it was not from |self|. In
+ // both cases, there's no need to call -updateWindows.
+ if (newContext) {
+ DCHECK_EQ(newContext, [super inputContext]);
+ return;
+ }
+
+ if (current == [super inputContext]) {
+ DCHECK_NE(oldInputClient, textInputClient_);
+ textInputClient_ = oldInputClient;
+ [NSApp updateWindows];
+ // Note: |pendingTextInputClient_| (and therefore +[NSTextInputContext
+ // currentInputContext] may have changed if called recursively.
+ textInputClient_ = pendingTextInputClient_;
+ }
+}
+
// If the point is classified as HTCAPTION (background, draggable), return nil
// so that it can lead to a window drag or double-click in the title bar.
- (NSView*)hitTest:(NSPoint)point {
@@ -328,16 +409,31 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
NSWindow* target = [self window];
DCHECK(target);
+ BOOL isScrollEvent = [theEvent type] == NSScrollWheel;
+
// If it's the view's window, process normally.
if ([target isEqual:source]) {
- [self mouseEvent:theEvent];
+ if (isScrollEvent)
+ [self scrollWheel:theEvent];
+ else
+ [self mouseEvent:theEvent];
+
return;
}
- ui::MouseEvent event(theEvent);
- event.set_location(
- MovePointToWindow([theEvent locationInWindow], source, target));
- hostedView_->GetWidget()->OnMouseEvent(&event);
+ gfx::Point event_location =
+ MovePointToWindow([theEvent locationInWindow], source, target);
+ [self updateTooltipIfRequiredAt:event_location];
+
+ if (isScrollEvent) {
+ ui::ScrollEvent event(theEvent);
+ event.set_location(event_location);
+ hostedView_->GetWidget()->OnScrollEvent(&event);
+ } else {
+ ui::MouseEvent event(theEvent);
+ event.set_location(event_location);
+ hostedView_->GetWidget()->OnMouseEvent(&event);
+ }
}
- (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent {
@@ -427,6 +523,14 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event));
}
+- (void)adjustUiEventLocation:(ui::LocatedEvent*)event
+ fromNativeEvent:(NSEvent*)nativeEvent {
+ if ([nativeEvent window] && [[self window] contentView] != self) {
+ NSPoint p = [self convertPoint:[nativeEvent locationInWindow] fromView:nil];
+ event->set_location(gfx::Point(p.x, NSHeight([self frame]) - p.y));
+ }
+}
+
- (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification {
DCHECK([[notification name]
isEqualToString:kFullKeyboardAccessChangedNotification]);
@@ -441,7 +545,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
text = [text string];
bool isCharacterEvent = keyDownEvent_ && [text length] == 1;
- // Pass the character event to the View hierarchy. Cases this handles (non-
+ // Pass "character" events to the View hierarchy. Cases this handles (non-
// exhaustive)-
// - Space key press on controls. Unlike Tab and newline which have
// corresponding action messages, an insertText: message is generated for
@@ -453,14 +557,26 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// key code to get the actual characters from the ui::KeyEvent. This for
// example is necessary for menu mnemonic selection of non-latin text.
- // Don't generate a key event when there is active composition text. These key
+ // Don't generate a key event when there is marked composition text. These key
// down events should be consumed by the IME and not reach the Views layer.
// For example, on pressing Return to commit composition text, if we passed a
// synthetic key event to the View hierarchy, it will have the effect of
- // performing the default action on the current dialog. We do not want this.
-
- // Also note that a single key down event can cause multiple
- // insertText:replacementRange: action messages. Example, on pressing Alt+e,
+ // performing the default action on the current dialog. We do not want this
+ // when there is marked text (Return should only confirm the IME).
+
+ // However, IME for phonetic languages such as Korean do not always _mark_
+ // text when a composition is active. For these, correct behaviour is to
+ // handle the final -keyDown: that caused the composition to be committed, but
+ // only _after_ the sequence of insertText: messages coming from IME have been
+ // sent to the TextInputClient. Detect this by comparing to -[NSEvent
+ // characters]. Note we do not use -charactersIgnoringModifiers: so that,
+ // e.g., ß (Alt+s) will match mnemonics with ß rather than s.
+ bool isFinalInsertForKeyEvent =
+ isCharacterEvent && [text isEqualToString:[keyDownEvent_ characters]];
+
+ // Also note that a single, non-IME key down event can also cause multiple
+ // insertText:replacementRange: action messages being generated from within
+ // -keyDown:'s call to -interpretKeyEvents:. One example, on pressing Alt+e,
// the accent (´) character is composed via setMarkedText:. Now on pressing
// the character 'r', two insertText:replacementRange: action messages are
// generated with the text value of accent (´) and 'r' respectively. The key
@@ -470,7 +586,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// Currently there seems to be no use case to pass non-character events routed
// from insertText: handlers to the View hierarchy.
- if (isCharacterEvent && ![self hasMarkedText]) {
+ if (isFinalInsertForKeyEvent && ![self hasMarkedText]) {
ui::KeyEvent charEvent([text characterAtIndex:0],
ui::KeyboardCodeFromNSEvent(keyDownEvent_),
ui::EF_NONE);
@@ -490,18 +606,26 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// the key modifier since |text| already accounts for the pressed key
// modifiers.
- // Also, note we don't use |keyDownEvent_| to generate the synthetic
- // ui::KeyEvent since for composed text, [keyDownEvent_ characters] might
- // not be the same as |text|. This is because |keyDownEvent_| will
- // correspond to the event that caused the composition text to be confirmed,
- // say, Return key press.
+ // Also, note we don't check isFinalInsertForKeyEvent, nor use
+ // |keyDownEvent_| to generate the synthetic ui::KeyEvent since: For
+ // composed text, [keyDownEvent_ characters] might not be the same as
+ // |text|. This is because |keyDownEvent_| will correspond to the event that
+ // caused the composition text to be confirmed, say, Return key press.
if (isCharacterEvent) {
textInputClient_->InsertChar(ui::KeyEvent([text characterAtIndex:0],
ui::VKEY_UNKNOWN, ui::EF_NONE));
+ // Leave character events that may have triggered IME confirmation for
+ // inline IME (e.g. Korean) as "unhandled". There will be no more
+ // -insertText: messages, but we are unable to handle these via
+ // -handleKeyEvent: earlier in this method since toolkit-views client code
+ // assumes it can ignore characters associated with, e.g., VKEY_TAB.
+ DCHECK(keyDownEvent_); // Otherwise it is not a character event.
+ if ([self hasMarkedText] || !IsImeTriggerEvent(keyDownEvent_))
+ hasUnhandledKeyDownEvent_ = NO;
} else {
textInputClient_->InsertText(base::SysNSStringToUTF16(text));
+ hasUnhandledKeyDownEvent_ = NO;
}
- hasUnhandledKeyDownEvent_ = NO;
}
}
@@ -566,14 +690,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
if (!hostedView_)
return;
+ DCHECK([theEvent type] != NSScrollWheel);
ui::MouseEvent event(theEvent);
-
- // ui::EventLocationFromNative() assumes the event hit the contentView.
- // Adjust if that's not the case (e.g. for reparented views).
- if ([theEvent window] && [[self window] contentView] != self) {
- NSPoint p = [self convertPoint:[theEvent locationInWindow] fromView:nil];
- event.set_location(gfx::Point(p.x, NSHeight([self frame]) - p.y));
- }
+ [self adjustUiEventLocation:&event fromNativeEvent:theEvent];
// Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
// Mac hooks in here.
@@ -680,7 +799,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
- (NSTextInputContext*)inputContext {
// If the textInputClient_ does not exist, return nil since this view does not
// conform to NSTextInputClient protocol.
- if (!textInputClient_)
+ if (!pendingTextInputClient_)
return nil;
// If a menu is active, and -[NSView interpretKeyEvents:] asks for the
@@ -693,7 +812,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// (http://crbug.com/23219), we don't want to show IME candidate windows.
// Returning nil prevents this view from getting messages defined as part of
// the NSTextInputClient protocol.
- switch (textInputClient_->GetTextInputType()) {
+ switch (pendingTextInputClient_->GetTextInputType()) {
case ui::TEXT_INPUT_TYPE_NONE:
case ui::TEXT_INPUT_TYPE_PASSWORD:
return nil;
@@ -744,6 +863,12 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
return;
ui::ScrollEvent event(theEvent);
+ [self adjustUiEventLocation:&event fromNativeEvent:theEvent];
+
+ // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
+ // Mac hooks in here.
+ [self updateTooltipIfRequiredAt:event.location()];
+
hostedView_->GetWidget()->OnScrollEvent(&event);
}
@@ -1253,6 +1378,14 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// NSTextInputClient protocol implementation.
+// IMPORTANT: Always null-check |textInputClient_|. It can change (or be
+// cleared) in -setTextInputClient:, which requires informing AppKit that the
+// -inputContext has changed and to update its raw pointer. However, the AppKit
+// method which does that may also spin a nested run loop communicating with an
+// IME window and cause it to *use* the exact same NSTextInputClient (i.e.,
+// |self|) that we're trying to invalidate in -setTextInputClient:.
+// See https://crbug.com/817097#c12 for further details on this atrocity.
+
- (NSAttributedString*)
attributedSubstringForProposedRange:(NSRange)range
actualRange:(NSRangePointer)actualRange {
@@ -1311,13 +1444,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
}
- (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
- if (!hostedView_)
+ if (!hostedView_ || !textInputClient_)
return;
- // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no
- // menu is active.
- DCHECK([self inputContext]);
-
textInputClient_->DeleteRange(gfx::Range(replacementRange));
[self insertTextInternal:text];
}
diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h
index ec83dfb3089..674a9b78228 100644
--- a/chromium/ui/views/cocoa/bridged_native_widget.h
+++ b/chromium/ui/views/cocoa/bridged_native_widget.h
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#import "ui/accelerated_widget_mac/accelerated_widget_mac.h"
+#include "ui/accelerated_widget_mac/ca_transaction_observer.h"
#include "ui/accelerated_widget_mac/display_ca_layer_tree.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/compositor/layer_owner.h"
@@ -30,6 +31,7 @@
namespace ui {
class InputMethod;
+class RecyclableCompositorMac;
}
namespace views {
@@ -47,7 +49,8 @@ class View;
// DesktopNativeWidgetMac. Serves as a helper class to bridge requests from the
// NativeWidgetMac to the Cocoa window. Behaves a bit like an aura::Window.
class VIEWS_EXPORT BridgedNativeWidget
- : public ui::LayerDelegate,
+ : public ui::CATransactionCoordinator::PreCommitObserver,
+ public ui::LayerDelegate,
public ui::LayerOwner,
public ui::internal::InputMethodDelegate,
public CocoaMouseCaptureDelegate,
@@ -208,9 +211,25 @@ class VIEWS_EXPORT BridgedNativeWidget
bool target_fullscreen_state() const { return target_fullscreen_state_; }
bool window_visible() const { return window_visible_; }
bool wants_to_be_visible() const { return wants_to_be_visible_; }
+ bool in_fullscreen_transition() const { return in_fullscreen_transition_; }
- bool animate() const { return animate_; }
- void set_animate(bool animate) { animate_ = animate; }
+ // Enables or disables all window animations.
+ void SetAnimationEnabled(bool animate);
+
+ // Sets which transitions will animate. Currently this only affects non-native
+ // animations. TODO(tapted): Use scoping to disable native animations at
+ // appropriate times as well.
+ void set_transitions_to_animate(int transitions) {
+ transitions_to_animate_ = transitions;
+ }
+
+ // Whether to run a custom animation for the provided |transition|.
+ bool ShouldRunCustomAnimationFor(
+ Widget::VisibilityTransition transition) const;
+
+ // ui::CATransactionCoordinator::PreCommitObserver implementation
+ bool ShouldWaitInPreCommit() override;
+ base::TimeDelta PreCommitTimeout() override;
// Overridden from ui::internal::InputMethodDelegate:
ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override;
@@ -234,8 +253,7 @@ class VIEWS_EXPORT BridgedNativeWidget
void InitCompositor();
void DestroyCompositor();
- // Installs the NSView for hosting the composited layer. It is later provided
- // to |compositor_widget_| via AcceleratedWidgetGetNSView().
+ // Installs the NSView for hosting the composited layer.
void AddCompositorSuperview();
// Size the layer to match the client area bounds, taking into account display
@@ -271,7 +289,6 @@ class VIEWS_EXPORT BridgedNativeWidget
float new_device_scale_factor) override;
// Overridden from ui::AcceleratedWidgetMac:
- NSView* AcceleratedWidgetGetNSView() const override;
void AcceleratedWidgetCALayerParamsUpdated() override;
// Overridden from BridgedNativeWidgetOwner:
@@ -283,6 +300,11 @@ class VIEWS_EXPORT BridgedNativeWidget
// DialogObserver:
void OnDialogModelChanged() override;
+ // Set |layer()| to be visible or not visible based on |window_visible_|. If
+ // the layer is not visible, then lock the compositor, so we don't draw any
+ // new frames.
+ void UpdateLayerVisibility();
+
views::NativeWidgetMac* native_widget_mac_; // Weak. Owns this.
base::scoped_nsobject<NSWindow> window_;
base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_;
@@ -300,16 +322,25 @@ class VIEWS_EXPORT BridgedNativeWidget
std::vector<BridgedNativeWidget*> child_windows_;
base::scoped_nsobject<NSView> compositor_superview_;
- std::unique_ptr<ui::AcceleratedWidgetMac> compositor_widget_;
std::unique_ptr<ui::DisplayCALayerTree> display_ca_layer_tree_;
- std::unique_ptr<ui::Compositor> compositor_;
- viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
+ std::unique_ptr<ui::RecyclableCompositorMac> compositor_;
// Tracks the bounds when the window last started entering fullscreen. Used to
// provide an answer for GetRestoredBounds(), but not ever sent to Cocoa (it
// has its own copy, but doesn't provide access to it).
gfx::Rect bounds_before_fullscreen_;
+ // Tracks the origin of the window (from the top-left of the screen) when it
+ // was last reported to toolkit-views. Needed to trigger move notifications
+ // associated with a window resize since AppKit won't send move notifications
+ // when the top-left point of the window moves vertically. The origin of the
+ // window in AppKit coordinates is not actually changing in this case.
+ gfx::Point last_window_frame_origin_;
+
+ // The transition types to animate when not relying on native NSWindow
+ // animation behaviors. Bitmask of Widget::VisibilityTransition.
+ int transitions_to_animate_ = Widget::ANIMATE_BOTH;
+
// Whether this window wants to be fullscreen. If a fullscreen animation is in
// progress then it might not be actually fullscreen.
bool target_fullscreen_state_;
@@ -326,18 +357,14 @@ class VIEWS_EXPORT BridgedNativeWidget
// currently hidden due to having a hidden parent.
bool wants_to_be_visible_;
- // Whether to animate the window (when it is appropriate to do so).
- bool animate_ = true;
+ // If true, then ignore interactions with CATransactionCoordinator until the
+ // first frame arrives.
+ bool ca_transaction_sync_suppressed_ = false;
// If true, the window has been made visible or changed shape and the window
// shadow needs to be invalidated when a frame is received for the new shape.
bool invalidate_shadow_on_frame_swap_ = false;
- // Whether the window's visibility is suppressed currently. For opaque non-
- // modal windows, the window's alpha value is set to 0, till the frame from
- // the compositor arrives to avoid "blinking".
- bool initial_visibility_suppressed_ = false;
-
AssociatedViews associated_views_;
DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget);
diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm
index c37fd628634..69c5f1f44d7 100644
--- a/chromium/ui/views/cocoa/bridged_native_widget.mm
+++ b/chromium/ui/views/cocoa/bridged_native_widget.mm
@@ -8,12 +8,12 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/command_line.h"
#include "base/logging.h"
#import "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/features.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
@@ -22,7 +22,8 @@
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/input_method_factory.h"
#include "ui/base/layout.h"
-#include "ui/compositor/compositor_switches.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/compositor/recyclable_compositor_mac.h"
#include "ui/gfx/geometry/dip_util.h"
#import "ui/gfx/mac/coordinate_conversion.h"
#import "ui/gfx/mac/nswindow_frame_controls.h"
@@ -51,6 +52,9 @@ CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection,
int radius);
}
+namespace {
+constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5);
+} // namespace
// The NSView that hosts the composited CALayer drawing the UI. It fills the
// window but is not hittable so that accessibility hit tests always go to the
@@ -168,18 +172,6 @@ gfx::Size GetClientSizeForWindowSize(NSWindow* window,
return gfx::Size([window contentRectForFrameRect:frame_rect].size);
}
-// Returns a task runner for creating a ui::Compositor. This allows compositor
-// tasks to be funneled through ui::WindowResizeHelper's task runner to allow
-// resize operations to coordinate with frames provided by the GPU process.
-scoped_refptr<base::SingleThreadTaskRunner> GetCompositorTaskRunner() {
- // If the WindowResizeHelper's pumpable task runner is set, it means the GPU
- // process is directing messages there, and the compositor can synchronize
- // with it. Otherwise, just use the UI thread.
- scoped_refptr<base::SingleThreadTaskRunner> task_runner =
- ui::WindowResizeHelperMac::Get()->task_runner();
- return task_runner ? task_runner : base::ThreadTaskRunnerHandle::Get();
-}
-
void RankNSViews(views::View* view,
const views::BridgedNativeWidget::AssociatedViews& hosts,
RankMap* rank) {
@@ -251,6 +243,7 @@ BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
DCHECK(parent);
window_delegate_.reset(
[[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
+ ui::CATransactionCoordinator::Get().AddPreCommitObserver(this);
}
BridgedNativeWidget::~BridgedNativeWidget() {
@@ -259,6 +252,7 @@ BridgedNativeWidget::~BridgedNativeWidget() {
// destructor is called.
DCHECK(![window_ delegate]);
+ ui::CATransactionCoordinator::Get().RemovePreCommitObserver(this);
RemoveOrDestroyChildren();
DCHECK(child_windows_.empty());
SetFocusManager(nullptr);
@@ -318,6 +312,15 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
NSWindowCollectionBehaviorTransient];
}
+ // Include "regular" windows without the standard frame in the window cycle.
+ // These use NSBorderlessWindowMask so do not get it by default.
+ if (widget_type_ == Widget::InitParams::TYPE_WINDOW &&
+ params.remove_standard_frame) {
+ [window_
+ setCollectionBehavior:[window_ collectionBehavior] |
+ NSWindowCollectionBehaviorParticipatesInCycle];
+ }
+
// OSX likes to put shadows on most things. However, frameless windows (with
// styleMask = NSBorderlessWindowMask) default to no shadow. So change that.
// SHADOW_TYPE_DROP is used for Menus, which get the same shadow style on Mac.
@@ -379,13 +382,21 @@ void BridgedNativeWidget::SetFocusManager(FocusManager* focus_manager) {
if (focus_manager_ == focus_manager)
return;
- if (focus_manager_)
+ if (focus_manager_) {
+ // Only the destructor can replace the focus manager (and it passes null).
+ DCHECK(![window_ delegate]);
+ DCHECK(!focus_manager);
+ if (View* old_focus = focus_manager_->GetFocusedView())
+ OnDidChangeFocus(old_focus, nullptr);
focus_manager_->RemoveFocusChangeListener(this);
-
- if (focus_manager)
- focus_manager->AddFocusChangeListener(this);
+ focus_manager_ = nullptr;
+ return;
+ }
focus_manager_ = focus_manager;
+ focus_manager_->AddFocusChangeListener(this);
+ if (View* new_focus = focus_manager_->GetFocusedView())
+ OnDidChangeFocus(nullptr, new_focus);
}
void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) {
@@ -430,7 +441,7 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
// If this is ever false, the compositor will need to be properly torn down
// and replaced, pointing at the new view.
- DCHECK(!view || !compositor_widget_);
+ DCHECK(!view || !compositor_);
drag_drop_client_.reset();
[bridged_view_ clearView];
@@ -447,6 +458,11 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
// this should be treated as an error and caught early.
CHECK(bridged_view_);
}
+
+ // Layer backing the content view improves resize performance, reduces memory
+ // use (no backing store), and clips sublayers to rounded window corners.
+ [bridged_view_ setWantsLayer:YES];
+
[window_ setContentView:bridged_view_];
}
@@ -481,6 +497,10 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
}
DCHECK(wants_to_be_visible_);
+
+ if (!ca_transaction_sync_suppressed_)
+ ui::CATransactionCoordinator::Get().Synchronize();
+
// If the parent (or an ancestor) is hidden, return and wait for it to become
// visible.
if (parent() && !parent()->IsVisibleParent())
@@ -491,23 +511,6 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
return;
}
- // Non-modal windows are not animated. Hence opaque non-modal windows can
- // appear with a "flash" if they are made visible before the frame from the
- // compositor arrives. To get around this, set the alpha value of the window
- // to 0, till we receive the correct frame from the compositor. Also, ignore
- // mouse clicks till then. Also check for an active task runner on the
- // WindowResizeHelperMac instance to ensure visibility is only suppressed when
- // there is an active GPU process.
- // TODO(karandeepb): Investigate whether similar technique is needed for other
- // dialog types.
- if (layer() && [window_ isOpaque] && !window_visible_ &&
- !native_widget_mac_->GetWidget()->IsModal() &&
- ui::WindowResizeHelperMac::Get()->task_runner()) {
- initial_visibility_suppressed_ = true;
- [window_ setAlphaValue:0.0];
- [window_ setIgnoresMouseEvents:YES];
- }
-
if (new_state == SHOW_AND_ACTIVATE_WINDOW) {
[window_ makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
@@ -516,8 +519,22 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
// parent window. So, if there's a parent, order above that. Otherwise, this
// will order above all windows at the same level.
NSInteger parent_window_number = 0;
- if (parent_)
+ if (parent_) {
+ // When there's a parent, check if the window is already visible. If
+ // ShowInactive() is called on an already-visible window, there should be
+ // no effect: the macOS childWindow mechanism should have already raised
+ // the window to the right stacking order. More importantly, invoking
+ // -[NSWindow orderWindow:] could cause a Space switch, which defeats the
+ // point of ShowInactive(), so avoid it. See https://crbug.com/866760.
+
+ // Sanity check: if the window is visible, the prior Show should have
+ // hooked it up as a native child window already.
+ DCHECK_EQ(window_visible_, !![window_ parentWindow]);
+ if (window_visible_)
+ return; // Avoid a Spaces transition.
+
parent_window_number = [parent_->GetNSWindow() windowNumber];
+ }
[window_ orderWindow:NSWindowAbove
relativeTo:parent_window_number];
@@ -526,7 +543,7 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
// For non-sheet modal types, use the constrained window animations to make
// the window appear.
- if (animate_ && native_widget_mac_->GetWidget()->IsModal()) {
+ if (ShouldRunCustomAnimationFor(Widget::ANIMATE_SHOW)) {
show_animation_.reset(
[[ModalShowAnimationWithLayer alloc] initWithBridgedNativeWidget:this]);
@@ -660,6 +677,9 @@ void BridgedNativeWidget::OnFullscreenTransitionStart(
// If going into fullscreen, store an answer for GetRestoredBounds().
if (target_fullscreen_state)
bounds_before_fullscreen_ = gfx::ScreenRectFromNSRect([window_ frame]);
+
+ // Notify that fullscreen state changed.
+ native_widget_mac_->OnWindowFullscreenStateChange();
}
void BridgedNativeWidget::OnFullscreenTransitionComplete(
@@ -723,6 +743,14 @@ void BridgedNativeWidget::ToggleDesiredFullscreenState(bool async) {
}
void BridgedNativeWidget::OnSizeChanged() {
+ const gfx::Rect new_bounds = native_widget_mac_->GetWindowBoundsInScreen();
+ if (new_bounds.origin() != last_window_frame_origin_) {
+ native_widget_mac_->GetWidget()->OnNativeWidgetMove();
+ last_window_frame_origin_ = new_bounds.origin();
+ }
+
+ // Note we can't use new_bounds.size(), since it includes the titlebar for the
+ // purposes of detecting a window move.
gfx::Size new_size = GetClientAreaSize();
native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged(new_size);
if (layer()) {
@@ -733,6 +761,13 @@ void BridgedNativeWidget::OnSizeChanged() {
}
void BridgedNativeWidget::OnPositionChanged() {
+ // When a window grows vertically, the AppKit origin changes, but as far as
+ // tookit-views is concerned, the window hasn't moved. Suppress these.
+ const gfx::Rect new_bounds = native_widget_mac_->GetWindowBoundsInScreen();
+ if (new_bounds.origin() == last_window_frame_origin_)
+ return;
+
+ last_window_frame_origin_ = new_bounds.origin();
native_widget_mac_->GetWidget()->OnNativeWidgetMove();
}
@@ -767,12 +802,11 @@ void BridgedNativeWidget::OnVisibilityChanged() {
// TODO(tapted): Investigate whether we want this for Mac. This is what Aura
// does, and it is what tests expect. However, because layer drawing is
// asynchronous (and things like deminiaturize in AppKit are not), it can
- // result in a CALayer appearing on screen before it has been redrawn in the
- // GPU process. This is a general problem. In content, a helper class,
- // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:]
- // and RenderWidgetHostView::Show() until a frame is ready.
+ // result in the compositor producing a blank frame during the time that the
+ // layer is not visible. Avoid this by locking the compositor (preventing any
+ // new frames) in UpdateLayerVisibility whenever the layer is hidden.
if (layer()) {
- layer()->SetVisible(window_visible_);
+ UpdateLayerVisibility();
layer()->SchedulePaint(gfx::Rect(GetClientAreaSize()));
// For translucent windows which are made visible, recalculate shadow when
@@ -875,7 +909,7 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type,
SetLayer(std::make_unique<ui::Layer>(layer_type));
// Note, except for controls, this will set the layer to be hidden, since it
// is only called during Init().
- layer()->SetVisible(window_visible_);
+ UpdateLayerVisibility();
layer()->set_delegate(this);
InitCompositor();
@@ -893,12 +927,16 @@ void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type,
// native shape is what's most appropriate for displaying sheets on Mac.
if (translucent && !native_widget_mac_->IsWindowModalSheet()) {
[window_ setOpaque:NO];
- // For Mac OS versions earlier than Yosemite, the Window server isn't able
- // to generate a window shadow from the composited CALayer. To get around
- // this, let the window background remain opaque and clip the window
- // boundary in drawRect method of BridgedContentView. See crbug.com/543671.
- if (base::mac::IsAtLeastOS10_10())
- [window_ setBackgroundColor:[NSColor clearColor]];
+ [window_ setBackgroundColor:[NSColor clearColor]];
+
+ // Don't block waiting for the initial frame of completely transparent
+ // windows. This allows us to avoid blocking on the UI thread e.g, while
+ // typing in the omnibox. Note window modal sheets _must_ wait: there is no
+ // way for a frame to arrive during AppKit's sheet animation.
+ // https://crbug.com/712268
+ ca_transaction_sync_suppressed_ = true;
+ } else {
+ DCHECK(!ca_transaction_sync_suppressed_);
}
UpdateLayerProperties();
@@ -970,6 +1008,48 @@ void BridgedNativeWidget::ReparentNativeView(NSView* native_view,
}
}
+void BridgedNativeWidget::SetAnimationEnabled(bool animate) {
+ [window_
+ setAnimationBehavior:(animate ? NSWindowAnimationBehaviorDocumentWindow
+ : NSWindowAnimationBehaviorNone)];
+}
+
+bool BridgedNativeWidget::ShouldRunCustomAnimationFor(
+ Widget::VisibilityTransition transition) const {
+ // The logic around this needs to change if new transition types are set.
+ // E.g. it would be nice to distinguish "hide" from "close". Mac currently
+ // treats "hide" only as "close". Hide (e.g. Cmd+h) should not animate on Mac.
+ constexpr int kSupported =
+ Widget::ANIMATE_SHOW | Widget::ANIMATE_HIDE | Widget::ANIMATE_NONE;
+ DCHECK_EQ(0, transitions_to_animate_ & ~kSupported);
+
+ // Custom animations are only used for tab-modals. Note this also checks the
+ // native animation property. Clearing that will also disable custom
+ // animations to ensure that the views::Widget API behaves consistently.
+ return (transitions_to_animate_ & transition) &&
+ native_widget_mac_->GetWidget()->IsModal() &&
+ [window_ animationBehavior] != NSWindowAnimationBehaviorNone &&
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableModalAnimations);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BridgedNativeWidget, ui::CATransactionObserver
+
+bool BridgedNativeWidget::ShouldWaitInPreCommit() {
+ if (!window_visible_)
+ return false;
+ if (ca_transaction_sync_suppressed_)
+ return false;
+ if (!compositor_)
+ return false;
+ return !compositor_->widget()->HasFrameOfSize(GetClientAreaSize());
+}
+
+base::TimeDelta BridgedNativeWidget::PreCommitTimeout() {
+ return kUIPaintTimeout;
+}
+
////////////////////////////////////////////////////////////////////////////////
// BridgedNativeWidget, internal::InputMethodDelegate:
@@ -1011,6 +1091,9 @@ void BridgedNativeWidget::OnDidChangeFocus(View* focused_before,
native_widget_mac_->GetWidget()->GetInputMethod();
if (input_method) {
ui::TextInputClient* input_client = input_method->GetTextInputClient();
+ // Sanity check: When focus moves away from the widget (i.e. |focused_now|
+ // is nil), then the textInputClient will be cleared.
+ DCHECK(!!focused_now || !input_client);
[bridged_view_ setTextInputClient:input_client];
}
}
@@ -1019,7 +1102,6 @@ void BridgedNativeWidget::OnDidChangeFocus(View* focused_before,
// BridgedNativeWidget, LayerDelegate:
void BridgedNativeWidget::OnPaintLayer(const ui::PaintContext& context) {
- DCHECK(window_visible_);
native_widget_mac_->GetWidget()->OnNativeWidgetPaint(context);
}
@@ -1033,28 +1115,21 @@ void BridgedNativeWidget::OnDeviceScaleFactorChanged(
////////////////////////////////////////////////////////////////////////////////
// BridgedNativeWidget, AcceleratedWidgetMac:
-NSView* BridgedNativeWidget::AcceleratedWidgetGetNSView() const {
- return compositor_superview_;
-}
-
void BridgedNativeWidget::AcceleratedWidgetCALayerParamsUpdated() {
// Ignore frames arriving "late" for an old size. A frame at the new size
// should arrive soon.
- if (!compositor_widget_->HasFrameOfSize(GetClientAreaSize()))
+ if (!compositor_->widget()->HasFrameOfSize(GetClientAreaSize()))
return;
// Update the DisplayCALayerTree with the most recent CALayerParams, to make
// the content display on-screen.
const gfx::CALayerParams* ca_layer_params =
- compositor_widget_->GetCALayerParams();
+ compositor_->widget()->GetCALayerParams();
if (ca_layer_params)
display_ca_layer_tree_->UpdateCALayerTree(*ca_layer_params);
- if (initial_visibility_suppressed_) {
- initial_visibility_suppressed_ = false;
- [window_ setAlphaValue:1.0];
- [window_ setIgnoresMouseEvents:NO];
- }
+ if (ca_transaction_sync_suppressed_)
+ ca_transaction_sync_suppressed_ = false;
if (invalidate_shadow_on_frame_swap_) {
invalidate_shadow_on_frame_swap_ = false;
@@ -1173,7 +1248,6 @@ gfx::Size BridgedNativeWidget::GetClientAreaSize() const {
void BridgedNativeWidget::CreateCompositor() {
DCHECK(!compositor_);
- DCHECK(!compositor_widget_);
DCHECK(ViewsDelegate::GetInstance());
ui::ContextFactory* context_factory =
@@ -1184,24 +1258,20 @@ void BridgedNativeWidget::CreateCompositor() {
AddCompositorSuperview();
- compositor_widget_.reset(new ui::AcceleratedWidgetMac());
- compositor_.reset(new ui::Compositor(
- context_factory_private->AllocateFrameSinkId(), context_factory,
- context_factory_private, GetCompositorTaskRunner(),
- features::IsSurfaceSynchronizationEnabled(),
- ui::IsPixelCanvasRecordingEnabled()));
- compositor_->SetAcceleratedWidget(compositor_widget_->accelerated_widget());
- compositor_widget_->SetNSView(this);
+ compositor_ = ui::RecyclableCompositorMacFactory::Get()->CreateCompositor(
+ context_factory, context_factory_private);
+ compositor_->widget()->SetNSView(this);
}
void BridgedNativeWidget::InitCompositor() {
+ TRACE_EVENT0("ui", "BridgedNativeWidget::InitCompositor");
DCHECK(layer());
float scale_factor = GetDeviceScaleFactorFromView(compositor_superview_);
gfx::Size size_in_dip = GetClientAreaSize();
- compositor_->SetScaleAndSize(scale_factor,
- ConvertSizeToPixel(scale_factor, size_in_dip),
- parent_local_surface_id_allocator_.GenerateId());
- compositor_->SetRootLayer(layer());
+ compositor_->UpdateSurface(ConvertSizeToPixel(scale_factor, size_in_dip),
+ scale_factor);
+ compositor_->compositor()->SetRootLayer(layer());
+ compositor_->Unsuspend();
}
void BridgedNativeWidget::DestroyCompositor() {
@@ -1216,13 +1286,12 @@ void BridgedNativeWidget::DestroyCompositor() {
}
DestroyLayer();
- if (!compositor_widget_) {
- DCHECK(!compositor_);
+ if (!compositor_)
return;
- }
- compositor_widget_->ResetNSView();
- compositor_.reset();
- compositor_widget_.reset();
+ compositor_->widget()->ResetNSView();
+ compositor_->compositor()->SetRootLayer(nullptr);
+ ui::RecyclableCompositorMacFactory::Get()->RecycleCompositor(
+ std::move(compositor_));
}
void BridgedNativeWidget::AddCompositorSuperview() {
@@ -1261,14 +1330,11 @@ void BridgedNativeWidget::UpdateLayerProperties() {
gfx::Size size_in_dip = GetClientAreaSize();
gfx::Size size_in_pixel = ConvertSizeToPixel(scale_factor, size_in_dip);
- layer()->SetBounds(gfx::Rect(size_in_dip));
+ if (!ca_transaction_sync_suppressed_)
+ ui::CATransactionCoordinator::Get().Synchronize();
- if (compositor_->size() != size_in_pixel ||
- compositor_->device_scale_factor() != scale_factor) {
- compositor_->SetScaleAndSize(
- scale_factor, size_in_pixel,
- parent_local_surface_id_allocator_.GenerateId());
- }
+ layer()->SetBounds(gfx::Rect(size_in_dip));
+ compositor_->UpdateSurface(size_in_pixel, scale_factor);
// For a translucent window, the shadow calculation needs to be carried out
// after the frame from the compositor arrives.
@@ -1277,7 +1343,8 @@ void BridgedNativeWidget::UpdateLayerProperties() {
}
void BridgedNativeWidget::MaybeWaitForFrame(const gfx::Size& size_in_dip) {
- if (!layer()->IsDrawn() || compositor_widget_->HasFrameOfSize(size_in_dip))
+ return; // TODO(https://crbug.com/682825): Delete this during cleanup.
+ if (!layer()->IsDrawn() || compositor_->widget()->HasFrameOfSize(size_in_dip))
return;
const int kPaintMsgTimeoutMS = 50;
@@ -1293,7 +1360,7 @@ void BridgedNativeWidget::MaybeWaitForFrame(const gfx::Size& size_in_dip) {
// Since the UI thread is blocked, the size shouldn't change.
DCHECK(size_in_dip == GetClientAreaSize());
- if (compositor_widget_->HasFrameOfSize(size_in_dip))
+ if (compositor_->widget()->HasFrameOfSize(size_in_dip))
return; // Frame arrived.
}
}
@@ -1303,16 +1370,20 @@ void BridgedNativeWidget::ShowAsModalSheet() {
// So that it doesn't animate a fully transparent window, first wait for a
// frame. The first step is to pretend that the window is already visible.
window_visible_ = true;
- layer()->SetVisible(true);
+ UpdateLayerVisibility();
native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged(true);
MaybeWaitForFrame(GetClientAreaSize());
NSWindow* parent_window = parent_->GetNSWindow();
DCHECK(parent_window);
+ // -beginSheet: does not retain |modalDelegate| (and we would not want it to).
+ // Since |this| may destroy [window_ delegate], use |window_| itself as the
+ // delegate, which will forward to ViewsNSWindowDelegate if |this| is still
+ // alive (i.e. it has not set the window delegate to nil).
[NSApp beginSheet:window_
modalForWindow:parent_window
- modalDelegate:[window_ delegate]
+ modalDelegate:window_
didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo:nullptr];
}
@@ -1328,4 +1399,12 @@ NSMutableDictionary* BridgedNativeWidget::GetWindowProperties() const {
return properties;
}
+void BridgedNativeWidget::UpdateLayerVisibility() {
+ layer()->SetVisible(window_visible_);
+ if (window_visible_)
+ compositor_->Unsuspend();
+ else
+ compositor_->Suspend();
+}
+
} // namespace views
diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm
index 282a17b231e..f25287d5bb5 100644
--- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -10,6 +10,7 @@
#import "base/mac/foundation_util.h"
#import "base/mac/mac_util.h"
+#import "base/mac/scoped_objc_class_swizzler.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
@@ -28,6 +29,7 @@
#import "ui/views/cocoa/native_widget_mac_nswindow.h"
#import "ui/views/cocoa/views_nswindow_delegate.h"
#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/controls/textfield/textfield_model.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/view.h"
@@ -197,8 +199,54 @@ bool IsRTLSelectBuggy(SEL sel) {
sel == @selector(moveLeftAndModifySelection:);
}
+// Used by InterpretKeyEventsDonorForNSView to simulate IME behavior.
+using InterpretKeyEventsCallback = base::RepeatingCallback<void(id)>;
+InterpretKeyEventsCallback* g_fake_interpret_key_events = nullptr;
+
+// Used by UpdateWindowsDonorForNSApp to hook -[NSApp updateWindows].
+base::RepeatingClosure* g_update_windows_closure = nullptr;
+
+// Used to provide a return value for +[NSTextInputContext currentInputContext].
+NSTextInputContext* g_fake_current_input_context = nullptr;
+
} // namespace
+// Class to hook [NSView interpretKeyEvents:] to simulate it interacting with an
+// IME window.
+@interface InterpretKeyEventsDonorForNSView : NSView
+@end
+
+@implementation InterpretKeyEventsDonorForNSView
+
+- (void)interpretKeyEvents:(NSArray<NSEvent*>*)eventArray {
+ ASSERT_TRUE(g_fake_interpret_key_events);
+ g_fake_interpret_key_events->Run(self);
+}
+
+@end
+
+@interface UpdateWindowsDonorForNSApp : NSApplication
+@end
+
+@implementation UpdateWindowsDonorForNSApp
+
+- (void)updateWindows {
+ ASSERT_TRUE(g_update_windows_closure);
+ g_update_windows_closure->Run();
+}
+
+@end
+@interface CurrentInputContextDonorForNSTextInputContext : NSTextInputContext
+@end
+
+@implementation CurrentInputContextDonorForNSTextInputContext
+
++ (NSTextInputContext*)currentInputContext {
+ return g_fake_current_input_context;
+}
+
+@end
+
// Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
// NSWindow's behavior when attempting to toggle fullscreen state again, when
// the last attempt failed but Cocoa has not yet sent
@@ -326,22 +374,21 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest {
DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTestBase);
};
-class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
+class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase,
+ public TextfieldController {
public:
+ using HandleKeyEventCallback =
+ base::RepeatingCallback<bool(Textfield*, const ui::KeyEvent& key_event)>;
+
BridgedNativeWidgetTest();
~BridgedNativeWidgetTest() override;
// Install a textfield with input type |text_input_type| in the view hierarchy
// and make it the text input client. Also initializes |dummy_text_view_|.
- void InstallTextField(const base::string16& text,
- ui::TextInputType text_input_type);
-
- // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view
- // hierarchy and make it the text input client. Also initializes
- // |dummy_text_view_|.
- void InstallTextField(const base::string16& text);
-
- void InstallTextField(const std::string& text);
+ Textfield* InstallTextField(
+ const base::string16& text,
+ ui::TextInputType text_input_type = ui::TEXT_INPUT_TYPE_TEXT);
+ Textfield* InstallTextField(const std::string& text);
// Returns the actual current text for |ns_view_|, or the selected substring.
NSString* GetActualText();
@@ -372,10 +419,17 @@ class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
// Helper method to set the private |keyDownEvent_| field on |ns_view_|.
void SetKeyDownEvent(NSEvent* event);
+ // Sets a callback to run on the next HandleKeyEvent().
+ void SetHandleKeyEventCallback(HandleKeyEventCallback callback);
+
// testing::Test:
void SetUp() override;
void TearDown() override;
+ // TextfieldController:
+ bool HandleKeyEvent(Textfield* sender,
+ const ui::KeyEvent& key_event) override;
+
protected:
// Test delete to beginning of line or paragraph based on |sel|. |sel| can be
// either deleteToBeginningOfLine: or deleteToBeginningOfParagraph:.
@@ -402,6 +456,8 @@ class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
// An NSTextView which helps set the expectations for our tests.
base::scoped_nsobject<NSTextView> dummy_text_view_;
+ HandleKeyEventCallback handle_key_event_callback_;
+
base::test::ScopedTaskEnvironment scoped_task_environment_;
private:
@@ -415,12 +471,13 @@ BridgedNativeWidgetTest::BridgedNativeWidgetTest()
BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
}
-void BridgedNativeWidgetTest::InstallTextField(
+Textfield* BridgedNativeWidgetTest::InstallTextField(
const base::string16& text,
ui::TextInputType text_input_type) {
Textfield* textfield = new Textfield();
textfield->SetText(text);
textfield->SetTextInputType(text_input_type);
+ textfield->set_controller(this);
view_->RemoveAllChildViews(true);
view_->AddChildView(textfield);
textfield->SetBoundsRect(init_params_.bounds);
@@ -437,14 +494,11 @@ void BridgedNativeWidgetTest::InstallTextField(
dummy_text_view_.reset(
[[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
[dummy_text_view_ setString:SysUTF16ToNSString(text)];
+ return textfield;
}
-void BridgedNativeWidgetTest::InstallTextField(const base::string16& text) {
- InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT);
-}
-
-void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
- InstallTextField(base::ASCIIToUTF16(text), ui::TEXT_INPUT_TYPE_TEXT);
+Textfield* BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
+ return InstallTextField(base::ASCIIToUTF16(text));
}
NSString* BridgedNativeWidgetTest::GetActualText() {
@@ -502,6 +556,11 @@ void BridgedNativeWidgetTest::SetKeyDownEvent(NSEvent* event) {
[ns_view_ setValue:event forKey:@"keyDownEvent_"];
}
+void BridgedNativeWidgetTest::SetHandleKeyEventCallback(
+ HandleKeyEventCallback callback) {
+ handle_key_event_callback_ = std::move(callback);
+}
+
void BridgedNativeWidgetTest::SetUp() {
BridgedNativeWidgetTestBase::SetUp();
@@ -534,6 +593,13 @@ void BridgedNativeWidgetTest::TearDown() {
BridgedNativeWidgetTestBase::TearDown();
}
+bool BridgedNativeWidgetTest::HandleKeyEvent(Textfield* sender,
+ const ui::KeyEvent& key_event) {
+ if (handle_key_event_callback_)
+ return handle_key_event_callback_.Run(sender, key_event);
+ return false;
+}
+
void BridgedNativeWidgetTest::TestDeleteBeginning(SEL sel) {
InstallTextField("foo bar baz");
EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
@@ -1325,6 +1391,187 @@ TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
EXPECT_EQ_RANGE(query_range, actual_range);
}
+// Test simulated codepaths for IMEs that do not always "mark" text. E.g.
+// phonetic languages such as Korean and Vietnamese.
+TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) {
+ Textfield* textfield = InstallTextField("");
+ EXPECT_TRUE([ns_view_ textInputClient]);
+
+ base::mac::ScopedObjCClassSwizzler interpret_key_events_swizzler(
+ [NSView class], [InterpretKeyEventsDonorForNSView class],
+ @selector(interpretKeyEvents:));
+
+ // Sequence of calls (and corresponding keyDown events) obtained via tracing
+ // with 2-Set Korean IME and pressing q, o, then Enter on the keyboard.
+ NSEvent* q_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode(
+ 12, [@"ㅂ" characterAtIndex:0], NSKeyDown, 0);
+ InterpretKeyEventsCallback handle_q_in_ime = base::BindRepeating([](id view) {
+ [view insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
+ });
+
+ NSEvent* o_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode(
+ 31, [@"ㅐ" characterAtIndex:0], NSKeyDown, 0);
+ InterpretKeyEventsCallback handle_o_in_ime = base::BindRepeating([](id view) {
+ [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+ });
+
+ NSEvent* return_in_ime = cocoa_test_event_utils::SynthesizeKeyEvent(
+ widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0);
+ InterpretKeyEventsCallback handle_return_in_ime =
+ base::BindRepeating([](id view) {
+ // When confirming the composition, AppKit repeats itself.
+ [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+ [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+ // Note: there is no insertText:@"\r", which we would normally see when
+ // not in an IME context for VKEY_RETURN.
+ });
+
+ // Add a hook for the KeyEvent being received by the TextfieldController. E.g.
+ // this is where the Omnibox would start to search when Return is pressed.
+ bool saw_vkey_return = false;
+ SetHandleKeyEventCallback(base::BindRepeating(
+ [](bool* saw_return, Textfield* textfield, const ui::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_RETURN) {
+ EXPECT_FALSE(*saw_return);
+ *saw_return = true;
+ EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+ }
+ return false;
+ },
+ &saw_vkey_return));
+
+ EXPECT_EQ(base::UTF8ToUTF16(""), textfield->text());
+
+ g_fake_interpret_key_events = &handle_q_in_ime;
+ [ns_view_ keyDown:q_in_ime];
+ EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->text());
+ EXPECT_FALSE(saw_vkey_return);
+
+ g_fake_interpret_key_events = &handle_o_in_ime;
+ [ns_view_ keyDown:o_in_ime];
+ EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+ EXPECT_FALSE(saw_vkey_return);
+
+ // Note the "Enter" should not replace the replacement range, even though a
+ // replacement range was set.
+ g_fake_interpret_key_events = &handle_return_in_ime;
+ [ns_view_ keyDown:return_in_ime];
+ EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+
+ // VKEY_RETURN should be seen by via the unhandled key event handler (but not
+ // via -insertText:.
+ EXPECT_TRUE(saw_vkey_return);
+
+ g_fake_interpret_key_events = nullptr;
+}
+
+// Test a codepath that could hypothetically cause [NSApp updateWindows] to be
+// called recursively due to IME dismissal during teardown triggering a focus
+// change. Twice.
+TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) {
+ Textfield* textfield = InstallTextField("");
+ EXPECT_TRUE([ns_view_ textInputClient]);
+
+ base::mac::ScopedObjCClassSwizzler interpret_key_events_swizzler(
+ [NSView class], [InterpretKeyEventsDonorForNSView class],
+ @selector(interpretKeyEvents:));
+ base::mac::ScopedObjCClassSwizzler update_windows_swizzler(
+ [NSApplication class], [UpdateWindowsDonorForNSApp class],
+ @selector(updateWindows));
+ base::mac::ScopedObjCClassSwizzler current_input_context_swizzler(
+ [NSTextInputContext class],
+ [CurrentInputContextDonorForNSTextInputContext class],
+ @selector(currentInputContext));
+
+ int vkey_return_count = 0;
+
+ // Everything happens with this one event.
+ NSEvent* return_with_fake_ime = cocoa_test_event_utils::SynthesizeKeyEvent(
+ widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0);
+
+ InterpretKeyEventsCallback generate_return_and_fake_ime = base::BindRepeating(
+ [](int* saw_return_count, id view) {
+ EXPECT_EQ(0, *saw_return_count);
+ // First generate the return to simulate an input context change.
+ [view insertText:@"\r" replacementRange:NSMakeRange(NSNotFound, 0)];
+
+ EXPECT_EQ(1, *saw_return_count);
+ },
+ &vkey_return_count);
+
+ bool saw_update_windows = false;
+ base::RepeatingClosure update_windows_closure = base::BindRepeating(
+ [](bool* saw_update_windows, BridgedContentView* view,
+ Textfield* textfield) {
+ // Ensure updateWindows is not invoked recursively.
+ EXPECT_FALSE(*saw_update_windows);
+ *saw_update_windows = true;
+
+ // Inside updateWindows, assume the IME got dismissed and wants to
+ // insert its last bit of text for the old input context.
+ [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+
+ // This is triggered by the setTextInputClient:nullptr in
+ // SetHandleKeyEventCallback(), so -inputContext should also be nil.
+ EXPECT_FALSE([view inputContext]);
+
+ // Ensure we can't recursively call updateWindows. A TextInputClient
+ // reacting to InsertChar could theoretically do this, but toolkit-views
+ // DCHECKs if there is recursive event dispatch, so call
+ // setTextInputClient directly.
+ [view setTextInputClient:textfield];
+
+ // Finally simulate what -[NSApp updateWindows] should _actually_ do,
+ // which is to update the input context (from the first responder).
+ g_fake_current_input_context = [view inputContext];
+
+ // Now, the |textfield| set above should have been set again.
+ EXPECT_TRUE(g_fake_current_input_context);
+ },
+ &saw_update_windows, ns_view_, textfield);
+
+ SetHandleKeyEventCallback(base::BindRepeating(
+ [](int* saw_return_count, BridgedContentView* view, Textfield* textfield,
+ const ui::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_RETURN) {
+ *saw_return_count += 1;
+ // Simulate Textfield::OnBlur() by clearing the input method.
+ // Textfield needs to be in a Widget to do this normally.
+ [view setTextInputClient:nullptr];
+ }
+ return false;
+ },
+ &vkey_return_count, ns_view_));
+
+ // Starting text (just insert it).
+ [ns_view_ insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
+
+ EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->text());
+
+ g_fake_interpret_key_events = &generate_return_and_fake_ime;
+ g_update_windows_closure = &update_windows_closure;
+ g_fake_current_input_context = [ns_view_ inputContext];
+ EXPECT_TRUE(g_fake_current_input_context);
+ [ns_view_ keyDown:return_with_fake_ime];
+
+ // We should see one VKEY_RETURNs and one updateWindows. In particular, note
+ // that there is not a second VKEY_RETURN seen generated by keyDown: thinking
+ // the event has been unhandled. This is because it was handled when the fake
+ // IME sent \r.
+ EXPECT_TRUE(saw_update_windows);
+ EXPECT_EQ(1, vkey_return_count);
+
+ // The text inserted during updateWindows should have been inserted, even
+ // though we were trying to change the input context.
+ EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+
+ EXPECT_TRUE(g_fake_current_input_context);
+
+ g_fake_current_input_context = nullptr;
+ g_fake_interpret_key_events = nullptr;
+ g_update_windows_closure = nullptr;
+}
+
typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
// Simulate the notifications that AppKit would send out if a fullscreen
diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture.mm b/chromium/ui/views/cocoa/cocoa_mouse_capture.mm
index ea5bbc72139..b1f3cb216de 100644
--- a/chromium/ui/views/cocoa/cocoa_mouse_capture.mm
+++ b/chromium/ui/views/cocoa/cocoa_mouse_capture.mm
@@ -71,12 +71,14 @@ NSWindow* CocoaMouseCapture::ActiveEventTap::GetGlobalCaptureWindow() {
}
void CocoaMouseCapture::ActiveEventTap::Init() {
+ // Consume most things, but not NSMouseEntered/Exited: The Widget doing
+ // capture will still see its own Entered/Exit events, but not those for other
+ // NSViews, since consuming those would break their tracking area logic.
NSEventMask event_mask =
NSLeftMouseDownMask | NSLeftMouseUpMask | NSRightMouseDownMask |
NSRightMouseUpMask | NSMouseMovedMask | NSLeftMouseDraggedMask |
- NSRightMouseDraggedMask | NSMouseEnteredMask | NSMouseExitedMask |
- NSScrollWheelMask | NSOtherMouseDownMask | NSOtherMouseUpMask |
- NSOtherMouseDraggedMask;
+ NSRightMouseDraggedMask | NSScrollWheelMask | NSOtherMouseDownMask |
+ NSOtherMouseUpMask | NSOtherMouseDraggedMask;
// Capture a WeakPtr via NSObject. This allows the block to detect another
// event monitor for the same event deleting |owner_|.
diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.mm b/chromium/ui/views/cocoa/drag_drop_client_mac.mm
index ce3f37b36a9..78e7b0b3226 100644
--- a/chromium/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/chromium/ui/views/cocoa/drag_drop_client_mac.mm
@@ -104,6 +104,12 @@ void DragDropClientMac::StartDragAndDrop(
NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace(
provider.GetDragImage(), base::mac::GetSRGBColorSpace());
+ // TODO(crbug/876201): This shouldn't happen. When a repro for this
+ // is identified and the bug is fixed, change the early return to
+ // a DCHECK.
+ if (!image || NSEqualSizes([image size], NSZeroSize))
+ return;
+
base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
[item setDataProvider:data_source_.get()
forTypes:provider.GetAvailableTypes()];
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 54989956996..dd5edea74a8 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,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#import "ui/base/clipboard/clipboard_util_mac.h"
+#include "ui/gfx/image/image_unittest_util.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view.h"
@@ -241,6 +242,8 @@ TEST_F(DragDropClientMacTest, ReleaseCapture) {
OSExchangeData data;
const base::string16& text = ASCIIToUTF16("text");
data.SetString(text);
+ data.provider().SetDragImage(gfx::test::CreateImageSkia(100, 100),
+ gfx::Vector2d());
SetData(data);
// There's no way to cleanly stop NSDraggingSession inside unit tests, so just
diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.h b/chromium/ui/views/cocoa/native_widget_mac_nswindow.h
index adc6b041693..8a0f51b791b 100644
--- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.h
+++ b/chromium/ui/views/cocoa/native_widget_mac_nswindow.h
@@ -9,6 +9,25 @@
#import "ui/base/cocoa/command_dispatcher.h"
#include "ui/views/views_export.h"
+#include "ui/views/widget/util_mac.h"
+
+@protocol WindowTouchBarDelegate;
+
+// Weak lets Chrome launch even if a future macOS doesn't have the below classes
+
+WEAK_IMPORT_ATTRIBUTE
+@interface NSNextStepFrame : NSView
+@end
+
+@class NSThemeFrame;
+
+VIEWS_EXPORT
+@interface NativeWidgetMacNSWindowBorderlessFrame : NSNextStepFrame
+@end
+
+VIEWS_EXPORT
+@interface NativeWidgetMacNSWindowTitledFrame : NSThemeFrame
+@end
// The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that
// can only be accomplished by overriding methods.
@@ -18,6 +37,16 @@ VIEWS_EXPORT
// Set a CommandDispatcherDelegate, i.e. to implement key event handling.
- (void)setCommandDispatcherDelegate:(id<CommandDispatcherDelegate>)delegate;
+// Selector passed to [NSApp beginSheet:]. Forwards to [self delegate], if set.
+- (void)sheetDidEnd:(NSWindow*)sheet
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo;
+
+// Set a WindowTouchBarDelegate to allow creation of a custom TouchBar when
+// AppKit follows the responder chain and reaches the NSWindow when trying to
+// create one.
+- (void)setWindowTouchBarDelegate:(id<WindowTouchBarDelegate>)delegate;
+
@end
#endif // UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm
index 0f44dc785d3..ea244d97722 100644
--- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm
+++ b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm
@@ -6,15 +6,22 @@
#include "base/mac/foundation_util.h"
#import "base/mac/sdk_forward_declarations.h"
-#import "ui/views/cocoa/bridged_native_widget.h"
#import "ui/base/cocoa/user_interface_item_command_handler.h"
+#import "ui/views/cocoa/bridged_native_widget.h"
#import "ui/views/cocoa/views_nswindow_delegate.h"
+#import "ui/views/cocoa/window_touch_bar_delegate.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/widget/widget_delegate.h"
@interface NSWindow (Private)
++ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle;
- (BOOL)hasKeyAppearance;
+- (long long)_resizeDirectionForMouseLocation:(CGPoint)location;
+
+// Available in later point releases of 10.10. On 10.11+, use the public
+// -performWindowDragWithEvent: instead.
+- (void)beginWindowDragWithEvent:(NSEvent*)event;
@end
@interface NativeWidgetMacNSWindow ()
@@ -28,10 +35,53 @@
- (BOOL)_isTitleHidden;
@end
+// Use this category to implement mouseDown: on multiple frame view classes
+// with different superclasses.
+@interface NSView (CRFrameViewAdditions)
+- (void)cr_mouseDownOnFrameView:(NSEvent*)event;
+@end
+
+@implementation NSView (CRFrameViewAdditions)
+// If a mouseDown: falls through to the frame view, turn it into a window drag.
+- (void)cr_mouseDownOnFrameView:(NSEvent*)event {
+ if ([self.window _resizeDirectionForMouseLocation:event.locationInWindow] !=
+ -1)
+ return;
+ if (@available(macOS 10.11, *))
+ [self.window performWindowDragWithEvent:event];
+ else if ([self.window
+ respondsToSelector:@selector(beginWindowDragWithEvent:)])
+ [self.window beginWindowDragWithEvent:event];
+ else
+ NOTREACHED();
+}
+@end
+
+@implementation NativeWidgetMacNSWindowTitledFrame
+- (void)mouseDown:(NSEvent*)event {
+ [self cr_mouseDownOnFrameView:event];
+ [super mouseDown:event];
+}
+- (BOOL)usesCustomDrawing {
+ return NO;
+}
+@end
+
+@implementation NativeWidgetMacNSWindowBorderlessFrame
+- (void)mouseDown:(NSEvent*)event {
+ [self cr_mouseDownOnFrameView:event];
+ [super mouseDown:event];
+}
+- (BOOL)usesCustomDrawing {
+ return NO;
+}
+@end
+
@implementation NativeWidgetMacNSWindow {
@private
base::scoped_nsobject<CommandDispatcher> commandDispatcher_;
base::scoped_nsprotocol<id<UserInterfaceItemCommandHandler>> commandHandler_;
+ id<WindowTouchBarDelegate> touchBarDelegate_; // Weak.
}
- (instancetype)initWithContentRect:(NSRect)contentRect
@@ -59,6 +109,21 @@
[commandDispatcher_ setDelegate:delegate];
}
+- (void)sheetDidEnd:(NSWindow*)sheet
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo {
+ // Note BridgedNativeWidget may have cleared [self delegate], in which case
+ // this will no-op. This indirection is necessary to handle AppKit invoking
+ // this selector via a posted task. See https://crbug.com/851376.
+ [[self viewsNSWindowDelegate] sheetDidEnd:sheet
+ returnCode:returnCode
+ contextInfo:contextInfo];
+}
+
+- (void)setWindowTouchBarDelegate:(id<WindowTouchBarDelegate>)delegate {
+ touchBarDelegate_ = delegate;
+}
+
// Private methods.
- (ViewsNSWindowDelegate*)viewsNSWindowDelegate {
@@ -82,6 +147,17 @@
// NSWindow overrides.
++ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
+ if (windowStyle & NSWindowStyleMaskTitled) {
+ if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class])
+ return customFrame;
+ } else if (Class customFrame =
+ [NativeWidgetMacNSWindowBorderlessFrame class]) {
+ return customFrame;
+ }
+ return [super frameViewClassForStyleMask:windowStyle];
+}
+
- (BOOL)_isTitleHidden {
if (![self delegate])
return NO;
@@ -89,6 +165,13 @@
return ![self viewsWidget]->widget_delegate()->ShouldShowWindowTitle();
}
+// The base implementation returns YES if the window's frame view is a custom
+// class, which causes undesirable changes in behavior. AppKit NSWindow
+// subclasses are known to override it and return NO.
+- (BOOL)_usesCustomDrawing {
+ return NO;
+}
+
// Ignore [super canBecome{Key,Main}Window]. The default is NO for windows with
// NSBorderlessWindowMask, which is not the desired behavior.
// Note these can be called via -[NSWindow close] while the widget is being torn
@@ -172,14 +255,18 @@
[super cursorUpdate:theEvent];
}
+- (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2)) {
+ return touchBarDelegate_ ? [touchBarDelegate_ makeTouchBar] : nil;
+}
+
// CommandDispatchingWindow implementation.
- (void)setCommandHandler:(id<UserInterfaceItemCommandHandler>)commandHandler {
commandHandler_.reset([commandHandler retain]);
}
-- (BOOL)redispatchKeyEvent:(NSEvent*)event {
- return [commandDispatcher_ redispatchKeyEvent:event];
+- (CommandDispatcher*)commandDispatcher {
+ return commandDispatcher_.get();
}
- (BOOL)defaultPerformKeyEquivalent:(NSEvent*)event {
@@ -210,6 +297,9 @@
// NSWindow overrides (NSAccessibility informal protocol implementation).
- (id)accessibilityFocusedUIElement {
+ if (![self delegate])
+ return [super accessibilityFocusedUIElement];
+
// The SDK documents this as "The deepest descendant of the accessibility
// hierarchy that has the focus" and says "if a child element does not have
// the focus, either return self or, if available, invoke the superclass's
diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.mm b/chromium/ui/views/cocoa/views_nswindow_delegate.mm
index 96acf216de7..9472e5e4043 100644
--- a/chromium/ui/views/cocoa/views_nswindow_delegate.mm
+++ b/chromium/ui/views/cocoa/views_nswindow_delegate.mm
@@ -4,8 +4,8 @@
#import "ui/views/cocoa/views_nswindow_delegate.h"
+#include "base/bind.h"
#include "base/logging.h"
-#import "base/mac/bind_objc_block.h"
#include "base/threading/thread_task_runner_handle.h"
#import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/bridged_native_widget.h"
@@ -34,7 +34,40 @@
return;
cursor_.reset([newCursor retain]);
- [parent_->ns_window() resetCursorRects];
+
+ // The window has a tracking rect that was installed in -[BridgedContentView
+ // initWithView:] that uses the NSTrackingCursorUpdate option. In the case
+ // where the window is the key window, that tracking rect will cause
+ // -cursorUpdate: to be sent up the responder chain, which will cause the
+ // cursor to be set when the message gets to the NativeWidgetMacNSWindow.
+ NSWindow* window = parent_->ns_window();
+ [window resetCursorRects];
+
+ // However, if this window isn't the key window, that tracking area will have
+ // no effect. This is good if this window is just some top-level window that
+ // isn't key, but isn't so good if this window isn't key but is a child window
+ // of a window that is key. To handle that case, the case where the
+ // -cursorUpdate: message will never be sent, just set the cursor here.
+ //
+ // Only do this for non-key windows so that there will be no flickering
+ // between cursors set here and set elsewhere.
+ //
+ // (This is a known issue; see https://stackoverflow.com/questions/45712066/.)
+ if (![window isKeyWindow]) {
+ NSWindow* currentWindow = window;
+ // Walk up the window chain. If there is a key window in the window parent
+ // chain, then work around the issue and set the cursor.
+ while (true) {
+ NSWindow* parentWindow = [currentWindow parentWindow];
+ if (!parentWindow)
+ break;
+ currentWindow = parentWindow;
+ if ([currentWindow isKeyWindow]) {
+ [(newCursor ? newCursor : [NSCursor arrowCursor]) set];
+ break;
+ }
+ }
+ }
}
- (void)onWindowOrderChanged:(NSNotification*)notification {
@@ -48,10 +81,6 @@
- (void)sheetDidEnd:(NSWindow*)sheet
returnCode:(NSInteger)returnCode
contextInfo:(void*)contextInfo {
- // |parent_| will be null when triggered from the block in -windowWillClose:.
- if (!parent_)
- return;
-
[sheet orderOut:nil];
parent_->OnWindowWillClose();
}
@@ -99,11 +128,6 @@
}
- (void)windowWillClose:(NSNotification*)notification {
- // Retain |self|. |parent_| should be cleared. OnWindowWillClose() may delete
- // |parent_|, but it may also dealloc |self| before returning. However, the
- // observers it notifies before that need a valid |parent_| on the delegate,
- // so it can only be cleared after OnWindowWillClose() returns.
- base::scoped_nsobject<NSObject> keepAlive(self, base::scoped_policy::RETAIN);
NSWindow* window = parent_->ns_window();
if (NSWindow* sheetParent = [window sheetParent]) {
// On no! Something called -[NSWindow close] on a sheet rather than calling
@@ -111,20 +135,23 @@
// then the parent will never be able to show another sheet. But calling
// -endSheet: here will block the thread with an animation, so post a task.
// Use a block: The argument to -endSheet: must be retained, since it's the
- // window that is closing and -performSelector: won't retain the argument.
- // The NSWindowDelegate (i.e. |self|) must also be explicitly retained. Even
- // though the call to OnWindowWillClose() below will remove |self| as the
- // NSWindow delegate, the call to -[NSApp beginSheet:] also took a weak
- // reference to the delegate, which will be destroyed when the sheet's
- // BridgedNativeWidget is destroyed.
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindBlock(^{
- [sheetParent endSheet:window];
- [[self retain] release]; // Force |self| to be retained for the block.
- }));
+ // window that is closing and -performSelector: won't retain the argument
+ // (putting |window| on the stack above causes this block to retain it).
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(base::RetainBlock(^{
+ [sheetParent endSheet:window];
+ })));
}
DCHECK([window isEqual:[notification object]]);
parent_->OnWindowWillClose();
- parent_ = nullptr;
+ // |self| may be deleted here (it's NSObject, so who really knows).
+ // |parent_| _will_ be deleted for sure.
+
+ // Note OnWindowWillClose() will clear the NSWindow delegate. That is, |self|.
+ // That guarantees that the task possibly-posted above will never call into
+ // our -sheetDidEnd:. (The task's purpose is just to unblock the modal session
+ // on the parent window.)
+ DCHECK(![window delegate]);
}
- (void)windowDidMiniaturize:(NSNotification*)notification {
diff --git a/chromium/ui/views/cocoa/window_touch_bar_delegate.h b/chromium/ui/views/cocoa/window_touch_bar_delegate.h
new file mode 100644
index 00000000000..daef5466ed6
--- /dev/null
+++ b/chromium/ui/views/cocoa/window_touch_bar_delegate.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_
+#define UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/availability.h"
+
+// Bridge delegate class for NativeWidgetMacNSWindow and
+// BrowserWindowTouchBarMac.
+@protocol WindowTouchBarDelegate<NSObject>
+
+// Creates and returns a touch bar for the browser window.
+- (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2));
+
+@end
+
+#endif // UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_ \ No newline at end of file
diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc
index 5fa2f6f1ebb..f73a3102e63 100644
--- a/chromium/ui/views/controls/button/button.cc
+++ b/chromium/ui/views/controls/button/button.cc
@@ -91,6 +91,7 @@ void Button::SetTooltipText(const base::string16& tooltip_text) {
tooltip_text_ = tooltip_text;
if (accessible_name_.empty())
accessible_name_ = tooltip_text_;
+ OnSetTooltipText(tooltip_text);
TooltipTextChanged();
}
@@ -515,6 +516,8 @@ void Button::OnClickCanceled(const ui::Event& event) {
}
}
+void Button::OnSetTooltipText(const base::string16& tooltip_text) {}
+
void Button::StateChanged(ButtonState old_state) {}
bool Button::IsTriggerableEvent(const ui::Event& event) {
diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h
index 6f0ce20627b..b5347ff0e00 100644
--- a/chromium/ui/views/controls/button/button.h
+++ b/chromium/ui/views/controls/button/button.h
@@ -211,6 +211,9 @@ class VIEWS_EXPORT Button : public InkDropHostView,
// events.
virtual void OnClickCanceled(const ui::Event& event);
+ // Called when the tooltip is set.
+ virtual void OnSetTooltipText(const base::string16& tooltip_text);
+
// Invoked from SetState() when SetState() is passed a value that differs from
// the current node_data. Button's implementation of StateChanged() does
// nothing; this method is provided for subclasses that wish to do something
@@ -247,6 +250,8 @@ class VIEWS_EXPORT Button : public InkDropHostView,
return hover_animation_;
}
+ FocusRing* focus_ring() { return focus_ring_.get(); }
+
// The button's listener. Notified when clicked.
ButtonListener* listener_;
diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc
index cfe84643274..5dc51b40272 100644
--- a/chromium/ui/views/controls/button/checkbox.cc
+++ b/chromium/ui/views/controls/button/checkbox.cc
@@ -33,8 +33,10 @@ namespace views {
// static
const char Checkbox::kViewClassName[] = "Checkbox";
-Checkbox::Checkbox(const base::string16& label, bool force_md)
- : LabelButton(NULL, label),
+Checkbox::Checkbox(const base::string16& label,
+ ButtonListener* listener,
+ bool force_md)
+ : LabelButton(listener, label),
checked_(false),
label_ax_id_(0),
use_md_(force_md ||
@@ -117,7 +119,7 @@ void Checkbox::SetAssociatedLabel(View* labelling_view) {
ui::AXNodeData node_data;
labelling_view->GetAccessibleNodeData(&node_data);
// TODO(aleventhal) automatically handle setting the name from the related
- // label in view_accessibility and have it update the name if the text of the
+ // label in ViewAccessibility and have it update the name if the text of the
// associated label changes.
SetAccessibleName(
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
@@ -223,7 +225,7 @@ void Checkbox::Layout() {
SkPath Checkbox::GetFocusRingPath() const {
SkPath path;
- gfx::Rect bounds = image()->bounds();
+ gfx::Rect bounds = image()->GetMirroredBounds();
bounds.Inset(1, 1);
path.addRect(RectToSkRect(bounds));
return path;
diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h
index de681d69271..6fb4c93b61a 100644
--- a/chromium/ui/views/controls/button/checkbox.h
+++ b/chromium/ui/views/controls/button/checkbox.h
@@ -27,13 +27,11 @@ class VIEWS_EXPORT Checkbox : public LabelButton {
static const char kViewClassName[];
// |force_md| forces MD even when --secondary-ui-md flag is not set.
- explicit Checkbox(const base::string16& label, bool force_md = false);
+ explicit Checkbox(const base::string16& label,
+ ButtonListener* listener = nullptr,
+ bool force_md = false);
~Checkbox() override;
- // Sets a listener for this checkbox. Checkboxes aren't required to have them
- // since their state can be read independently of them being toggled.
- void set_listener(ButtonListener* listener) { listener_ = listener; }
-
// Sets/Gets whether or not the checkbox is checked.
virtual void SetChecked(bool checked);
bool checked() const { return checked_; }
diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc
index 677d4dc3d84..851a2079c80 100644
--- a/chromium/ui/views/controls/button/image_button.cc
+++ b/chromium/ui/views/controls/button/image_button.cc
@@ -305,4 +305,8 @@ void ToggleImageButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
}
}
+bool ToggleImageButton::toggled_for_testing() const {
+ return toggled_;
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h
index bc69e6e350b..6e5b0cb292f 100644
--- a/chromium/ui/views/controls/button/image_button.h
+++ b/chromium/ui/views/controls/button/image_button.h
@@ -161,6 +161,8 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton {
base::string16* tooltip) 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.
diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc
index e12bae6e2d9..969b67c86c5 100644
--- a/chromium/ui/views/controls/button/menu_button_unittest.cc
+++ b/chromium/ui/views/controls/button/menu_button_unittest.cc
@@ -264,7 +264,7 @@ class TestDragDropClient : public aura::client::DragDropClient,
// True while receiving ui::LocatedEvents for drag operations.
bool drag_in_progress_;
- // Target window where drag operations are occuring.
+ // Target window where drag operations are occurring.
aura::Window* target_;
DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
@@ -411,7 +411,7 @@ TEST_F(MenuButtonTest, ButtonStateForMenuButtonsWithPressedLocks) {
pressed_lock1.reset();
EXPECT_EQ(Button::STATE_PRESSED, button()->state());
- // Reseting the final lock should return the button's state to normal...
+ // Resetting the final lock should return the button's state to normal...
pressed_lock2.reset();
EXPECT_EQ(Button::STATE_NORMAL, button()->state());
@@ -568,8 +568,8 @@ TEST_F(MenuButtonTest,
// Tests that the MenuButton does not become pressed if it can be dragged, and a
// DragDropClient is processing the events.
TEST_F(MenuButtonTest, DraggableMenuButtonDoesNotActivateOnDrag) {
- // TODO: test uses GetContext(), which is not applicable to aura-mus.
- // http://crbug.com/663809.
+ // TODO(https://crbug.com/663809): test uses GetContext(), which is not
+ // applicable to aura-mus.
if (IsMus())
return;
TestMenuButtonListener menu_button_listener;
diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc
index efadd78b3e9..b9dbdbe82dc 100644
--- a/chromium/ui/views/controls/button/radio_button.cc
+++ b/chromium/ui/views/controls/button/radio_button.cc
@@ -22,7 +22,7 @@ const char RadioButton::kViewClassName[] = "RadioButton";
RadioButton::RadioButton(const base::string16& label,
int group_id,
bool force_md)
- : Checkbox(label, force_md) {
+ : Checkbox(label, nullptr, force_md) {
SetGroup(group_id);
if (!UseMd()) {
@@ -163,7 +163,7 @@ const gfx::VectorIcon& RadioButton::GetVectorIcon() const {
SkPath RadioButton::GetFocusRingPath() const {
SkPath path;
- path.addOval(gfx::RectToSkRect(image()->bounds()));
+ path.addOval(gfx::RectToSkRect(image()->GetMirroredBounds()));
return path;
}
diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc
index c17bc868e2f..148c22ae909 100644
--- a/chromium/ui/views/controls/focus_ring.cc
+++ b/chromium/ui/views/controls/focus_ring.cc
@@ -34,12 +34,12 @@ std::unique_ptr<FocusRing> FocusRing::Install(View* parent) {
// static
bool FocusRing::IsPathUseable(const SkPath& path) {
- return path.isRect(nullptr) || path.isOval(nullptr) || path.isRRect(nullptr);
+ return !path.isEmpty() && (path.isRect(nullptr) || path.isOval(nullptr) ||
+ path.isRRect(nullptr));
}
void FocusRing::SetPath(const SkPath& path) {
- DCHECK(IsPathUseable(path));
- path_ = path;
+ path_ = IsPathUseable(path) ? path : SkPath();
SchedulePaint();
}
diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h
index 1b4d99ca2b1..a608042b24f 100644
--- a/chromium/ui/views/controls/focus_ring.h
+++ b/chromium/ui/views/controls/focus_ring.h
@@ -49,11 +49,15 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
static std::unique_ptr<FocusRing> Install(View* parent);
// Returns whether this class can draw a focus ring from |path|. Not all paths
- // are useable since not all paths can be easily outset.
+ // are useable since not all paths can be easily outset. If a FocusRing is
+ // configured to use an unuseable path, it will fall back to the default focus
+ // ring path.
static bool IsPathUseable(const SkPath& path);
// Sets the path to draw this FocusRing around. This path is in the parent
- // view's coordinate system, *not* in the FocusRing's coordinate system.
+ // view's coordinate system, *not* in the FocusRing's coordinate system. Note
+ // that this path will not be mirrored in RTL, so your View's computation of
+ // it should take RTL into account.
void SetPath(const SkPath& path);
// Sets whether the FocusRing should show an invalid state for the View it
diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc
index 5b7f65a5522..b1abacf1e0d 100644
--- a/chromium/ui/views/controls/image_view.cc
+++ b/chromium/ui/views/controls/image_view.cc
@@ -9,6 +9,7 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/paint/paint_flags.h"
+#include "skia/ext/image_operations.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
@@ -43,6 +44,7 @@ void ImageView::SetImage(const gfx::ImageSkia& img) {
last_painted_bitmap_pixels_ = nullptr;
gfx::Size pref_size(GetPreferredSize());
image_ = img;
+ scaled_image_ = gfx::ImageSkia();
if (pref_size != GetPreferredSize())
PreferredSizeChanged();
SchedulePaint();
@@ -207,25 +209,50 @@ void ImageView::OnPaintImage(gfx::Canvas* canvas) {
last_paint_scale_ = canvas->image_scale();
last_painted_bitmap_pixels_ = nullptr;
- if (image_.isNull())
+ gfx::ImageSkia image = GetPaintImage(last_paint_scale_);
+ if (image.isNull())
return;
gfx::Rect image_bounds(GetImageBounds());
if (image_bounds.IsEmpty())
return;
- if (image_bounds.size() != gfx::Size(image_.width(), image_.height())) {
+ if (image_bounds.size() != gfx::Size(image.width(), image.height())) {
// Resize case
cc::PaintFlags flags;
flags.setFilterQuality(kLow_SkFilterQuality);
- canvas->DrawImageInt(image_, 0, 0, image_.width(), image_.height(),
+ canvas->DrawImageInt(image, 0, 0, image.width(), image.height(),
image_bounds.x(), image_bounds.y(),
image_bounds.width(), image_bounds.height(), true,
flags);
} else {
- canvas->DrawImageInt(image_, image_bounds.x(), image_bounds.y());
+ canvas->DrawImageInt(image, image_bounds.x(), image_bounds.y());
}
- last_painted_bitmap_pixels_ = GetBitmapPixels(image_, last_paint_scale_);
+ last_painted_bitmap_pixels_ = GetBitmapPixels(image, last_paint_scale_);
+}
+
+gfx::ImageSkia ImageView::GetPaintImage(float scale) {
+ if (image_.isNull())
+ return image_;
+
+ const gfx::ImageSkiaRep& rep = image_.GetRepresentation(scale);
+ if (rep.scale() == scale)
+ return image_;
+
+ if (scaled_image_.HasRepresentation(scale))
+ return scaled_image_;
+
+ // Only caches one image rep for the current scale.
+ scaled_image_ = gfx::ImageSkia();
+
+ gfx::Size scaled_size =
+ gfx::ScaleToCeiledSize(rep.pixel_size(), scale / rep.scale());
+ scaled_image_.AddRepresentation(gfx::ImageSkiaRep(
+ skia::ImageOperations::Resize(rep.sk_bitmap(),
+ skia::ImageOperations::RESIZE_BEST,
+ scaled_size.width(), scaled_size.height()),
+ scale));
+ return scaled_image_;
}
} // namespace views
diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h
index c19a3c5ea8e..c3adf654e36 100644
--- a/chromium/ui/views/controls/image_view.h
+++ b/chromium/ui/views/controls/image_view.h
@@ -87,6 +87,9 @@ class VIEWS_EXPORT ImageView : public View {
void OnPaintImage(gfx::Canvas* canvas);
+ // Gets an ImageSkia to paint that has proper rep for |scale|.
+ gfx::ImageSkia GetPaintImage(float scale);
+
// Returns true if |img| is the same as the last image we painted. This is
// intended to be a quick check, not exhaustive. In other words it's possible
// for this to return false even though the images are in fact equal.
@@ -105,6 +108,9 @@ class VIEWS_EXPORT ImageView : public View {
// The underlying image.
gfx::ImageSkia image_;
+ // Caches the scaled image reps.
+ gfx::ImageSkia scaled_image_;
+
// Horizontal alignment.
Alignment horizontal_alignment_;
diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc
index ec25ede20df..e5eafe1bd53 100644
--- a/chromium/ui/views/controls/label_unittest.cc
+++ b/chromium/ui/views/controls/label_unittest.cc
@@ -16,6 +16,7 @@
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
+#include "ui/base/ui_base_switches.h"
#include "ui/compositor/canvas_painter.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/test/event_generator.h"
@@ -276,10 +277,14 @@ class MDLabelTest : public LabelTest,
// LabelTest:
void SetUp() override {
- if (GetParam() == SecondaryUiMode::MD)
+ if (GetParam() == SecondaryUiMode::MD) {
scoped_feature_list_.InitAndEnableFeature(features::kSecondaryUiMd);
- else
+ } else {
+ // Force Refresh UI to be off, since that mode implies MD secondary UI.
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kTopChromeMD, switches::kTopChromeMDMaterial);
scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+ }
LabelTest::SetUp();
}
diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc
index c398009a47d..db532134db5 100644
--- a/chromium/ui/views/controls/menu/menu_config.cc
+++ b/chromium/ui/views/controls/menu/menu_config.cc
@@ -9,11 +9,12 @@
#include "ui/views/controls/menu/menu_image_util.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/round_rect_painter.h"
+
namespace views {
MenuConfig::MenuConfig()
: arrow_color(SK_ColorBLACK),
- menu_vertical_border_size(3),
+ menu_vertical_border_size(4),
menu_horizontal_border_size(views::RoundRectPainter::kBorderWidth),
submenu_horizontal_inset(3),
item_top_margin(4),
@@ -23,12 +24,12 @@ MenuConfig::MenuConfig()
minimum_text_item_height(0),
minimum_container_item_height(0),
minimum_menu_width(0),
- item_left_margin(10),
- touchable_item_left_margin(16),
- label_to_arrow_padding(10),
+ // TODO(ftirelo): Paddings should come from the layout provider, once
+ // Harmony is the default behavior.
+ item_horizontal_padding(8),
+ touchable_item_horizontal_padding(16),
+ label_to_arrow_padding(8),
arrow_to_edge_padding(5),
- icon_to_label_padding(10),
- touchable_icon_to_label_padding(16),
touchable_icon_size(20),
touchable_icon_color(SkColorSetRGB(0x5F, 0x63, 0x60)),
check_width(kMenuCheckSize),
@@ -44,7 +45,6 @@ MenuConfig::MenuConfig()
show_mnemonics(false),
use_mnemonics(true),
scroll_arrow_height(3),
- label_to_minor_text_padding(10),
item_min_height(0),
actionable_submenu_arrow_to_edge_padding(14),
actionable_submenu_width(37),
diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h
index c07195b1be6..df02a976c59 100644
--- a/chromium/ui/views/controls/menu/menu_config.h
+++ b/chromium/ui/views/controls/menu/menu_config.h
@@ -64,11 +64,11 @@ struct VIEWS_EXPORT MenuConfig {
int minimum_container_item_height;
int minimum_menu_width;
- // Margins between the left of the item and the icon.
- int item_left_margin;
+ // Horizontal padding between components in a menu item.
+ int item_horizontal_padding;
- // Margins between the left of the touchable item and the icon.
- int touchable_item_left_margin;
+ // Horizontal padding between components in a touchable menu item.
+ int touchable_item_horizontal_padding;
// Padding between the label and submenu arrow.
int label_to_arrow_padding;
@@ -76,12 +76,6 @@ struct VIEWS_EXPORT MenuConfig {
// Padding between the arrow and the edge.
int arrow_to_edge_padding;
- // Padding between the icon and label.
- int icon_to_label_padding;
-
- // Padding between the icon and label for touchable menu items.
- int touchable_icon_to_label_padding;
-
// The icon size used for icons in touchable menu items.
int touchable_icon_size;
@@ -127,10 +121,6 @@ struct VIEWS_EXPORT MenuConfig {
// Height of the scroll arrow.
int scroll_arrow_height;
- // Padding between the label and minor text. Only used if there is an
- // accelerator or sublabel.
- int label_to_minor_text_padding;
-
// Minimum height of menu item.
int item_min_height;
diff --git a/chromium/ui/views/controls/menu/menu_config_chromeos.cc b/chromium/ui/views/controls/menu/menu_config_chromeos.cc
index 75250e7c43f..085c1adb937 100644
--- a/chromium/ui/views/controls/menu/menu_config_chromeos.cc
+++ b/chromium/ui/views/controls/menu/menu_config_chromeos.cc
@@ -17,8 +17,6 @@ void MenuConfig::Init() {
separator_spacing_height = 7;
separator_lower_height = 8;
separator_upper_height = 8;
- label_to_arrow_padding = 20;
- label_to_minor_text_padding = 20;
always_use_icon_to_label_padding = true;
align_arrow_and_shortcut = true;
offset_context_menus = true;
diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm
index 7854af9d373..90939262b2e 100644
--- a/chromium/ui/views/controls/menu/menu_config_mac.mm
+++ b/chromium/ui/views/controls/menu/menu_config_mac.mm
@@ -14,25 +14,21 @@ namespace {
void InitMaterialMenuConfig(views::MenuConfig* config) {
// These config parameters are from https://crbug.com/829347 and the spec
// images linked from that bug.
- config->menu_vertical_border_size = 8;
config->menu_horizontal_border_size = 0;
config->submenu_horizontal_inset = 0;
- config->minimum_text_item_height = 32;
- config->minimum_container_item_height = 48;
+ config->minimum_text_item_height = 28;
+ config->minimum_container_item_height = 40;
config->minimum_menu_width = 320;
- config->item_left_margin = 8;
config->label_to_arrow_padding = 0;
config->arrow_to_edge_padding = 16;
- config->icon_to_label_padding = 8;
config->check_width = 16;
config->check_height = 16;
config->arrow_width = 8;
- config->separator_height = 17;
- config->separator_lower_height = 9;
- config->separator_upper_height = 9;
- config->separator_spacing_height = 9;
+ config->separator_height = 9;
+ config->separator_lower_height = 4;
+ config->separator_upper_height = 4;
+ config->separator_spacing_height = 5;
config->separator_thickness = 1;
- config->label_to_minor_text_padding = 8;
config->align_arrow_and_shortcut = true;
config->use_outer_border = false;
config->icons_in_label = true;
diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc
index 00590f09d91..beecb7f2717 100644
--- a/chromium/ui/views/controls/menu/menu_controller.cc
+++ b/chromium/ui/views/controls/menu/menu_controller.cc
@@ -10,6 +10,7 @@
#include "base/i18n/case_conversion.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
+#include "base/numerics/ranges.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -77,7 +78,7 @@ const int kCloseOnExitTime = 1200;
// If a context menu is invoked by touch, we shift the menu by this offset so
// that the finger does not obscure the menu.
-const int kCenteredContextMenuYOffset = -15;
+const int kTouchYPadding = 15;
// The spacing offset for the bubble tip.
const int kBubbleTipSizeLeftRight = 12;
@@ -311,8 +312,8 @@ class MenuController::MenuScrollTask {
if (!scrolling_timer_.IsRunning()) {
scrolling_timer_.Start(FROM_HERE,
- TimeDelta::FromMilliseconds(kScrollTimerMS),
- this, &MenuScrollTask::Run);
+ TimeDelta::FromMilliseconds(kScrollTimerMS), this,
+ &MenuScrollTask::Run);
}
}
@@ -333,9 +334,10 @@ class MenuController::MenuScrollTask {
const int delta_y = static_cast<int>(
(base::Time::Now() - start_scroll_time_).InMilliseconds() *
pixels_per_second_ / 1000);
- vis_rect.set_y(is_scrolling_up_ ?
- std::max(0, start_y_ - delta_y) :
- std::min(submenu_->height() - vis_rect.height(), start_y_ + delta_y));
+ vis_rect.set_y(is_scrolling_up_
+ ? std::max(0, start_y_ - delta_y)
+ : std::min(submenu_->height() - vis_rect.height(),
+ start_y_ + delta_y));
submenu_->ScrollRectToVisible(vis_rect);
}
@@ -367,8 +369,7 @@ struct MenuController::SelectByCharDetails {
: first_match(-1),
has_multiple(false),
index_of_item(-1),
- next_match(-1) {
- }
+ next_match(-1) {}
// Index of the first menu with the specified mnemonic.
int first_match;
@@ -392,8 +393,7 @@ MenuController::State::State()
hot_button(nullptr),
submenu_open(false),
anchor(MENU_ANCHOR_TOPLEFT),
- context_menu(false) {
-}
+ context_menu(false) {}
MenuController::State::State(const State& other) = default;
@@ -424,16 +424,16 @@ void MenuController::Run(Widget* parent,
menu_start_time_ = base::TimeTicks::Now();
menu_start_mouse_press_loc_ = gfx::Point();
+ ui::Event* event = nullptr;
if (parent) {
View* root_view = parent->GetRootView();
if (root_view) {
- const ui::Event* event =
- static_cast<internal::RootView*>(root_view)->current_event();
+ event = static_cast<internal::RootView*>(root_view)->current_event();
if (event && event->type() == ui::ET_MOUSE_PRESSED) {
gfx::Point screen_loc(
static_cast<const ui::MouseEvent*>(event)->location());
- View::ConvertPointToScreen(
- static_cast<View*>(event->target()), &screen_loc);
+ View::ConvertPointToScreen(static_cast<View*>(event->target()),
+ &screen_loc);
menu_start_mouse_press_loc_ = screen_loc;
}
}
@@ -476,8 +476,10 @@ void MenuController::Run(Widget* parent,
// Set the selection, which opens the initial menu.
SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
- if (button)
- pressed_lock_ = std::make_unique<MenuButton::PressedLock>(button);
+ if (button) {
+ pressed_lock_ = std::make_unique<MenuButton::PressedLock>(
+ button, false, ui::LocatedEvent::FromIfValid(event));
+ }
if (for_drop_) {
if (!is_nested_drag) {
@@ -677,8 +679,7 @@ void MenuController::OnMouseReleased(SubmenuView* source,
MenuItemView* menu = part.menu;
// |menu| is NULL means this event is from an empty menu or a separator.
// If it is from an empty menu, use parent context menu instead of that.
- if (menu == NULL &&
- part.submenu->child_count() == 1 &&
+ if (menu == NULL && part.submenu->child_count() == 1 &&
part.submenu->child_at(0)->id() == MenuItemView::kEmptyMenuItemViewID) {
menu = part.parent;
}
@@ -748,6 +749,14 @@ void MenuController::OnMouseMoved(SubmenuView* source,
return;
}
+ // Ignore mouse move events whose location is the same as where the mouse
+ // was when a menu was opened. This fixes the issue of opening a menu
+ // with the keyboard and having the menu item under the current mouse
+ // position incorrectly selected.
+ if (menu_open_mouse_loc_ && *menu_open_mouse_loc_ == event.location())
+ return;
+
+ menu_open_mouse_loc_.reset();
MenuHostRootView* root_view = GetRootView(source, event.location());
if (root_view) {
root_view->ProcessMouseMoved(event);
@@ -757,8 +766,7 @@ void MenuController::OnMouseMoved(SubmenuView* source,
// mouse and keyboard are used to navigate the menu.
ui::MouseEvent event_for_root(event);
ConvertLocatedEventForRootView(source, root_view, &event_for_root);
- View* view =
- root_view->GetEventHandlerForPoint(event_for_root.location());
+ View* view = root_view->GetEventHandlerForPoint(event_for_root.location());
Button* button = Button::AsButton(view);
if (button && button->IsHotTracked())
SetHotTrackedButton(button);
@@ -776,6 +784,10 @@ void MenuController::OnMouseEntered(SubmenuView* source,
bool MenuController::OnMouseWheel(SubmenuView* source,
const ui::MouseWheelEvent& event) {
MenuPart part = GetMenuPart(source, event.location());
+
+ SetSelection(part.menu ? part.menu : state_.item,
+ SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
+
return part.submenu && part.submenu->OnMouseWheel(event);
}
@@ -800,8 +812,7 @@ void MenuController::OnGestureEvent(SubmenuView* source,
// Reset hot-tracking if a different view is getting a touch event.
ui::GestureEvent event_for_root(*event);
ConvertLocatedEventForRootView(source, root_view, &event_for_root);
- View* view =
- root_view->GetEventHandlerForPoint(event_for_root.location());
+ View* view = root_view->GetEventHandlerForPoint(event_for_root.location());
Button* button = Button::AsButton(view);
if (hot_button_ && hot_button_ != button)
SetHotTrackedButton(nullptr);
@@ -822,8 +833,7 @@ void MenuController::OnGestureEvent(SubmenuView* source,
} else if (event->type() == ui::ET_GESTURE_TAP) {
if (!part.is_scroll() && part.menu &&
!(part.should_submenu_show && part.menu->HasSubmenu())) {
- if (part.menu->GetDelegate()->IsTriggerableEvent(
- part.menu, *event)) {
+ if (part.menu->GetDelegate()->IsTriggerableEvent(part.menu, *event)) {
item_selected_by_touch_ = true;
Accept(part.menu, event->flags());
}
@@ -834,14 +844,13 @@ void MenuController::OnGestureEvent(SubmenuView* source,
SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
event->StopPropagation();
}
- } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL &&
- part.menu &&
+ } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL && part.menu &&
part.type == MenuPart::MENU_ITEM) {
// Move the selection to the parent menu so that the selection in the
// current menu is unset. Make sure the submenu remains open by sending the
// appropriate SetSelectionTypes flags.
SetSelection(part.menu->GetParentMenuItem(),
- SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
+ SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
event->StopPropagation();
}
@@ -893,9 +902,9 @@ void MenuController::ViewHierarchyChanged(
}
bool MenuController::GetDropFormats(
- SubmenuView* source,
- int* formats,
- std::set<ui::Clipboard::FormatType>* format_types) {
+ SubmenuView* source,
+ int* formats,
+ std::set<ui::Clipboard::FormatType>* format_types) {
return source->GetMenuItem()->GetDelegate()->GetDropFormats(
source->GetMenuItem(), formats, format_types);
}
@@ -947,8 +956,9 @@ int MenuController::OnDragUpdated(SubmenuView* source,
menu_item_loc.y() < (menu_item_height - kDropBetweenPixels))) {
drop_position = MenuDelegate::DROP_ON;
} else {
- drop_position = (menu_item_loc.y() < menu_item_height / 2) ?
- MenuDelegate::DROP_BEFORE : MenuDelegate::DROP_AFTER;
+ drop_position = (menu_item_loc.y() < menu_item_height / 2)
+ ? MenuDelegate::DROP_BEFORE
+ : MenuDelegate::DROP_AFTER;
}
query_menu_item = menu_item;
} else {
@@ -959,8 +969,8 @@ int MenuController::OnDragUpdated(SubmenuView* source,
query_menu_item, event, &drop_position);
// If the menu has a submenu, schedule the submenu to open.
- SetSelection(menu_item, menu_item->HasSubmenu() ? SELECTION_OPEN_SUBMENU :
- SELECTION_DEFAULT);
+ SetSelection(menu_item, menu_item->HasSubmenu() ? SELECTION_OPEN_SUBMENU
+ : SELECTION_DEFAULT);
if (drop_position == MenuDelegate::DROP_NONE ||
drop_operation == ui::DragDropTypes::DRAG_NONE)
@@ -1014,8 +1024,8 @@ int MenuController::OnPerformDrop(SubmenuView* source,
// WARNING: the call to MenuClosed deletes us.
- return drop_target->GetDelegate()->OnPerformDrop(
- drop_target, drop_position, event);
+ return drop_target->GetDelegate()->OnPerformDrop(drop_target, drop_position,
+ event);
}
void MenuController::OnDragEnteredScrollButton(SubmenuView* source,
@@ -1123,8 +1133,8 @@ void MenuController::UpdateSubmenuSelection(SubmenuView* submenu) {
gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
const SubmenuView* root_submenu =
submenu->GetMenuItem()->GetRootMenuItem()->GetSubmenu();
- View::ConvertPointFromScreen(
- root_submenu->GetWidget()->GetRootView(), &point);
+ View::ConvertPointFromScreen(root_submenu->GetWidget()->GetRootView(),
+ &point);
HandleMouseLocation(submenu, point);
}
}
@@ -1308,7 +1318,7 @@ void MenuController::StartDrag(SubmenuView* source,
base::WeakPtr<MenuController> this_ref = AsWeakPtr();
// TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops,
- ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+ ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
// MenuController may have been deleted so check before accessing member
// variables.
if (this_ref)
@@ -1528,18 +1538,15 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source,
return false;
View* source_view = source->GetScrollViewContainer();
- if (mouse_location.x() >= 0 &&
- mouse_location.x() < source_view->width() &&
- mouse_location.y() >= 0 &&
- mouse_location.y() < source_view->height()) {
+ if (mouse_location.x() >= 0 && mouse_location.x() < source_view->width() &&
+ mouse_location.y() >= 0 && mouse_location.y() < source_view->height()) {
// The mouse is over the menu, no need to continue.
return false;
}
// TODO(oshima): Replace with views only API.
- if (!owner_ ||
- !display::Screen::GetScreen()->IsWindowUnderCursor(
- owner_->GetNativeWindow())) {
+ if (!owner_ || !display::Screen::GetScreen()->IsWindowUnderCursor(
+ owner_->GetNativeWindow())) {
return false;
}
@@ -1550,9 +1557,9 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source,
MenuAnchorPosition anchor;
bool has_mnemonics;
MenuButton* button = NULL;
- MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()->
- GetSiblingMenu(source->GetMenuItem()->GetRootMenuItem(),
- screen_point, &anchor, &has_mnemonics, &button);
+ MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()->GetSiblingMenu(
+ source->GetMenuItem()->GetRootMenuItem(), screen_point, &anchor,
+ &has_mnemonics, &button);
if (!alt_menu || (state_.item && state_.item->GetRootMenuItem() == alt_menu))
return false;
@@ -1877,9 +1884,11 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) {
bool prefer_leading =
state_.open_leading.empty() ? true : state_.open_leading.back();
bool resulting_direction;
- gfx::Rect bounds = MenuItemView::IsBubble(state_.anchor) ?
- CalculateBubbleMenuBounds(item, prefer_leading, &resulting_direction) :
- CalculateMenuBounds(item, prefer_leading, &resulting_direction);
+ gfx::Rect bounds =
+ MenuItemView::IsBubble(state_.anchor)
+ ? CalculateBubbleMenuBounds(item, prefer_leading,
+ &resulting_direction)
+ : CalculateMenuBounds(item, prefer_leading, &resulting_direction);
state_.open_leading.push_back(resulting_direction);
bool do_capture = (!did_capture_ && !for_drop_);
showing_submenu_ = true;
@@ -1887,7 +1896,22 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) {
// Menus are the only place using kGroupingPropertyKey, so any value (other
// than 0) is fine.
const int kGroupingId = 1001;
+
item->GetSubmenu()->ShowAt(owner_, bounds, do_capture);
+ // Figure out if the mouse is under the menu; if so, remember the mouse
+ // location so we can ignore the first mouse move event(s) with that
+ // location. We do this after ShowAt because ConvertPointFromScreen
+ // doesn't work correctly if the widget isn't shown.
+ if (item->GetSubmenu()->GetWidget() != nullptr) {
+ gfx::Point mouse_pos =
+ display::Screen::GetScreen()->GetCursorScreenPoint();
+ View::ConvertPointFromScreen(item->submenu_->GetWidget()->GetRootView(),
+ &mouse_pos);
+ MenuPart part_under_mouse = GetMenuPart(item->submenu_, mouse_pos);
+ if (part_under_mouse.type != MenuPart::NONE)
+ menu_open_mouse_loc_ = mouse_pos;
+ }
+
item->GetSubmenu()->GetWidget()->SetNativeWindowProperty(
TooltipManager::kGroupingPropertyKey,
reinterpret_cast<void*>(kGroupingId));
@@ -1963,8 +1987,8 @@ void MenuController::StopShowTimer() {
void MenuController::StartCancelAllTimer() {
cancel_all_timer_.Start(FROM_HERE,
- TimeDelta::FromMilliseconds(kCloseOnExitTime),
- this, &MenuController::CancelAll);
+ TimeDelta::FromMilliseconds(kCloseOnExitTime), this,
+ &MenuController::CancelAll);
}
void MenuController::StopCancelAllTimer() {
@@ -1979,133 +2003,33 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
SubmenuView* submenu = item->GetSubmenu();
DCHECK(submenu);
- gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
+ gfx::Rect menu_bounds =
+ gfx::Rect(submenu->GetScrollViewContainer()->GetPreferredSize());
+
+ const gfx::Rect& monitor_bounds = state_.monitor_bounds;
+ const gfx::Rect& anchor_bounds = state_.initial_bounds;
// For comboboxes, ensure the menu is at least as wide as the anchor.
if (is_combobox_)
- pref.set_width(std::max(pref.width(), state_.initial_bounds.width()));
+ menu_bounds.set_width(std::max(menu_bounds.width(), anchor_bounds.width()));
- // Don't let the menu go too wide.
- pref.set_width(
- std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item)));
- if (!state_.monitor_bounds.IsEmpty())
- pref.set_width(std::min(pref.width(), state_.monitor_bounds.width()));
+ // Don't let the menu go too wide or too tall.
+ menu_bounds.set_width(std::min(
+ menu_bounds.width(), item->GetDelegate()->GetMaxWidthForMenu(item)));
+ if (!monitor_bounds.IsEmpty()) {
+ menu_bounds.set_width(
+ std::min(menu_bounds.width(), monitor_bounds.width()));
+ menu_bounds.set_height(
+ std::min(menu_bounds.height(), monitor_bounds.height()));
+ }
// Assume we can honor prefer_leading.
*is_leading = prefer_leading;
- int x, y;
-
const MenuConfig& menu_config = MenuConfig::instance();
- if (!item->GetParentMenuItem()) {
- // First item, position relative to initial location.
- x = state_.initial_bounds.x();
-
- // Offsets for context menu prevent menu items being selected by
- // simply opening the menu (bug 142992).
- if (menu_config.offset_context_menus && state_.context_menu)
- x += 1;
-
- y = state_.initial_bounds.bottom();
- if (state_.anchor == MENU_ANCHOR_TOPRIGHT) {
- x = x + state_.initial_bounds.width() - pref.width();
- if (menu_config.offset_context_menus && state_.context_menu)
- x -= 1;
- } else if (state_.anchor == MENU_ANCHOR_BOTTOMCENTER) {
- x += (state_.initial_bounds.width() - pref.width()) / 2;
- if (pref.height() >
- state_.initial_bounds.y() + kCenteredContextMenuYOffset) {
- // Place the menu below if it does not fit above.
- y = state_.initial_bounds.y() - kCenteredContextMenuYOffset;
- } else {
- y = std::max(0, state_.initial_bounds.y() - pref.height()) +
- kCenteredContextMenuYOffset;
- }
- } else if (state_.anchor == MENU_ANCHOR_FIXED_BOTTOMCENTER) {
- x += (state_.initial_bounds.width() - pref.width()) / 2;
- } else if (state_.anchor == MENU_ANCHOR_FIXED_SIDECENTER) {
- y += (state_.initial_bounds.height() - pref.height()) / 2;
- }
-
- if (!state_.monitor_bounds.IsEmpty() &&
- y + pref.height() > state_.monitor_bounds.bottom()) {
- // The menu doesn't fit fully below the button on the screen. The menu
- // position with respect to the bounds will be preserved if it has
- // already been drawn. When the requested positioning is below the bounds
- // it will shrink the menu to make it fit below.
- // If the requested positioning is best fit, it will first try to fit the
- // menu below. If that does not fit it will try to place it above. If
- // that will not fit it will place it at the bottom of the work area and
- // moving it off the initial_bounds region to avoid overlap.
- // In all other requested position styles it will be flipped above and
- // the height will be shrunken to the usable height.
- if (item->actual_menu_position() == MenuItemView::POSITION_BELOW_BOUNDS) {
- pref.set_height(std::min(pref.height(),
- state_.monitor_bounds.bottom() - y));
- } else if (item->actual_menu_position() ==
- MenuItemView::POSITION_BEST_FIT) {
- MenuItemView::MenuPosition orientation =
- MenuItemView::POSITION_BELOW_BOUNDS;
- if (state_.monitor_bounds.height() < pref.height()) {
- // Handle very tall menus.
- pref.set_height(state_.monitor_bounds.height());
- y = state_.monitor_bounds.y();
- } else if (state_.monitor_bounds.y() + pref.height() <
- state_.initial_bounds.y()) {
- // Flipping upwards if there is enough space.
- y = state_.initial_bounds.y() - pref.height();
- orientation = MenuItemView::POSITION_ABOVE_BOUNDS;
- } else {
- // It is allowed to move the menu a bit around in order to get the
- // best fit and to avoid showing scroll elements.
- y = state_.monitor_bounds.bottom() - pref.height();
- }
- if (orientation == MenuItemView::POSITION_BELOW_BOUNDS) {
- // The menu should never overlap the owning button. So move it.
- // We use the anchor view style to determine the preferred position
- // relative to the owning button.
- if (state_.anchor == MENU_ANCHOR_TOPLEFT) {
- // The menu starts with the same x coordinate as the owning button.
- if (x + state_.initial_bounds.width() + pref.width() >
- state_.monitor_bounds.right())
- x -= pref.width(); // Move the menu to the left of the button.
- else
- x += state_.initial_bounds.width(); // Move the menu right.
- } else {
- // The menu should end with the same x coordinate as the owning
- // button.
- if (state_.monitor_bounds.x() >
- state_.initial_bounds.x() - pref.width())
- x = state_.initial_bounds.right(); // Move right of the button.
- else
- x = state_.initial_bounds.x() - pref.width(); // Move left.
- }
- }
- item->set_actual_menu_position(orientation);
- } else {
- pref.set_height(std::min(pref.height(),
- state_.initial_bounds.y() - state_.monitor_bounds.y()));
- y = state_.initial_bounds.y() - pref.height();
- item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS);
- }
- } else if (item->actual_menu_position() ==
- MenuItemView::POSITION_ABOVE_BOUNDS) {
- pref.set_height(std::min(pref.height(),
- state_.initial_bounds.y() - state_.monitor_bounds.y()));
- y = state_.initial_bounds.y() - pref.height();
- } else {
- item->set_actual_menu_position(MenuItemView::POSITION_BELOW_BOUNDS);
- }
- if (state_.monitor_bounds.width() != 0 &&
- menu_config.offset_context_menus && state_.context_menu) {
- if (x + pref.width() > state_.monitor_bounds.right())
- x = state_.initial_bounds.x() - pref.width() - 1;
- if (x < state_.monitor_bounds.x())
- x = state_.monitor_bounds.x();
- }
- } else {
- // Not the first menu; position it relative to the bounds of the menu
+ if (item->GetParentMenuItem()) {
+ // Not the first menu; position it relative to the bounds of its parent menu
// item.
gfx::Point item_loc;
View::ConvertPointToScreen(item, &item_loc);
@@ -2113,48 +2037,117 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
// We must make sure we take into account the UI layout. If the layout is
// RTL, then a 'leading' menu is positioned to the left of the parent menu
// item and not to the right.
- bool layout_is_rtl = base::i18n::IsRTL();
- bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
- (!prefer_leading && layout_is_rtl);
- int submenu_horizontal_inset = menu_config.submenu_horizontal_inset;
+ const bool layout_is_rtl = base::i18n::IsRTL();
+ const bool create_on_right = prefer_leading != layout_is_rtl;
+ const int submenu_horizontal_inset = menu_config.submenu_horizontal_inset;
- if (create_on_the_right) {
- x = item_loc.x() + item->width() - submenu_horizontal_inset;
- if (state_.monitor_bounds.width() != 0 &&
- x + pref.width() > state_.monitor_bounds.right()) {
- if (layout_is_rtl)
- *is_leading = true;
- else
- *is_leading = false;
- x = item_loc.x() - pref.width() + submenu_horizontal_inset;
- }
+ const int left_of_parent =
+ item_loc.x() - menu_bounds.width() + submenu_horizontal_inset;
+ const int right_of_parent =
+ item_loc.x() + item->width() - submenu_horizontal_inset;
+
+ menu_bounds.set_y(item_loc.y() - menu_config.menu_vertical_border_size);
+
+ // Assume the menu can be placed in the preferred location.
+ menu_bounds.set_x(create_on_right ? right_of_parent : left_of_parent);
+
+ // Everything after this check requires monitor bounds to be non-empty.
+ if (monitor_bounds.IsEmpty())
+ return menu_bounds;
+
+ // Menu does not actually fit where it was placed, move it to the other side
+ // and update |is_leading|.
+ if (menu_bounds.x() < monitor_bounds.x()) {
+ *is_leading = !layout_is_rtl;
+ menu_bounds.set_x(right_of_parent);
+ } else if (menu_bounds.right() > monitor_bounds.right()) {
+ *is_leading = layout_is_rtl;
+ menu_bounds.set_x(left_of_parent);
+ }
+ } else {
+ // First item, align top left corner of menu with bottom left corner of
+ // anchor bounds.
+ menu_bounds.set_x(anchor_bounds.x());
+ menu_bounds.set_y(anchor_bounds.bottom());
+
+ const int above_anchor = anchor_bounds.y() - menu_bounds.height();
+ const int horizontally_centered =
+ anchor_bounds.x() + (anchor_bounds.width() - menu_bounds.width()) / 2;
+ const int vertically_centered =
+ anchor_bounds.y() + (anchor_bounds.height() - menu_bounds.height()) / 2;
+
+ if (state_.anchor == MENU_ANCHOR_TOPRIGHT) {
+ // Move the menu so that its right edge is aligned with the anchor
+ // bounds right edge.
+ menu_bounds.set_x(anchor_bounds.right() - menu_bounds.width());
+ } else if (state_.anchor == MENU_ANCHOR_BOTTOMCENTER) {
+ // Try to fit the menu above the anchor bounds. If it doesn't fit, place
+ // it below.
+ menu_bounds.set_x(horizontally_centered);
+ menu_bounds.set_y(above_anchor - kTouchYPadding);
+ if (menu_bounds.y() < monitor_bounds.y())
+ menu_bounds.set_y(anchor_bounds.y() + kTouchYPadding);
+ } else if (state_.anchor == MENU_ANCHOR_FIXED_BOTTOMCENTER) {
+ menu_bounds.set_x(horizontally_centered);
+ } else if (state_.anchor == MENU_ANCHOR_FIXED_SIDECENTER) {
+ menu_bounds.set_y(vertically_centered);
+ }
+
+ if (item->actual_menu_position() == MenuItemView::POSITION_ABOVE_BOUNDS) {
+ // Menu has already been drawn above, put it above the anchor bounds.
+ menu_bounds.set_y(above_anchor);
+ }
+
+ // Everything beyond this point requires monitor bounds to be non-empty.
+ if (monitor_bounds.IsEmpty())
+ return menu_bounds;
+
+ // If the menu position is below or above the anchor bounds, force it to fit
+ // on the screen. Otherwise, try to fit the menu in the following locations:
+ // 1.) Below the anchor bounds
+ // 2.) Above the anchor bounds
+ // 3.) At the bottom of the monitor and off the side of the anchor bounds
+ if (item->actual_menu_position() == MenuItemView::POSITION_BELOW_BOUNDS ||
+ item->actual_menu_position() == MenuItemView::POSITION_ABOVE_BOUNDS) {
+ // Menu has been drawn below/above the anchor bounds, make sure it fits
+ // on the screen in its current location.
+ menu_bounds.Intersect(monitor_bounds);
+ } else if (menu_bounds.bottom() <= monitor_bounds.bottom()) {
+ // Menu fits below anchor bounds.
+ item->set_actual_menu_position(MenuItemView::POSITION_BELOW_BOUNDS);
+ } else if (above_anchor >= monitor_bounds.y()) {
+ // Menu fits above anchor bounds.
+ menu_bounds.set_y(above_anchor);
+ item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS);
} else {
- x = item_loc.x() - pref.width() + submenu_horizontal_inset;
- if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
- if (layout_is_rtl)
- *is_leading = false;
- else
- *is_leading = true;
- x = item_loc.x() + item->width() - submenu_horizontal_inset;
+ const int left_of_anchor = anchor_bounds.x() - menu_bounds.width();
+ const int right_of_anchor = anchor_bounds.right();
+
+ menu_bounds.set_y(monitor_bounds.bottom() - menu_bounds.height());
+ if (state_.anchor == MENU_ANCHOR_TOPLEFT) {
+ // Prefer menu to right of anchor bounds but move it to left if it
+ // doesn't fit.
+ menu_bounds.set_x(right_of_anchor);
+ if (menu_bounds.right() > monitor_bounds.right())
+ menu_bounds.set_x(left_of_anchor);
+ } else {
+ // Prefer menu to left of anchor bounds but move it to right if it
+ // doesn't fit.
+ menu_bounds.set_x(left_of_anchor);
+ if (menu_bounds.x() < monitor_bounds.x())
+ menu_bounds.set_x(right_of_anchor);
}
}
- y = item_loc.y() - menu_config.menu_vertical_border_size;
- if (state_.monitor_bounds.width() != 0) {
- pref.set_height(std::min(pref.height(), state_.monitor_bounds.height()));
- if (y + pref.height() > state_.monitor_bounds.bottom())
- y = state_.monitor_bounds.bottom() - pref.height();
- if (y < state_.monitor_bounds.y())
- y = state_.monitor_bounds.y();
- }
}
- if (state_.monitor_bounds.width() != 0) {
- if (x + pref.width() > state_.monitor_bounds.right())
- x = state_.monitor_bounds.right() - pref.width();
- if (x < state_.monitor_bounds.x())
- x = state_.monitor_bounds.x();
- }
- return gfx::Rect(x, y, pref.width(), pref.height());
+ menu_bounds.set_x(
+ base::ClampToRange(menu_bounds.x(), monitor_bounds.x(),
+ monitor_bounds.right() - menu_bounds.width()));
+ menu_bounds.set_y(
+ base::ClampToRange(menu_bounds.y(), monitor_bounds.y(),
+ monitor_bounds.bottom() - menu_bounds.height()));
+
+ return menu_bounds;
}
gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
@@ -2239,7 +2232,7 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
border_and_shadow_insets.right();
}
// Align the top of the menu with the bottom of the anchor.
- if (y < 0) {
+ if (y < state_.monitor_bounds.y()) {
y = owner_bounds.bottom() - border_and_shadow_insets.top() +
menu_config.touchable_anchor_offset;
}
@@ -2251,7 +2244,7 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
menu_config.touchable_anchor_offset;
y = owner_bounds.origin().y() - border_and_shadow_insets.top();
// Align the left of the menu with the right of the anchor.
- if (x < 0) {
+ if (x < state_.monitor_bounds.x()) {
x = owner_bounds.right() - border_and_shadow_insets.left() +
menu_config.touchable_anchor_offset;
}
@@ -2260,6 +2253,23 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
y = owner_bounds.bottom() - pref.height() +
border_and_shadow_insets.bottom();
}
+ } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT) {
+ // Align the left of the menu with the right of the anchor, and the top of
+ // the menu with the top of the anchor.
+ x = owner_bounds.right() - border_and_shadow_insets.left() +
+ menu_config.touchable_anchor_offset;
+ y = owner_bounds.origin().y() - border_and_shadow_insets.top();
+ if (x + pref.width() > state_.monitor_bounds.width()) {
+ // Align the right of the menu with the left of the anchor.
+ x = owner_bounds.origin().x() - pref.width() +
+ border_and_shadow_insets.right() -
+ menu_config.touchable_anchor_offset;
+ }
+ if (y + pref.height() > state_.monitor_bounds.height()) {
+ // Align the bottom of the menu with the bottom of the anchor.
+ y = owner_bounds.bottom() - pref.height() +
+ border_and_shadow_insets.bottom();
+ }
} else {
if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
x = owner_bounds.right() - kBubbleTipSizeLeftRight;
@@ -2346,9 +2356,9 @@ void MenuController::IncrementSelection(
SetHotTrackedButton(nullptr);
}
bool direction_is_down = direction == INCREMENT_SELECTION_DOWN;
- View* to_make_hot = button
- ? GetNextFocusableView(item, button, direction_is_down)
- : GetInitialFocusableView(item, direction_is_down);
+ View* to_make_hot =
+ button ? GetNextFocusableView(item, button, direction_is_down)
+ : GetInitialFocusableView(item, direction_is_down);
Button* hot_button = Button::AsButton(to_make_hot);
if (hot_button) {
SetHotTrackedButton(hot_button);
@@ -2363,7 +2373,7 @@ void MenuController::IncrementSelection(
for (int i = 0; i < parent_count; ++i) {
if (parent->GetSubmenu()->GetMenuItemAt(i) == item) {
MenuItemView* to_select =
- FindNextSelectableMenuItem(parent, i, direction);
+ FindNextSelectableMenuItem(parent, i, direction, false);
SetInitialHotTrackedView(to_select, direction);
break;
}
@@ -2376,13 +2386,14 @@ MenuItemView* MenuController::FindInitialSelectableMenuItem(
MenuItemView* parent,
SelectionIncrementDirectionType direction) {
return FindNextSelectableMenuItem(
- parent, direction == INCREMENT_SELECTION_DOWN ? -1 : 0, direction);
+ parent, direction == INCREMENT_SELECTION_DOWN ? -1 : 0, direction, true);
}
MenuItemView* MenuController::FindNextSelectableMenuItem(
MenuItemView* parent,
int index,
- SelectionIncrementDirectionType direction) {
+ SelectionIncrementDirectionType direction,
+ bool is_initial) {
int parent_count = parent->GetSubmenu()->GetMenuItemCount();
int stop_index = (index + parent_count) % parent_count;
bool include_all_items =
@@ -2392,7 +2403,7 @@ MenuItemView* MenuController::FindNextSelectableMenuItem(
// Loop through the menu items skipping any invisible menus. The loop stops
// when we wrap or find a visible and enabled child.
do {
- if (!MenuConfig::instance().arrow_key_selection_wraps) {
+ if (!MenuConfig::instance().arrow_key_selection_wraps && !is_initial) {
if (index == 0 && direction == INCREMENT_SELECTION_UP)
return nullptr;
if (index == parent_count - 1 && direction == INCREMENT_SELECTION_DOWN)
@@ -2481,8 +2492,7 @@ void MenuController::AcceptOrSelect(MenuItemView* parent,
SetSelection(submenu->GetMenuItemAt(details.first_match),
SELECTION_DEFAULT);
} else {
- SetSelection(submenu->GetMenuItemAt(details.next_match),
- SELECTION_DEFAULT);
+ SetSelection(submenu->GetMenuItemAt(details.next_match), SELECTION_DEFAULT);
}
}
@@ -2493,7 +2503,7 @@ void MenuController::SelectByChar(base::char16 character) {
if (!character)
return;
- base::char16 char_array[] = { character, 0 };
+ base::char16 char_array[] = {character, 0};
base::char16 key = base::i18n::ToLower(char_array)[0];
MenuItemView* item = pending_state_.item;
if (!item->SubmenuIsShowing())
@@ -2574,9 +2584,8 @@ void MenuController::RepostEventAndCancel(SubmenuView* source,
Cancel(exit_type);
}
-void MenuController::SetDropMenuItem(
- MenuItemView* new_target,
- MenuDelegate::DropPosition new_position) {
+void MenuController::SetDropMenuItem(MenuItemView* new_target,
+ MenuDelegate::DropPosition new_position) {
if (new_target == drop_target_ && new_position == drop_position_)
return;
@@ -2629,8 +2638,7 @@ void MenuController::UpdateActiveMouseView(SubmenuView* event_source,
active_mouse_view_tracker_->SetView(active_mouse_view);
if (active_mouse_view) {
gfx::Point target_point(target_menu_loc);
- View::ConvertPointToTarget(
- target_menu, active_mouse_view, &target_point);
+ View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point);
ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED, target_point,
target_point, ui::EventTimeForNow(), 0,
0);
diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h
index 67b8849bca5..1fcfaa99d46 100644
--- a/chromium/ui/views/controls/menu/menu_controller.h
+++ b/chromium/ui/views/controls/menu/menu_controller.h
@@ -10,6 +10,7 @@
#include <list>
#include <memory>
#include <set>
+#include <utility>
#include <vector>
#include "base/compiler_specific.h"
@@ -54,6 +55,7 @@ class MenuRunnerImpl;
namespace test {
class MenuControllerTest;
class MenuControllerTestApi;
+class MenuControllerUITest;
}
// MenuController -------------------------------------------------------------
@@ -118,7 +120,7 @@ class VIEWS_EXPORT MenuController
// WARNING: this may be NULL.
Widget* owner() { return owner_; }
- // Get the anchor position wich is used to show this menu.
+ // Get the anchor position which is used to show this menu.
MenuAnchorPosition GetAnchorPosition() { return state_.anchor; }
// Cancels the current Run. See ExitType for a description of what happens
@@ -220,6 +222,7 @@ class VIEWS_EXPORT MenuController
friend class internal::MenuRunnerImpl;
friend class test::MenuControllerTest;
friend class test::MenuControllerTestApi;
+ friend class test::MenuControllerUITest;
friend class MenuHostRootView;
friend class MenuItemView;
friend class SubmenuView;
@@ -230,18 +233,18 @@ class VIEWS_EXPORT MenuController
// Values supplied to SetSelection.
enum SetSelectionTypes {
- SELECTION_DEFAULT = 0,
+ SELECTION_DEFAULT = 0,
// If set submenus are opened immediately, otherwise submenus are only
- // openned after a timer fires.
- SELECTION_UPDATE_IMMEDIATELY = 1 << 0,
+ // opened after a timer fires.
+ SELECTION_UPDATE_IMMEDIATELY = 1 << 0,
// If set and the menu_item has a submenu, the submenu is shown.
- SELECTION_OPEN_SUBMENU = 1 << 1,
+ SELECTION_OPEN_SUBMENU = 1 << 1,
// SetSelection is being invoked as the result exiting or cancelling the
// menu. This is used for debugging.
- SELECTION_EXIT = 1 << 2,
+ SELECTION_EXIT = 1 << 2,
};
// Direction for IncrementSelection and FindInitialSelectableMenuItem.
@@ -323,7 +326,7 @@ class VIEWS_EXPORT MenuController
// Sets the selection to |menu_item|. A value of NULL unselects
// everything. |types| is a bitmask of |SetSelectionTypes|.
//
- // Internally this updates pending_state_ immediatley. state_ is only updated
+ // Internally this updates pending_state_ immediately. state_ is only updated
// immediately if SELECTION_UPDATE_IMMEDIATELY is set. If
// SELECTION_UPDATE_IMMEDIATELY is not set CommitPendingSelection is invoked
// to show/hide submenus and update state_.
@@ -498,7 +501,8 @@ class VIEWS_EXPORT MenuController
MenuItemView* FindNextSelectableMenuItem(
MenuItemView* parent,
int index,
- SelectionIncrementDirectionType direction);
+ SelectionIncrementDirectionType direction,
+ bool is_initial);
// If the selected item has a submenu and it isn't currently open, the
// the selection is changed such that the menu opens immediately.
@@ -559,7 +563,7 @@ class VIEWS_EXPORT MenuController
// Sets exit type. Calling this can terminate the active nested message-loop.
void SetExitType(ExitType type);
- // Performs the teardown of menus. This will notifiy the |delegate_|. If
+ // Performs the teardown of menus. This will notify the |delegate_|. If
// |exit_type_| is EXIT_ALL all nested runs will be exited.
void ExitMenu();
@@ -690,6 +694,12 @@ class VIEWS_EXPORT MenuController
// screen coordinates). Otherwise this will be (0, 0).
gfx::Point menu_start_mouse_press_loc_;
+ // If the mouse was under the menu when the menu was run, this will have its
+ // location. Otherwise it will be null. This is used to ignore mouse move
+ // events triggered by the menu opening, to avoid selecting the menu item
+ // over the mouse.
+ base::Optional<gfx::Point> menu_open_mouse_loc_;
+
// Controls behavior differences between a combobox and other types of menu
// (like a context menu).
bool is_combobox_ = false;
diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
index 2d1c138bdc0..c8052ea2734 100644
--- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
@@ -12,8 +12,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
-#include "ui/aura/scoped_window_targeter.h"
-#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
@@ -119,7 +117,7 @@ void TestMenuControllerDelegate::SiblingMenuCreated(MenuItemView* menu) {}
class SubmenuViewShown : public SubmenuView {
public:
- SubmenuViewShown(MenuItemView* parent) : SubmenuView(parent) {}
+ using SubmenuView::SubmenuView;
~SubmenuViewShown() override {}
bool IsShowing() override { return true; }
@@ -132,7 +130,7 @@ class TestEventHandler : public ui::EventHandler {
TestEventHandler() : outstanding_touches_(0) {}
void OnTouchEvent(ui::TouchEvent* event) override {
- switch(event->type()) {
+ switch (event->type()) {
case ui::ET_TOUCH_PRESSED:
outstanding_touches_++;
break;
@@ -246,25 +244,50 @@ void DestructingTestViewsDelegate::ReleaseRef() {
class TestMenuItemViewShown : public MenuItemView {
public:
- TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) {
+ explicit TestMenuItemViewShown(MenuDelegate* delegate)
+ : MenuItemView(delegate) {
submenu_ = new SubmenuViewShown(this);
}
~TestMenuItemViewShown() override {}
- void SetController(MenuController* controller) {
- set_controller(controller);
- }
+ void SetController(MenuController* controller) { set_controller(controller); }
void AddEmptyMenusForTest() { AddEmptyMenus(); }
+ void SetActualMenuPosition(MenuItemView::MenuPosition position) {
+ set_actual_menu_position(position);
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown);
};
-class MenuControllerTest : public ViewsTestBase {
+class TestMenuItemViewNotShown : public MenuItemView {
public:
- MenuControllerTest() : menu_controller_(nullptr) {
+ explicit TestMenuItemViewNotShown(MenuDelegate* delegate)
+ : MenuItemView(delegate) {
+ submenu_ = new SubmenuView(this);
}
+ ~TestMenuItemViewNotShown() override {}
+
+ void SetController(MenuController* controller) { set_controller(controller); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewNotShown);
+};
+
+struct MenuBoundsOptions {
+ public:
+ gfx::Rect anchor_bounds = gfx::Rect(500, 500, 10, 10);
+ gfx::Rect monitor_bounds = gfx::Rect(0, 0, 1000, 1000);
+ gfx::Size menu_size = gfx::Size(100, 100);
+ MenuAnchorPosition menu_anchor = MENU_ANCHOR_TOPLEFT;
+ MenuItemView::MenuPosition menu_position = MenuItemView::POSITION_BEST_FIT;
+};
+
+class MenuControllerTest : public ViewsTestBase {
+ public:
+ MenuControllerTest() : menu_controller_(nullptr) {}
~MenuControllerTest() override {}
@@ -286,9 +309,7 @@ class MenuControllerTest : public ViewsTestBase {
ViewsTestBase::TearDown();
}
- void ReleaseTouchId(int id) {
- event_generator_->ReleaseTouchId(id);
- }
+ void ReleaseTouchId(int id) { event_generator_->ReleaseTouchId(id); }
void PressKey(ui::KeyboardCode key_code) {
event_generator_->PressKey(key_code, 0);
@@ -299,6 +320,18 @@ class MenuControllerTest : public ViewsTestBase {
menu_controller_->OnWillDispatchKeyEvent(&event);
}
+ gfx::Rect CalculateMenuBounds(const MenuBoundsOptions& options) {
+ menu_controller_->state_.anchor = options.menu_anchor;
+ menu_controller_->state_.initial_bounds = options.anchor_bounds;
+ menu_controller_->state_.monitor_bounds = options.monitor_bounds;
+ menu_item_->SetActualMenuPosition(options.menu_position);
+ menu_item_->GetSubmenu()->GetScrollViewContainer()->SetPreferredSize(
+ options.menu_size);
+ bool resulting_direction;
+ return menu_controller_->CalculateMenuBounds(menu_item_.get(), true,
+ &resulting_direction);
+ }
+
#if defined(USE_AURA)
// Verifies that a non-nested menu fully closes when receiving an escape key.
void TestAsyncEscapeKey() {
@@ -371,9 +404,8 @@ class MenuControllerTest : public ViewsTestBase {
void ResetSelection() {
menu_controller_->SetSelection(
- nullptr,
- MenuController::SELECTION_EXIT |
- MenuController::SELECTION_UPDATE_IMMEDIATELY);
+ nullptr, MenuController::SELECTION_EXIT |
+ MenuController::SELECTION_UPDATE_IMMEDIATELY);
}
void IncrementSelection() {
@@ -405,15 +437,14 @@ class MenuControllerTest : public ViewsTestBase {
MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent,
int index) {
-
return menu_controller_->FindNextSelectableMenuItem(
- parent, index, MenuController::INCREMENT_SELECTION_DOWN);
+ parent, index, MenuController::INCREMENT_SELECTION_DOWN, false);
}
MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent,
int index) {
return menu_controller_->FindNextSelectableMenuItem(
- parent, index, MenuController::INCREMENT_SELECTION_UP);
+ parent, index, MenuController::INCREMENT_SELECTION_UP, false);
}
internal::MenuControllerDelegate* GetCurrentDelegate() {
@@ -655,12 +686,8 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
// The last selectable item should be item "Four".
MenuItemView* last_selectable =
FindInitialSelectableMenuItemUp(menu_item());
- if (SelectionWraps()) {
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(4, last_selectable->GetCommand());
- } else {
- ASSERT_EQ(nullptr, last_selectable);
- }
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(4, last_selectable->GetCommand());
// Leave items "One" and "Two" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true);
@@ -673,12 +700,8 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(1, first_selectable->GetCommand());
// The last selectable item should be item "Two".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- if (SelectionWraps()) {
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(2, last_selectable->GetCommand());
- } else {
- ASSERT_EQ(nullptr, last_selectable);
- }
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(2, last_selectable->GetCommand());
// Leave only a single item "One" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true);
@@ -691,12 +714,8 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(1, first_selectable->GetCommand());
// The last selectable item should be item "One".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- if (SelectionWraps()) {
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(1, last_selectable->GetCommand());
- } else {
- ASSERT_EQ(nullptr, last_selectable);
- }
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(1, last_selectable->GetCommand());
// Leave only a single item "Three" enabled.
menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false);
@@ -709,12 +728,8 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
EXPECT_EQ(3, first_selectable->GetCommand());
// The last selectable item should be item "Three".
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- if (SelectionWraps()) {
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(3, last_selectable->GetCommand());
- } else {
- ASSERT_EQ(nullptr, last_selectable);
- }
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(3, last_selectable->GetCommand());
// Leave only a single item ("Two") selected. It should be the first and the
// last selectable item.
@@ -726,12 +741,8 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
ASSERT_NE(nullptr, first_selectable);
EXPECT_EQ(2, first_selectable->GetCommand());
last_selectable = FindInitialSelectableMenuItemUp(menu_item());
- if (SelectionWraps()) {
- ASSERT_NE(nullptr, last_selectable);
- EXPECT_EQ(2, last_selectable->GetCommand());
- } else {
- ASSERT_EQ(nullptr, last_selectable);
- }
+ ASSERT_NE(nullptr, last_selectable);
+ EXPECT_EQ(2, last_selectable->GetCommand());
// There should be no next or previous selectable item since there is only a
// single enabled item in the menu.
@@ -799,10 +810,7 @@ TEST_F(MenuControllerTest, PreviousSelectedItem) {
// Move up and select a previous (in our case the last enabled) item.
DecrementSelection();
- if (SelectionWraps())
- EXPECT_EQ(3, pending_state_item()->GetCommand());
- else
- EXPECT_EQ(0, pending_state_item()->GetCommand());
+ EXPECT_EQ(3, pending_state_item()->GetCommand());
// Clear references in menu controller to the menu item that is going away.
ResetSelection();
@@ -1107,7 +1115,7 @@ TEST_F(MenuControllerTest, AsynchronousCancelDuringDrag) {
controller_delegate->on_menu_closed_notify_type());
}
-// Tests that if a menu is destroyed while drag operations are occuring, that
+// Tests that if a menu is destroyed while drag operations are occurring, that
// the MenuHost does not crash as the drag completes.
TEST_F(MenuControllerTest, AsynchronousDragHostDeleted) {
SubmenuView* submenu = menu_item()->GetSubmenu();
@@ -1142,8 +1150,8 @@ TEST_F(MenuControllerTest, HostReceivesInputBeforeDestruction) {
root_view->OnMouseMoved(event);
}
-// Tets that an asynchronous menu nested within an asynchronous menu closes both
-// menus, and notifies both delegates.
+// Tests that an asynchronous menu nested within an asynchronous menu closes
+// both menus, and notifies both delegates.
TEST_F(MenuControllerTest, DoubleAsynchronousNested) {
MenuController* controller = menu_controller();
TestMenuControllerDelegate* delegate = menu_controller_delegate();
@@ -1190,7 +1198,7 @@ TEST_F(MenuControllerTest, PreserveGestureForOwner) {
controller->OnGestureEvent(sub_menu, &event2);
EXPECT_EQ(CountOwnerOnGestureEvent(), 2);
- // ET_GESTURE_END resets the |send_gesture_events_to_owner_| flag, so futher
+ // ET_GESTURE_END resets the |send_gesture_events_to_owner_| flag, so further
// gesture events should not be sent to the owner.
controller->OnGestureEvent(sub_menu, &event2);
EXPECT_EQ(CountOwnerOnGestureEvent(), 2);
@@ -1361,7 +1369,231 @@ TEST_F(MenuControllerTest, ArrowKeysAtEnds) {
EXPECT_EQ(4, pending_state_item()->GetCommand());
}
+// Test that the menu is properly placed where it best fits.
+TEST_F(MenuControllerTest, CalculateMenuBoundsBestFitTest) {
+ MenuBoundsOptions options;
+ gfx::Rect expected;
+
+ // Fits in all locations -> placed below.
+ options.anchor_bounds =
+ gfx::Rect(options.menu_size.width(), options.menu_size.height(), 0, 0);
+ options.monitor_bounds =
+ gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(),
+ options.anchor_bounds.bottom() + options.menu_size.height());
+ expected =
+ gfx::Rect(options.anchor_bounds.x(), options.anchor_bounds.bottom(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Fits above and to both sides -> placed above.
+ options.anchor_bounds =
+ gfx::Rect(options.menu_size.width(), options.menu_size.height(), 0, 0);
+ options.monitor_bounds =
+ gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(),
+ options.anchor_bounds.bottom());
+ expected = gfx::Rect(options.anchor_bounds.x(),
+ options.anchor_bounds.y() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Fits on both sides, prefer right -> placed right.
+ options.anchor_bounds = gfx::Rect(options.menu_size.width(),
+ options.menu_size.height() / 2, 0, 0);
+ options.monitor_bounds =
+ gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(),
+ options.menu_size.height());
+ expected =
+ gfx::Rect(options.anchor_bounds.right(),
+ options.monitor_bounds.bottom() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Fits only on left -> placed left.
+ options.anchor_bounds = gfx::Rect(options.menu_size.width(),
+ options.menu_size.height() / 2, 0, 0);
+ options.monitor_bounds = gfx::Rect(0, 0, options.anchor_bounds.right(),
+ options.menu_size.height());
+ expected =
+ gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(),
+ options.monitor_bounds.bottom() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Fits on both sides, prefer left -> placed left.
+ options.menu_anchor = MENU_ANCHOR_TOPRIGHT;
+ options.anchor_bounds = gfx::Rect(options.menu_size.width(),
+ options.menu_size.height() / 2, 0, 0);
+ options.monitor_bounds =
+ gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(),
+ options.menu_size.height());
+ expected =
+ gfx::Rect(options.anchor_bounds.x() - options.menu_size.width(),
+ options.monitor_bounds.bottom() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Fits only on right -> placed right.
+ options.anchor_bounds = gfx::Rect(0, options.menu_size.height() / 2, 0, 0);
+ options.monitor_bounds =
+ gfx::Rect(0, 0, options.anchor_bounds.right() + options.menu_size.width(),
+ options.menu_size.height());
+ expected =
+ gfx::Rect(options.anchor_bounds.right(),
+ options.monitor_bounds.bottom() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+}
+
+// Tests that the menu is properly placed according to its anchor.
+TEST_F(MenuControllerTest, CalculateMenuBoundsAnchorTest) {
+ MenuBoundsOptions options;
+ gfx::Rect expected;
+
+ options.menu_anchor = MENU_ANCHOR_TOPLEFT;
+ expected =
+ gfx::Rect(options.anchor_bounds.x(), options.anchor_bounds.bottom(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ options.menu_anchor = MENU_ANCHOR_TOPRIGHT;
+ expected =
+ gfx::Rect(options.anchor_bounds.right() - options.menu_size.width(),
+ options.anchor_bounds.bottom(), options.menu_size.width(),
+ options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Menu will be placed above or below with an offset.
+ options.menu_anchor = MENU_ANCHOR_BOTTOMCENTER;
+ const int kTouchYPadding = 15;
+
+ // Menu fits above -> placed above.
+ expected = gfx::Rect(
+ options.anchor_bounds.x() +
+ (options.anchor_bounds.width() - options.menu_size.width()) / 2,
+ options.anchor_bounds.y() - options.menu_size.height() - kTouchYPadding,
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Menu does not fit above -> placed below.
+ options.anchor_bounds = gfx::Rect(options.menu_size.height() / 2,
+ options.menu_size.width(), 0, 0);
+ expected = gfx::Rect(
+ options.anchor_bounds.x() +
+ (options.anchor_bounds.width() - options.menu_size.width()) / 2,
+ options.anchor_bounds.y() + kTouchYPadding, options.menu_size.width(),
+ options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Assumes anchor bounds is at the bottom of screen.
+ options.menu_anchor = MENU_ANCHOR_FIXED_BOTTOMCENTER;
+ options.anchor_bounds =
+ gfx::Rect(options.menu_size.width(), options.menu_size.height(), 0, 0);
+ options.monitor_bounds = gfx::Rect(0, 0, options.menu_size.width() * 2,
+ options.menu_size.height());
+ expected = gfx::Rect(
+ options.anchor_bounds.x() +
+ (options.anchor_bounds.width() - options.menu_size.width()) / 2,
+ options.anchor_bounds.y() - options.menu_size.height(),
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ // Assumes anchor bounds is on left/right edge of screen.
+ options.menu_anchor = MENU_ANCHOR_FIXED_SIDECENTER;
+ options.monitor_bounds = gfx::Rect(0, 0, options.menu_size.width(),
+ options.menu_size.height() * 2);
+ options.anchor_bounds =
+ gfx::Rect(options.monitor_bounds.x(), options.menu_size.height(), 0, 0);
+ expected = gfx::Rect(
+ options.anchor_bounds.x(),
+ options.anchor_bounds.y() +
+ (options.anchor_bounds.height() - options.menu_size.height()) / 2,
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ options.anchor_bounds = gfx::Rect(options.monitor_bounds.right(),
+ options.menu_size.height(), 0, 0);
+ expected = gfx::Rect(
+ options.anchor_bounds.right() - options.menu_size.width(),
+ options.anchor_bounds.y() +
+ (options.anchor_bounds.height() - options.menu_size.height()) / 2,
+ options.menu_size.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+}
+
+TEST_F(MenuControllerTest, CalculateMenuBoundsMonitorFitTest) {
+ MenuBoundsOptions options;
+ gfx::Rect expected;
+ options.monitor_bounds = gfx::Rect(0, 0, 100, 100);
+ options.anchor_bounds = gfx::Rect();
+
+ options.menu_size = gfx::Size(options.monitor_bounds.width() / 2,
+ options.monitor_bounds.height() * 2);
+ expected =
+ gfx::Rect(options.anchor_bounds.x(), options.anchor_bounds.bottom(),
+ options.menu_size.width(), options.monitor_bounds.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ options.menu_size = gfx::Size(options.monitor_bounds.width() * 2,
+ options.monitor_bounds.height() / 2);
+ expected =
+ gfx::Rect(options.anchor_bounds.x(), options.anchor_bounds.bottom(),
+ options.monitor_bounds.width(), options.menu_size.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+
+ options.menu_size = gfx::Size(options.monitor_bounds.width() * 2,
+ options.monitor_bounds.height() * 2);
+ expected = gfx::Rect(
+ options.anchor_bounds.x(), options.anchor_bounds.bottom(),
+ options.monitor_bounds.width(), options.monitor_bounds.height());
+ EXPECT_EQ(expected, CalculateMenuBounds(options));
+}
+
#if defined(USE_AURA)
+// This tests that mouse moved events from the initial position of the mouse
+// when the menu was shown don't select the menu item at the mouse position.
+TEST_F(MenuControllerTest, MouseAtMenuItemOnShow) {
+ // aura::Window::MoveCursorTo check fails in Mus due to null
+ // window_manager_client_.
+ if (IsMus())
+ return;
+
+ // Most tests create an already shown menu but this test needs one that's
+ // not shown, so it can show it. The mouse position is remembered when
+ // the menu is shown.
+ std::unique_ptr<TestMenuItemViewNotShown> menu_item(
+ new TestMenuItemViewNotShown(menu_delegate()));
+ MenuItemView* first_item =
+ menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
+ menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
+ menu_item->SetController(menu_controller());
+
+ // Move the mouse to where the first menu item will be shown,
+ // and show the menu.
+ gfx::Size item_size = first_item->CalculatePreferredSize();
+ gfx::Point location(item_size.width() / 2, item_size.height() / 2);
+ owner()->GetNativeWindow()->GetRootWindow()->MoveCursorTo(location);
+ menu_controller()->Run(owner(), nullptr, menu_item.get(), gfx::Rect(),
+ MENU_ANCHOR_TOPLEFT, false, false);
+
+ EXPECT_EQ(0, pending_state_item()->GetCommand());
+
+ // Synthesize an event at the mouse position when the menu was opened.
+ // It should be ignored, and selected item shouldn't change.
+ SubmenuView* sub_menu = menu_item->GetSubmenu();
+ View::ConvertPointFromScreen(sub_menu->GetScrollViewContainer(), &location);
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location,
+ ui::EventTimeForNow(), 0, 0);
+ ProcessMouseMoved(sub_menu, event);
+ EXPECT_EQ(0, pending_state_item()->GetCommand());
+ // Synthesize an event at a slightly different mouse position. It
+ // should cause the item under the cursor to be selected.
+ location.Offset(0, 1);
+ ui::MouseEvent second_event(ui::ET_MOUSE_MOVED, location, location,
+ ui::EventTimeForNow(), 0, 0);
+ ProcessMouseMoved(sub_menu, second_event);
+ EXPECT_EQ(1, pending_state_item()->GetCommand());
+}
+
// Tests that when an asynchronous menu receives a cancel event, that it closes.
TEST_F(MenuControllerTest, AsynchronousCancelEvent) {
ExitMenuRun();
@@ -1447,7 +1679,7 @@ TEST_F(MenuControllerTest, RepostEventToEmptyMenuItem) {
->SetContentsView(base_submenu->GetScrollViewContainer());
// Build the submenu to have an empty menu item. Additionally hook up
- // appropriate Widget and View containersm with counds, so that hit testing
+ // appropriate Widget and View containers with bounds, so that hit testing
// works.
std::unique_ptr<TestMenuDelegate> sub_menu_item_delegate =
std::make_unique<TestMenuDelegate>();
@@ -1532,7 +1764,7 @@ TEST_F(MenuControllerTest, RepostEventToEmptyMenuItem) {
gfx::Rect(150, 50, 100, 100), MENU_ANCHOR_TOPLEFT, true,
false);
- // The escapce key should only close the nested menu. SelectByChar should not
+ // The escape key should only close the nested menu. SelectByChar should not
// crash.
TestAsyncEscapeKey();
EXPECT_EQ(nested_controller_delegate_2->on_menu_closed_called(), 1);
diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc
index 22a219b4660..4ede4cb5fa0 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view.cc
@@ -204,7 +204,8 @@ bool MenuItemView::IsBubble(MenuAnchorPosition anchor) {
anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
anchor == MENU_ANCHOR_BUBBLE_BELOW ||
anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE ||
- anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT;
+ anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT ||
+ anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT;
}
// static
@@ -331,6 +332,14 @@ void MenuItemView::AppendSeparator() {
ui::NORMAL_SEPARATOR);
}
+void MenuItemView::AddSeparatorAt(int index) {
+ AddMenuItemAt(index, /*item_id=*/0, /*label=*/base::string16(),
+ /*sub_label=*/base::string16(),
+ /*minor_text=*/base::string16(), /*minor_icon=*/nullptr,
+ /*icon=*/gfx::ImageSkia(), /*type=*/SEPARATOR,
+ /*separator_style=*/ui::NORMAL_SEPARATOR);
+}
+
MenuItemView* MenuItemView::AppendMenuItemWithIcon(int item_id,
const base::string16& label,
const gfx::ImageSkia& icon) {
@@ -616,12 +625,12 @@ void MenuItemView::Layout() {
if (icon_view_) {
icon_view_->SizeToPreferredSize();
gfx::Size size = icon_view_->GetPreferredSize();
- int x = config.item_left_margin + left_icon_margin_ +
+ int x = config.item_horizontal_padding + left_icon_margin_ +
(icon_area_width_ - size.width()) / 2;
if (config.icons_in_label || type_ == CHECKBOX || type_ == RADIO)
x = label_start_;
if (GetMenuController() && GetMenuController()->use_touchable_layout())
- x = config.touchable_item_left_margin;
+ x = config.touchable_item_horizontal_padding;
int y =
(height() + GetTopMargin() - GetBottomMargin() - size.height()) / 2;
@@ -629,9 +638,9 @@ void MenuItemView::Layout() {
}
if (radio_check_image_view_) {
- int x = config.item_left_margin + left_icon_margin_;
+ int x = config.item_horizontal_padding + left_icon_margin_;
if (GetMenuController() && GetMenuController()->use_touchable_layout())
- x = config.touchable_item_left_margin;
+ x = config.touchable_item_horizontal_padding;
int y =
(height() + GetTopMargin() - GetBottomMargin() - kMenuCheckSize) / 2;
radio_check_image_view_->SetBounds(x, y, kMenuCheckSize, kMenuCheckSize);
@@ -723,18 +732,20 @@ void MenuItemView::UpdateMenuPartSizes() {
const bool use_touchable_layout =
GetMenuController() && GetMenuController()->use_touchable_layout();
- label_start_ = (use_touchable_layout ? config.touchable_item_left_margin
- : config.item_left_margin) +
- icon_area_width_;
+ label_start_ =
+ (use_touchable_layout ? config.touchable_item_horizontal_padding
+ : config.item_horizontal_padding) +
+ icon_area_width_;
int padding = 0;
if (config.always_use_icon_to_label_padding) {
- padding = config.icon_to_label_padding;
+ padding = config.item_horizontal_padding;
} else if (!config.icons_in_label) {
- padding = (has_icons_ || HasChecksOrRadioButtons()) ?
- config.icon_to_label_padding : 0;
+ padding = (has_icons_ || HasChecksOrRadioButtons())
+ ? config.item_horizontal_padding
+ : 0;
}
if (use_touchable_layout)
- padding = config.touchable_icon_to_label_padding;
+ padding = config.touchable_item_horizontal_padding;
label_start_ += padding;
@@ -845,6 +856,9 @@ const gfx::FontList& MenuItemView::GetFontList() const {
if (font_list)
return *font_list;
}
+
+ if (GetMenuController() && GetMenuController()->use_touchable_layout())
+ return style::GetFont(style::CONTEXT_TOUCH_MENU, style::STYLE_PRIMARY);
return MenuConfig::instance().font_list;
}
@@ -1006,7 +1020,7 @@ void MenuItemView::PaintMinorIconAndText(gfx::Canvas* canvas, SkColor color) {
int image_x = GetMirroredRect(minor_text_bounds).right() -
render_text->GetContentWidth() -
- (minor_text.empty() ? 0 : config.icon_to_label_padding) -
+ (minor_text.empty() ? 0 : config.item_horizontal_padding) -
image.width();
int minor_text_center_y =
minor_text_bounds.y() + minor_text_bounds.height() / 2;
@@ -1102,18 +1116,25 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const {
gfx::Size child_size = GetChildPreferredSize();
MenuItemDimensions dimensions;
- // Get the container height.
dimensions.children_width = child_size.width();
const MenuConfig& menu_config = MenuConfig::instance();
if (GetMenuController() && GetMenuController()->use_touchable_layout()) {
- // Touchable layout uses a fixed size, but adjusts the height for icons.
dimensions.height = menu_config.touchable_menu_height;
+
+ // For container MenuItemViews, the width components should only include the
+ // |children_width|. Setting a |standard_width| would result in additional
+ // width being added to the container because the total width used in layout
+ // is |children_width| + |standard_width|.
+ if (IsContainer())
+ return dimensions;
+
+ dimensions.standard_width = menu_config.touchable_menu_width;
+
if (icon_view_) {
dimensions.height = icon_view_->height() +
2 * menu_config.vertical_touchable_menu_item_padding;
}
- dimensions.standard_width = menu_config.touchable_menu_width;
return dimensions;
}
@@ -1195,14 +1216,14 @@ int MenuItemView::GetLabelStartForThisItem() const {
// Touchable items with icons do not respect |label_start_|.
if (GetMenuController() && GetMenuController()->use_touchable_layout() &&
icon_view_) {
- return config.touchable_item_left_margin + icon_view_->width() +
- config.touchable_icon_to_label_padding;
+ return 2 * config.touchable_item_horizontal_padding + icon_view_->width();
}
int label_start = label_start_ + left_icon_margin_ + right_icon_margin_;
if ((config.icons_in_label || type_ == CHECKBOX || type_ == RADIO) &&
- icon_view_)
- label_start += icon_view_->size().width() + config.icon_to_label_padding;
+ icon_view_) {
+ label_start += icon_view_->size().width() + config.item_horizontal_padding;
+ }
return label_start;
}
diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h
index 6276932ceaf..929f3f87204 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.h
+++ b/chromium/ui/views/controls/menu/menu_item_view.h
@@ -40,6 +40,7 @@ class MenuRunnerImpl;
namespace test {
class TestMenuItemViewShown;
+class TestMenuItemViewNotShown;
}
class MenuController;
@@ -201,6 +202,9 @@ class VIEWS_EXPORT MenuItemView : public View {
// Adds a separator to this menu
void AppendSeparator();
+ // Adds a separator to this menu at the specified position.
+ void AddSeparatorAt(int index);
+
// Appends a menu item with an icon. This is for the menu item which
// needs an icon. Calling this function forces the Menu class to draw
// the menu, instead of relying on Windows.
@@ -377,6 +381,7 @@ class VIEWS_EXPORT MenuItemView : public View {
private:
friend class internal::MenuRunnerImpl; // For access to ~MenuItemView.
friend class test::TestMenuItemViewShown; // for access to |submenu_|;
+ friend class test::TestMenuItemViewNotShown; // for access to |submenu_|;
friend class TestMenuItemView; // For access to AddEmptyMenus();
enum PaintButtonMode { PB_NORMAL, PB_FOR_DRAG };
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 8ccd3382da9..f0ea47108de 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
@@ -123,6 +123,10 @@ MenuRunnerImplInterface* MenuRunnerImplInterface::Create(
ui::MenuModel* menu_model,
int32_t run_types,
const base::Closure& on_menu_closed_callback) {
+ if ((run_types & MenuRunner::CONTEXT_MENU) &&
+ !(run_types & MenuRunner::IS_NESTED)) {
+ return new MenuRunnerImplCocoa(menu_model, on_menu_closed_callback);
+ }
return new MenuRunnerImplAdapter(menu_model, on_menu_closed_callback);
}
@@ -214,8 +218,7 @@ base::TimeTicks MenuRunnerImplCocoa::GetClosingEventTime() const {
return closing_event_time_;
}
-MenuRunnerImplCocoa::~MenuRunnerImplCocoa() {
-}
+MenuRunnerImplCocoa::~MenuRunnerImplCocoa() {}
} // namespace internal
} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_runner_unittest.cc b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
index 4c6c320cd93..704f242106d 100644
--- a/chromium/ui/views/controls/menu/menu_runner_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_runner_unittest.cc
@@ -64,8 +64,12 @@ class MenuRunnerTest : public ViewsTestBase {
MenuRunnerTest();
~MenuRunnerTest() override;
- // Initializes a MenuRunner with |run_types|. It takes ownership of
- // |menu_item_view_|.
+ // Initializes the delegates and views needed for a menu. It does not create
+ // the MenuRunner.
+ void InitMenuViews();
+
+ // Initializes all delegates and views needed for a menu. A MenuRunner is also
+ // created with |run_types|, it takes ownership of |menu_item_view_|.
void InitMenuRunner(int32_t run_types);
MenuItemView* menu_item_view() { return menu_item_view_; }
@@ -74,12 +78,11 @@ class MenuRunnerTest : public ViewsTestBase {
Widget* owner() { return owner_.get(); }
// ViewsTestBase:
- void SetUp() override;
void TearDown() override;
private:
- // Owned by MenuRunner.
- MenuItemView* menu_item_view_;
+ // Owned by menu_runner_.
+ MenuItemView* menu_item_view_ = nullptr;
std::unique_ptr<TestMenuDelegate> menu_delegate_;
std::unique_ptr<MenuRunner> menu_runner_;
@@ -92,12 +95,7 @@ MenuRunnerTest::MenuRunnerTest() {}
MenuRunnerTest::~MenuRunnerTest() {}
-void MenuRunnerTest::InitMenuRunner(int32_t run_types) {
- menu_runner_.reset(new MenuRunner(menu_item_view_, run_types));
-}
-
-void MenuRunnerTest::SetUp() {
- ViewsTestBase::SetUp();
+void MenuRunnerTest::InitMenuViews() {
menu_delegate_.reset(new TestMenuDelegate);
menu_item_view_ = new MenuItemView(menu_delegate_.get());
menu_item_view_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
@@ -111,8 +109,14 @@ void MenuRunnerTest::SetUp() {
owner_->Show();
}
+void MenuRunnerTest::InitMenuRunner(int32_t run_types) {
+ InitMenuViews();
+ menu_runner_.reset(new MenuRunner(menu_item_view_, run_types));
+}
+
void MenuRunnerTest::TearDown() {
- owner_->CloseNow();
+ if (owner_)
+ owner_->CloseNow();
ViewsTestBase::TearDown();
}
@@ -367,7 +371,21 @@ TEST_F(MenuRunnerWidgetTest, ClearsMouseHandlerOnRun) {
EXPECT_EQ(1, second_event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED));
}
-typedef MenuRunnerTest MenuRunnerImplTest;
+class MenuRunnerImplTest : public MenuRunnerTest {
+ public:
+ MenuRunnerImplTest() {}
+ ~MenuRunnerImplTest() override {}
+
+ void SetUp() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MenuRunnerImplTest);
+};
+
+void MenuRunnerImplTest::SetUp() {
+ MenuRunnerTest::SetUp();
+ InitMenuViews();
+}
// Tests that when nested menu runners are destroyed out of order, that
// MenuController is not accessed after it has been destroyed. This should not
@@ -472,6 +490,7 @@ void MenuRunnerDestructionTest::SetUp() {
views_delegate_ = views_delegate.get();
set_views_delegate(std::move(views_delegate));
MenuRunnerTest::SetUp();
+ InitMenuViews();
}
// Tests that when ViewsDelegate is released that a nested Cancel of the
diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc
index abcc8e28e7e..16a1bef6715 100644
--- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -335,6 +335,7 @@ BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor(
return BubbleBorder::TOP_CENTER;
case MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE:
case MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT:
+ case MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT:
return BubbleBorder::FLOAT;
default:
return BubbleBorder::NONE;
diff --git a/chromium/ui/views/controls/menu/menu_separator.h b/chromium/ui/views/controls/menu/menu_separator.h
index e0dbdb9760d..4e91f1707e2 100644
--- a/chromium/ui/views/controls/menu/menu_separator.h
+++ b/chromium/ui/views/controls/menu/menu_separator.h
@@ -9,10 +9,11 @@
#include "base/macros.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/views/view.h"
+#include "ui/views/views_export.h"
namespace views {
-class MenuSeparator : public View {
+class VIEWS_EXPORT MenuSeparator : public View {
public:
explicit MenuSeparator(ui::MenuSeparatorType type) : type_(type) {}
diff --git a/chromium/ui/views/controls/menu/menu_types.h b/chromium/ui/views/controls/menu/menu_types.h
index 03a0c72a5db..d8419e6ad1e 100644
--- a/chromium/ui/views/controls/menu/menu_types.h
+++ b/chromium/ui/views/controls/menu/menu_types.h
@@ -22,7 +22,10 @@ enum MenuAnchorPosition {
MENU_ANCHOR_BUBBLE_ABOVE,
MENU_ANCHOR_BUBBLE_BELOW,
MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE,
- MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT
+ MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT,
+ MENU_ANCHOR_BUBBLE_TOUCHABLE_RIGHT,
+ // Keep this the last item.
+ MENU_ANCHOR_POSITION_LAST
};
} // namespace views
diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc
index b69a318e69f..cdb36b6df6c 100644
--- a/chromium/ui/views/controls/menu/submenu_view.cc
+++ b/chromium/ui/views/controls/menu/submenu_view.cc
@@ -172,7 +172,7 @@ gfx::Size SubmenuView::CalculatePreferredSize() const {
}
}
if (max_minor_text_width_ > 0)
- max_minor_text_width_ += MenuConfig::instance().label_to_minor_text_padding;
+ max_minor_text_width_ += MenuConfig::instance().item_horizontal_padding;
// Finish calculating our optimum width.
gfx::Insets insets = GetInsets();
@@ -385,6 +385,7 @@ void SubmenuView::ShowAt(Widget* parent,
const gfx::Rect& bounds,
bool do_capture) {
if (host_) {
+ host_->SetMenuHostBounds(bounds);
host_->ShowMenuHost(do_capture);
} else {
host_ = new MenuHost(this);
diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc
index f15b82824d5..32019ba3fdb 100644
--- a/chromium/ui/views/controls/native/native_view_host.cc
+++ b/chromium/ui/views/controls/native/native_view_host.cc
@@ -8,6 +8,7 @@
#include "ui/base/cursor/cursor.h"
#include "ui/gfx/canvas.h"
#include "ui/views/controls/native/native_view_host_wrapper.h"
+#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"
namespace views {
@@ -49,7 +50,14 @@ void NativeViewHost::Detach() {
}
bool NativeViewHost::SetCornerRadius(int corner_radius) {
- return native_wrapper_->SetCornerRadius(corner_radius);
+ return SetCustomMask(views::Painter::CreatePaintedLayer(
+ views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+ corner_radius)));
+}
+
+bool NativeViewHost::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) {
+ DCHECK(native_wrapper_);
+ return native_wrapper_->SetCustomMask(std::move(mask));
}
void NativeViewHost::SetNativeViewSize(const gfx::Size& size) {
diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h
index c81cb7443a5..5bd897cf3ed 100644
--- a/chromium/ui/views/controls/native/native_view_host.h
+++ b/chromium/ui/views/controls/native/native_view_host.h
@@ -49,9 +49,14 @@ class VIEWS_EXPORT NativeViewHost : public View {
// Sets the corner radius for clipping gfx::NativeView. Returns true on
// success or false if the platform doesn't support the operation.
- // NB: This does not interact nicely with fast_resize.
+ // This method calls SetCustomMask internally.
bool SetCornerRadius(int corner_radius);
+ // Sets the custom layer mask for clipping gfx::NativeView. Returns true on
+ // success or false if the platform doesn't support the operation.
+ // NB: This does not interact nicely with fast_resize.
+ bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask);
+
// Sets the size for the NativeView that may or may not match the size of this
// View when it is being captured. If the size does not match, scaling will
// occur. Pass an empty size to revert to the default behavior, where the
diff --git a/chromium/ui/views/controls/native/native_view_host_aura.cc b/chromium/ui/views/controls/native/native_view_host_aura.cc
index 097bcd4879b..6afc8fbf9c3 100644
--- a/chromium/ui/views/controls/native/native_view_host_aura.cc
+++ b/chromium/ui/views/controls/native/native_view_host_aura.cc
@@ -19,6 +19,7 @@
#include "ui/views/view_constants_aura.h"
#include "ui/views/view_properties.h"
#include "ui/views/widget/widget.h"
+#include "ui/wm/core/window_util.h"
namespace views {
@@ -148,16 +149,16 @@ void NativeViewHostAura::RemovedFromWidget() {
}
}
-bool NativeViewHostAura::SetCornerRadius(int corner_radius) {
+bool NativeViewHostAura::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) {
#if defined(OS_WIN)
// TODO(crbug/843250): On Aura, layer masks don't play with HiDPI. Fix this
// and enable this on Windows.
return false;
#else
- mask_ = views::Painter::CreatePaintedLayer(
- views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
- corner_radius));
- mask_->layer()->SetFillsBoundsOpaquely(false);
+ UninstallMask();
+ mask_ = std::move(mask);
+ if (mask_)
+ mask_->layer()->SetFillsBoundsOpaquely(false);
InstallMask();
return true;
#endif
@@ -237,8 +238,15 @@ void NativeViewHostAura::OnWindowBoundsChanged(
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
- if (mask_)
+ if (mask_) {
+ // Having a mask means this layer has a render surface of its own. This
+ // means we want this layer snapped as the render surface uses this layer
+ // (its primary layer) to snap to the physical pixel grid.
+ // See https://crbug.com/843250 for more details.
+ wm::SnapWindowToPixelBoundary(window);
+
mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size()));
+ }
}
void NativeViewHostAura::OnWindowDestroying(aura::Window* window) {
@@ -292,9 +300,24 @@ void NativeViewHostAura::InstallMask() {
if (!mask_)
return;
if (host_->native_view()) {
+ // Setting a mask triggers this layer to have a render surface of its own.
+ // This means we cannot skip computing its subpixel offset positioning as
+ // the render surface uses this layer (its primary layer) to snap to the
+ // physical pixel grid.
+ // See https://crbug.com/843250 for more details.
+ wm::SnapWindowToPixelBoundary(host_->native_view());
+
mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size()));
host_->native_view()->layer()->SetMaskLayer(mask_->layer());
}
}
+void NativeViewHostAura::UninstallMask() {
+ if (!host_->native_view() || !mask_)
+ return;
+
+ host_->native_view()->layer()->SetMaskLayer(nullptr);
+ mask_.reset();
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/native/native_view_host_aura.h b/chromium/ui/views/controls/native/native_view_host_aura.h
index 41a56cc63e4..0ea78965b8f 100644
--- a/chromium/ui/views/controls/native/native_view_host_aura.h
+++ b/chromium/ui/views/controls/native/native_view_host_aura.h
@@ -30,7 +30,7 @@ class NativeViewHostAura : public NativeViewHostWrapper,
void NativeViewDetaching(bool destroyed) override;
void AddedToWidget() override;
void RemovedFromWidget() override;
- bool SetCornerRadius(int corner_radius) override;
+ bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) override;
void InstallClip(int x, int y, int w, int h) override;
bool HasInstalledClip() override;
void UninstallClip() override;
@@ -64,6 +64,9 @@ class NativeViewHostAura : public NativeViewHostWrapper,
// Sets or updates the mask layer on the native view's layer.
void InstallMask();
+ // Unsets the mask layer on the native view's layer.
+ void UninstallMask();
+
// Our associated NativeViewHost.
NativeViewHost* host_;
diff --git a/chromium/ui/views/controls/native/native_view_host_mac.h b/chromium/ui/views/controls/native/native_view_host_mac.h
index 5db28b49aba..69659c9aafd 100644
--- a/chromium/ui/views/controls/native/native_view_host_mac.h
+++ b/chromium/ui/views/controls/native/native_view_host_mac.h
@@ -10,6 +10,10 @@
#include "ui/views/controls/native/native_view_host_wrapper.h"
#include "ui/views/views_export.h"
+namespace ui {
+class LayerOwner;
+}
+
namespace views {
class NativeViewHost;
@@ -25,7 +29,7 @@ class NativeViewHostMac : public NativeViewHostWrapper {
void NativeViewDetaching(bool destroyed) override;
void AddedToWidget() override;
void RemovedFromWidget() override;
- bool SetCornerRadius(int corner_radius) override;
+ bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) override;
void InstallClip(int x, int y, int w, int h) override;
bool HasInstalledClip() override;
void UninstallClip() override;
diff --git a/chromium/ui/views/controls/native/native_view_host_mac.mm b/chromium/ui/views/controls/native/native_view_host_mac.mm
index 0ca2c4a6a41..debbe51f102 100644
--- a/chromium/ui/views/controls/native/native_view_host_mac.mm
+++ b/chromium/ui/views/controls/native/native_view_host_mac.mm
@@ -7,6 +7,8 @@
#import <Cocoa/Cocoa.h>
#include "base/mac/foundation_util.h"
+#import "ui/accessibility/platform/ax_platform_node_mac.h"
+#import "ui/base/cocoa/accessibility_hostable.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/widget/native_widget_mac.h"
@@ -36,6 +38,17 @@ void EnsureNativeViewHasNoChildWidgets(NSView* native_view) {
}
}
+AXPlatformNodeCocoa* ClosestPlatformAncestorNode(views::View* view) {
+ do {
+ gfx::NativeViewAccessible accessible = view->GetNativeViewAccessible();
+ if ([accessible isKindOfClass:[AXPlatformNodeCocoa class]]) {
+ return NSAccessibilityUnignoredAncestor(accessible);
+ }
+ view = view->parent();
+ } while (view);
+ return nil;
+}
+
} // namespace
NativeViewHostMac::NativeViewHostMac(NativeViewHost* host) : host_(host) {
@@ -56,6 +69,28 @@ void NativeViewHostMac::AttachNativeView() {
if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
[native_view_ cr_setParentUiLayer:host_->layer()];
+ if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
+ // Find the closest ancestor view that participates in the views toolkit
+ // accessibility hierarchy and set its element as the native view's parent.
+ // This is necessary because a closer ancestor might already be attaching
+ // to the NSView/content hierarchy.
+ // For example, web content is currently embedded into the views hierarchy
+ // roughly like this:
+ // BrowserView (views)
+ // |_ WebView (views)
+ // |_ NativeViewHost (views)
+ // |_ WebContentView (Cocoa, is |native_view_| in this scenario,
+ // | accessibility ignored).
+ // |_ RenderWidgetHostView (Cocoa)
+ // WebView specifies either the RenderWidgetHostView or the native view as
+ // its accessibility element. That means that if we were to set it as
+ // |native_view_|'s parent, the RenderWidgetHostView would be its own
+ // accessibility parent! Instead, we want to find the browser view and
+ // attach to its node.
+ id hostable = native_view_;
+ [hostable setAccessibilityParentElement:ClosestPlatformAncestorNode(
+ host_->parent())];
+ }
EnsureNativeViewHasNoChildWidgets(native_view_);
BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
@@ -85,6 +120,10 @@ void NativeViewHostMac::NativeViewDetaching(bool destroyed) {
if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
[native_view_ cr_setParentUiLayer:nullptr];
+ if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
+ id hostable = native_view_;
+ [hostable setAccessibilityParentElement:nil];
+ }
EnsureNativeViewHasNoChildWidgets(host_->native_view());
BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
@@ -111,7 +150,7 @@ void NativeViewHostMac::RemovedFromWidget() {
NativeViewDetaching(false);
}
-bool NativeViewHostMac::SetCornerRadius(int corner_radius) {
+bool NativeViewHostMac::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) {
NOTIMPLEMENTED();
return false;
}
@@ -164,7 +203,7 @@ void NativeViewHostMac::SetFocus() {
}
gfx::NativeViewAccessible NativeViewHostMac::GetNativeViewAccessible() {
- return NULL;
+ return nullptr;
}
gfx::NativeCursor NativeViewHostMac::GetCursor(int x, int y) {
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 bb2493f5c11..4a4fdcad1b1 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
@@ -12,11 +12,19 @@
#import "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#import "testing/gtest_mac.h"
+#import "ui/base/cocoa/accessibility_hostable.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/controls/native/native_view_host_test_base.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
+@interface TestAccessibilityHostableView : NSView<AccessibilityHostable>
+@property(nonatomic, assign) id accessibilityParentElement;
+@end
+@implementation TestAccessibilityHostableView
+@synthesize accessibilityParentElement = accessibilityParentElement_;
+@end
+
namespace views {
class NativeViewHostMacTest : public test::NativeViewHostTestBase {
@@ -100,6 +108,24 @@ TEST_F(NativeViewHostMacTest, Attach) {
DestroyHost();
}
+// Ensure the native view is integrated into the views accessibility
+// hierarchy if the native view conforms to the AccessibilityParent
+// protocol.
+TEST_F(NativeViewHostMacTest, AccessibilityParent) {
+ CreateHost();
+ host()->Detach();
+
+ base::scoped_nsobject<TestAccessibilityHostableView> view(
+ [[TestAccessibilityHostableView alloc] init]);
+ host()->Attach(view);
+ EXPECT_NSEQ([view accessibilityParentElement],
+ toplevel()->GetRootView()->GetNativeViewAccessible());
+
+ host()->Detach();
+ DestroyHost();
+ EXPECT_FALSE([view accessibilityParentElement]);
+}
+
// Test that the content windows' bounds are set to the correct values while the
// native size is equal or not equal to the View size.
TEST_F(NativeViewHostMacTest, ContentViewPositionAndSize) {
diff --git a/chromium/ui/views/controls/native/native_view_host_wrapper.h b/chromium/ui/views/controls/native/native_view_host_wrapper.h
index b8c156f14fc..513c35f1a71 100644
--- a/chromium/ui/views/controls/native/native_view_host_wrapper.h
+++ b/chromium/ui/views/controls/native/native_view_host_wrapper.h
@@ -8,6 +8,10 @@
#include "ui/gfx/native_widget_types.h"
#include "ui/views/views_export.h"
+namespace ui {
+class LayerOwner;
+}
+
namespace views {
class NativeViewHost;
@@ -38,9 +42,9 @@ class NativeViewHostWrapper {
// rooted at a valid Widget.
virtual void RemovedFromWidget() = 0;
- // Sets the corner radius for clipping gfx::NativeView. Returns true on
+ // Sets the custom mask for clipping gfx::NativeView. Returns true on
// success or false if the platform doesn't support the operation.
- virtual bool SetCornerRadius(int corner_radius) = 0;
+ virtual bool SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) = 0;
// Installs a clip on the gfx::NativeView. These values are in the coordinate
// space of the Widget, so if this method is called from ShowWidget
diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc
index 6590a8e0b05..9844d192d78 100644
--- a/chromium/ui/views/controls/prefix_selector.cc
+++ b/chromium/ui/views/controls/prefix_selector.cc
@@ -145,10 +145,10 @@ bool PrefixSelector::IsTextEditCommandEnabled(
void PrefixSelector::SetTextEditCommandForNextKeyEvent(
ui::TextEditCommand command) {}
-const std::string& PrefixSelector::GetClientSourceInfo() const {
- // TODO(yhanada): Implement this method.
+ukm::SourceId PrefixSelector::GetClientSourceForMetrics() const {
+ // TODO(shend): Implement this method.
NOTIMPLEMENTED_LOG_ONCE();
- return base::EmptyString();
+ return ukm::SourceId();
}
bool PrefixSelector::ShouldDoLearning() {
diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h
index e3654eebbb3..ba2005191b3 100644
--- a/chromium/ui/views/controls/prefix_selector.h
+++ b/chromium/ui/views/controls/prefix_selector.h
@@ -60,7 +60,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
- const std::string& GetClientSourceInfo() const override;
+ ukm::SourceId GetClientSourceForMetrics() const override;
bool ShouldDoLearning() override;
private:
diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc
index d2596d71109..4da3917fefb 100644
--- a/chromium/ui/views/controls/scroll_view_unittest.cc
+++ b/chromium/ui/views/controls/scroll_view_unittest.cc
@@ -44,7 +44,8 @@ class ScrollViewTestApi {
return static_cast<BaseScrollBar*>(scroll_bar);
}
- const base::Timer& GetScrollBarTimer(ScrollBarOrientation orientation) {
+ const base::OneShotTimer& GetScrollBarTimer(
+ ScrollBarOrientation orientation) {
return GetBaseScrollBar(orientation)->repeater_.timer_for_testing();
}
@@ -58,7 +59,8 @@ class ScrollViewTestApi {
gfx::ScrollOffset CurrentOffset() { return scroll_view_->CurrentOffset(); }
- base::Timer* GetScrollBarHideTimer(ScrollBarOrientation orientation) {
+ base::RetainingOneShotTimer* GetScrollBarHideTimer(
+ ScrollBarOrientation orientation) {
return BaseScrollBar::GetHideTimerForTest(GetBaseScrollBar(orientation));
}
@@ -987,8 +989,9 @@ TEST_F(WidgetScrollViewTest, ScrollersOnRest) {
ScrollViewTestApi test_api(scroll_view);
BaseScrollBar* bar[]{test_api.GetBaseScrollBar(HORIZONTAL),
test_api.GetBaseScrollBar(VERTICAL)};
- base::Timer* hide_timer[]{test_api.GetScrollBarHideTimer(HORIZONTAL),
- test_api.GetScrollBarHideTimer(VERTICAL)};
+ base::RetainingOneShotTimer* hide_timer[] = {
+ test_api.GetScrollBarHideTimer(HORIZONTAL),
+ test_api.GetScrollBarHideTimer(VERTICAL)};
EXPECT_EQ(0, bar[HORIZONTAL]->layer()->opacity());
EXPECT_EQ(0, bar[VERTICAL]->layer()->opacity());
@@ -1459,7 +1462,7 @@ TEST_F(WidgetScrollViewTest, ScrollTrackScrolling) {
ui::MouseEvent press(TestLeftMouseAt(location, ui::ET_MOUSE_PRESSED));
ui::MouseEvent release(TestLeftMouseAt(location, ui::ET_MOUSE_RELEASED));
- const base::Timer& timer = test_api.GetScrollBarTimer(VERTICAL);
+ const base::OneShotTimer& timer = test_api.GetScrollBarTimer(VERTICAL);
EXPECT_FALSE(timer.IsRunning());
EXPECT_EQ(0, scroll_view->GetVisibleRect().y());
diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc
index bf652746430..0801c66123a 100644
--- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc
@@ -405,7 +405,8 @@ int BaseScrollBar::GetScrollIncrement(bool is_page, bool is_positive) {
#if !defined(OS_MACOSX)
// static
-base::Timer* BaseScrollBar::GetHideTimerForTest(BaseScrollBar* scroll_bar) {
+base::RetainingOneShotTimer* BaseScrollBar::GetHideTimerForTest(
+ BaseScrollBar* scroll_bar) {
return nullptr;
}
#endif
diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h
index 07ae5b04111..9b447add2ee 100644
--- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h
+++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h
@@ -111,7 +111,8 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar,
FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, ScrollBarFitsToBottom);
FRIEND_TEST_ALL_PREFIXES(ScrollBarViewsTest, ThumbFullLengthOfTrack);
- static base::Timer* GetHideTimerForTest(BaseScrollBar* scroll_bar);
+ static base::RetainingOneShotTimer* GetHideTimerForTest(
+ BaseScrollBar* scroll_bar);
int GetThumbSizeForTest();
// Changes to 'pushed' state and starts a timer to scroll repeatedly.
diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h
index a78bc8467ad..a0c88726dab 100644
--- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h
+++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h
@@ -98,7 +98,7 @@ class VIEWS_EXPORT CocoaScrollBar : public BaseScrollBar,
NSScrollerStyle scroller_style_;
// Timer that will start the scrollbar's hiding animation when it reaches 0.
- base::Timer hide_scrollbar_timer_;
+ base::RetainingOneShotTimer hide_scrollbar_timer_;
// Slide animation that animates the thickness of an overlay scrollbar.
// The animation expands the scrollbar as the showing animation and shrinks
diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index 1f977ae170e..05431a33382 100644
--- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -178,8 +178,7 @@ CocoaScrollBar::CocoaScrollBar(bool horizontal)
hide_scrollbar_timer_(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kScrollbarHideTimeoutMs),
- base::Bind(&CocoaScrollBar::HideScrollbar, base::Unretained(this)),
- false),
+ base::Bind(&CocoaScrollBar::HideScrollbar, base::Unretained(this))),
thickness_animation_(this),
last_contents_scroll_offset_(0),
is_expanded_(false),
@@ -542,7 +541,8 @@ CocoaScrollBarThumb* CocoaScrollBar::GetCocoaScrollBarThumb() const {
}
// static
-base::Timer* BaseScrollBar::GetHideTimerForTest(BaseScrollBar* scroll_bar) {
+base::RetainingOneShotTimer* BaseScrollBar::GetHideTimerForTest(
+ BaseScrollBar* scroll_bar) {
return &static_cast<CocoaScrollBar*>(scroll_bar)->hide_scrollbar_timer_;
}
diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc
index e62c0c84a1b..acd37458520 100644
--- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc
+++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc
@@ -124,7 +124,7 @@ void OverlayScrollBar::Thumb::OnStateChanged() {
}
OverlayScrollBar::OverlayScrollBar(bool horizontal)
- : BaseScrollBar(horizontal), hide_timer_(false, false) {
+ : BaseScrollBar(horizontal) {
auto* thumb = new Thumb(this);
SetThumb(thumb);
thumb->Init();
diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h
index b0f2a7c7c9a..ac1ac3849c0 100644
--- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h
+++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h
@@ -59,7 +59,7 @@ class VIEWS_EXPORT OverlayScrollBar : public BaseScrollBar {
// Starts a countdown that hides this when it fires.
void StartHideCountdown();
- base::Timer hide_timer_;
+ base::OneShotTimer hide_timer_;
DISALLOW_COPY_AND_ASSIGN(OverlayScrollBar);
};
diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc
index 4f18b6880c4..a71c62d657a 100644
--- a/chromium/ui/views/controls/styled_label_unittest.cc
+++ b/chromium/ui/views/controls/styled_label_unittest.cc
@@ -18,6 +18,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_features.h"
+#include "ui/base/ui_base_switches.h"
#include "ui/gfx/font_list.h"
#include "ui/views/border.h"
#include "ui/views/controls/link.h"
@@ -79,6 +80,12 @@ class MDStyledLabelTest
// StyledLabelTest:
void SetUp() override {
+ if (GetParam() == SecondaryUiMode::NON_MD) {
+ // Force Refresh UI to be off, since that mode implies MD secondary UI.
+ // Must be done before ViewsTestBase::SetUp().
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kTopChromeMD, switches::kTopChromeMDMaterial);
+ }
// This works while StyledLabelTest has no SetUp() of its own. Otherwise the
// mode should be set after ViewsTestBase::SetUp(), but before the rest of
// StyledLabelTest::SetUp(), so that StyledLabelTest::SetUp() obeys the MD
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 17f9259f012..4cd621b05c5 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -4,6 +4,7 @@
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
+#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/macros.h"
#include "cc/paint/paint_flags.h"
@@ -18,6 +19,7 @@
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/native_theme/native_theme.h"
@@ -37,19 +39,19 @@ namespace {
// TODO(markusheintz|msw): Use NativeTheme colors.
constexpr SkColor kTabTitleColor_InactiveBorder =
SkColorSetARGB(0xFF, 0x64, 0x64, 0x64);
-constexpr SkColor kTabTitleColor_InactiveHighlight =
- SkColorSetARGB(0xFF, 0x80, 0x86, 0x8B);
+constexpr SkColor kTabTitleColor_InactiveHighlight = gfx::kGoogleGrey700;
constexpr SkColor kTabTitleColor_ActiveBorder = SK_ColorBLACK;
-constexpr SkColor kTabTitleColor_ActiveHighlight =
- SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4);
+constexpr SkColor kTabTitleColor_ActiveHighlight = gfx::kGoogleBlue600;
const SkColor kTabTitleColor_Hovered = SK_ColorBLACK;
const SkColor kTabBorderColor = SkColorSetRGB(0xC8, 0xC8, 0xC8);
const SkScalar kTabBorderThickness = 1.0f;
-constexpr SkColor kTabHighlightBackgroundColor =
+constexpr SkColor kTabHighlightBackgroundColor_Active =
SkColorSetARGB(0xFF, 0xE8, 0xF0, 0xFE);
+constexpr SkColor kTabHighlightBackgroundColor_Focused =
+ SkColorSetARGB(0xFF, 0xD2, 0xE3, 0xFC);
constexpr int kTabHighlightBorderRadius = 32;
constexpr int kTabHighlightPreferredHeight = 32;
-constexpr int kTabHighlightPreferredWidth = 208;
+constexpr int kTabHighlightPreferredWidth = 192;
const gfx::Font::Weight kHoverWeightBorder = gfx::Font::Weight::NORMAL;
const gfx::Font::Weight kHoverWeightHighlight = gfx::Font::Weight::MEDIUM;
@@ -134,10 +136,8 @@ Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents)
const bool is_highlight_style =
tabbed_pane_->GetStyle() == TabbedPane::TabStripStyle::kHighlight;
- if (is_vertical) {
+ if (is_vertical)
title_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
- title_->SetElideBehavior(gfx::ElideBehavior::NO_ELIDE);
- }
if (is_highlight_style && is_vertical) {
const int kTabVerticalPadding = 8;
@@ -273,13 +273,22 @@ void Tab::OnPaint(gfx::Canvas* canvas) {
}
SkScalar radius = SkIntToScalar(kTabHighlightBorderRadius);
- const SkScalar kRadius[8] = {0, 0, radius, radius, radius, radius, 0, 0};
SkPath path;
gfx::Rect bounds(size());
- path.addRoundRect(gfx::RectToSkRect(bounds), kRadius);
+ if (base::i18n::IsRTL()) {
+ const SkScalar kRadius[8] = {radius, radius, 0, 0, 0, 0, radius, radius};
+ path.addRoundRect(gfx::RectToSkRect(bounds), kRadius);
+ } else {
+ const SkScalar kRadius[8] = {0, 0, radius, radius, radius, radius, 0, 0};
+ path.addRoundRect(gfx::RectToSkRect(bounds), kRadius);
+ }
+
cc::PaintFlags fill_flags;
fill_flags.setAntiAlias(true);
- fill_flags.setColor(kTabHighlightBackgroundColor);
+ if (HasFocus())
+ fill_flags.setColor(kTabHighlightBackgroundColor_Focused);
+ else
+ fill_flags.setColor(kTabHighlightBackgroundColor_Active);
canvas->DrawPath(path, fill_flags);
}
@@ -367,16 +376,21 @@ gfx::Size MdTab::CalculatePreferredSize() const {
}
void MdTab::OnFocus() {
- SetBorder(CreateSolidBorder(
- GetInsets().top(),
- SkColorSetA(GetNativeTheme()->GetSystemColor(
- ui::NativeTheme::kColorId_FocusedBorderColor),
- 0x66)));
+ // Do not draw focus ring in kHighlight mode.
+ if (tabbed_pane()->GetStyle() != TabbedPane::TabStripStyle::kHighlight) {
+ SetBorder(CreateSolidBorder(
+ GetInsets().top(),
+ SkColorSetA(GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_FocusedBorderColor),
+ 0x66)));
+ }
SchedulePaint();
}
void MdTab::OnBlur() {
- SetBorder(CreateEmptyBorder(GetInsets()));
+ // Do not draw focus ring in kHighlight mode.
+ if (tabbed_pane()->GetStyle() != TabbedPane::TabStripStyle::kHighlight)
+ SetBorder(CreateEmptyBorder(GetInsets()));
SchedulePaint();
}
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
index 0d3cec8d493..50a34abf01b 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -153,6 +153,8 @@ class Tab : public View {
protected:
Label* title() { return title_; }
+ TabbedPane* tabbed_pane() { return tabbed_pane_; }
+
// Called whenever |tab_state_| changes.
virtual void OnStateChanged();
diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc
index 3c1d189c674..c9a96b43788 100644
--- a/chromium/ui/views/controls/textfield/textfield.cc
+++ b/chromium/ui/views/controls/textfield/textfield.cc
@@ -325,7 +325,7 @@ void Textfield::SetAssociatedLabel(View* labelling_view) {
ui::AXNodeData node_data;
labelling_view->GetAccessibleNodeData(&node_data);
// TODO(aleventhal) automatically handle setting the name from the related
- // label in view_accessibility and have it update the name if the text of the
+ // label in ViewAccessibility and have it update the name if the text of the
// associated label changes.
SetAccessibleName(
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
@@ -531,10 +531,10 @@ void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
GetRenderText()->SetHorizontalAlignment(alignment);
}
-void Textfield::ShowImeIfNeeded() {
+void Textfield::ShowVirtualKeyboardIfEnabled() {
// GetInputMethod() may return nullptr in tests.
if (enabled() && !read_only() && GetInputMethod())
- GetInputMethod()->ShowImeIfNeeded();
+ GetInputMethod()->ShowVirtualKeyboardIfEnabled();
}
bool Textfield::IsIMEComposing() const {
@@ -666,7 +666,7 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
(event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())) {
if (!had_focus)
RequestFocusWithPointer(ui::EventPointerType::POINTER_TYPE_MOUSE);
- ShowImeIfNeeded();
+ ShowVirtualKeyboardIfEnabled();
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -746,7 +746,7 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) {
switch (event->type()) {
case ui::ET_GESTURE_TAP_DOWN:
RequestFocusWithPointer(event->details().primary_pointer_type());
- ShowImeIfNeeded();
+ ShowVirtualKeyboardIfEnabled();
event->SetHandled();
break;
case ui::ET_GESTURE_TAP:
@@ -1035,11 +1035,11 @@ bool Textfield::HandleAccessibleAction(const ui::AXActionData& action_data) {
return View::HandleAccessibleAction(action_data);
if (action_data.action == ax::mojom::Action::kSetValue) {
- SetText(action_data.value);
+ SetText(base::UTF8ToUTF16(action_data.value));
ClearSelection();
return true;
} else if (action_data.action == ax::mojom::Action::kReplaceSelectedText) {
- InsertOrReplaceText(action_data.value);
+ InsertOrReplaceText(base::UTF8ToUTF16(action_data.value));
ClearSelection();
return true;
}
@@ -1168,6 +1168,15 @@ void Textfield::OnCompositionTextConfirmedOrCleared() {
void Textfield::ShowContextMenuForView(View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) {
+#if defined(OS_MACOSX)
+ // On Mac, the context menu contains a look up item which displays the
+ // selected text. As such, the menu needs to be updated if the selection has
+ // changed. Be careful to reset the MenuRunner first so it doesn't reference
+ // the old model.
+ context_menu_runner_.reset();
+ context_menu_contents_.reset();
+#endif
+
UpdateContextMenu();
context_menu_runner_->RunMenuAt(GetWidget(), NULL,
gfx::Rect(point, gfx::Size()),
@@ -1743,10 +1752,10 @@ void Textfield::SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) {
scheduled_text_edit_command_ = command;
}
-const std::string& Textfield::GetClientSourceInfo() const {
- // TODO(yhanada): Implement this method.
+ukm::SourceId Textfield::GetClientSourceForMetrics() const {
+ // TODO(shend): Implement this method.
NOTIMPLEMENTED_LOG_ONCE();
- return base::EmptyString();
+ return ukm::SourceId();
}
bool Textfield::ShouldDoLearning() {
@@ -1984,6 +1993,14 @@ void Textfield::OffsetDoubleClickWord(int offset) {
selection_controller_.OffsetDoubleClickWord(offset);
}
+bool Textfield::IsDropCursorForInsertion() const {
+ return true;
+}
+
+bool Textfield::ShouldShowPlaceholderText() const {
+ return text().empty() && !GetPlaceholderText().empty();
+}
+
////////////////////////////////////////////////////////////////////////////////
// Textfield, private:
@@ -2136,7 +2153,7 @@ void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
// Draw placeholder text if needed.
gfx::RenderText* render_text = GetRenderText();
- if (text().empty() && !GetPlaceholderText().empty()) {
+ if (ShouldShowPlaceholderText()) {
// Disable subpixel rendering when the background color is not opaque
// because it draws incorrect colors around the glyphs in that case.
// See crbug.com/786343
@@ -2155,10 +2172,26 @@ void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
render_text->display_rect(), placeholder_text_draw_flags);
}
- render_text->Draw(canvas);
+ if (drop_cursor_visible_ && !IsDropCursorForInsertion()) {
+ // Draw as selected to mark the entire text that will be replaced by drop.
+ // TODO(http://crbug.com/853678): Replace this state push/pop with a clean
+ // call to a finer grained rendering API when one becomes available. These
+ // changes are only applied because RenderText is so stateful and subtle
+ // (e.g. focus is required for the selection to be rendered as selected).
+ const gfx::SelectionModel sm = render_text->selection_model();
+ const bool focused = render_text->focused();
+ render_text->SelectAll(true);
+ render_text->set_focused(true);
+ render_text->Draw(canvas);
+ render_text->set_focused(focused);
+ render_text->SetSelection(sm);
+ } else {
+ // Draw text as normal
+ render_text->Draw(canvas);
+ }
- // Draw the detached drop cursor that marks where the text will be dropped.
- if (drop_cursor_visible_) {
+ if (drop_cursor_visible_ && IsDropCursorForInsertion()) {
+ // Draw a drop cursor that marks where the text will be dropped/inserted.
canvas->FillRect(render_text->GetCursorBounds(drop_cursor_position_, true),
GetTextColor());
}
@@ -2177,15 +2210,6 @@ void Textfield::OnCaretBoundsChanged() {
if (touch_selection_controller_)
touch_selection_controller_->SelectionChanged();
-#if defined(OS_MACOSX)
- // On Mac, the context menu contains a look up item which displays the
- // selected text. As such, the menu needs to be updated if the selection has
- // changed. Be careful to reset the MenuRunner first so it doesn't reference
- // the old model.
- context_menu_runner_.reset();
- context_menu_contents_.reset();
-#endif
-
// Screen reader users don't expect notifications about unfocused textfields.
if (HasFocus())
NotifyAccessibilityEvent(ax::mojom::Event::kTextSelectionChanged, true);
diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h
index 3df902cef6d..314bde9feff 100644
--- a/chromium/ui/views/controls/textfield/textfield.h
+++ b/chromium/ui/views/controls/textfield/textfield.h
@@ -197,7 +197,7 @@ class VIEWS_EXPORT Textfield : public View,
void SetHorizontalAlignment(gfx::HorizontalAlignment alignment);
// Displays a virtual keyboard or alternate input view if enabled.
- void ShowImeIfNeeded();
+ void ShowVirtualKeyboardIfEnabled();
// Returns whether or not an IME is composing text.
bool IsIMEComposing() const;
@@ -359,7 +359,7 @@ class VIEWS_EXPORT Textfield : public View,
void EnsureCaretNotInRect(const gfx::Rect& rect) override;
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
- const std::string& GetClientSourceInfo() const override;
+ ukm::SourceId GetClientSourceForMetrics() const override;
bool ShouldDoLearning() override;
protected:
@@ -383,6 +383,15 @@ class VIEWS_EXPORT Textfield : public View,
// This is harmless if there is not a currently double-clicked word.
void OffsetDoubleClickWord(int offset);
+ // Returns true if the drop cursor is for insertion at a target text location,
+ // the standard behavior/style. Returns false when drop will do something
+ // else (like replace the text entirely).
+ virtual bool IsDropCursorForInsertion() const;
+
+ // Returns true if the placeholder text should be shown. Subclasses may
+ // override this to customize when the placeholder text is shown.
+ virtual bool ShouldShowPlaceholderText() const;
+
private:
friend class TextfieldTestApi;
diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc
index 12e9a987863..6ccde824c4d 100644
--- a/chromium/ui/views/controls/textfield/textfield_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc
@@ -96,7 +96,7 @@ class MockInputMethod : public ui::InputMethodBase {
void OnCaretBoundsChanged(const ui::TextInputClient* client) override {}
void CancelComposition(const ui::TextInputClient* client) override;
bool IsCandidatePopupOpen() const override;
- void ShowImeIfNeeded() override {}
+ void ShowVirtualKeyboardIfEnabled() override {}
bool untranslated_ime_message_called() const {
return untranslated_ime_message_called_;
@@ -698,14 +698,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
textfield_->GetSelectedRange().length() == text.length();
int menu_index = 0;
-#if defined(OS_MACOSX)
- // On Mac, the Look Up item should appear at the top of the menu if the
- // textfield has a selection.
- if (textfield_has_selection) {
- EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */));
- EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
- }
-#endif
if (ui::IsEmojiPanelSupported()) {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
@@ -3550,6 +3542,11 @@ TEST_F(TextfieldTest, LookUpItemUpdate) {
EXPECT_EQ(context_menu->GetLabelAt(0),
l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kTextOne));
+#if !defined(OS_MACOSX)
+ // Mac context menus don't behave this way: it's not possible to update the
+ // text while the menu is still "open", but also the selection can't change
+ // while the menu is open (because the user can't interact with the rest of
+ // the app).
const base::string16 kTextTwo = ASCIIToUTF16("rail");
textfield_->SetText(kTextTwo);
textfield_->SelectAll(false);
@@ -3559,6 +3556,7 @@ TEST_F(TextfieldTest, LookUpItemUpdate) {
EXPECT_GT(context_menu->GetItemCount(), 0);
EXPECT_EQ(context_menu->GetLabelAt(0),
l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kTextTwo));
+#endif
}
// Tests to see if the look up item is hidden for password fields.
diff --git a/chromium/ui/views/controls/views_text_services_context_menu.cc b/chromium/ui/views/controls/views_text_services_context_menu.cc
index 03a3a7b6e61..5e1559e61bc 100644
--- a/chromium/ui/views/controls/views_text_services_context_menu.cc
+++ b/chromium/ui/views/controls/views_text_services_context_menu.cc
@@ -25,4 +25,4 @@ bool ViewsTextServicesContextMenu::IsTextDirectionCheckedForTesting(
return false;
}
-} // namespace views \ No newline at end of file
+} // namespace views
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 e5fdea933b5..eae69906422 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
@@ -4,6 +4,7 @@
#include "ui/views/controls/views_text_services_context_menu_base.h"
+#include "base/metrics/histogram_macros.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/resources/grit/ui_resources.h"
@@ -12,6 +13,9 @@
namespace views {
+const char kViewsTextServicesContextMenuHistogram[] =
+ "ViewsTextServicesContextMenu.Used";
+
ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
ui::SimpleMenuModel* menu,
Textfield* client)
@@ -21,7 +25,7 @@ ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
// Not inserted on read-only fields or if the OS/version doesn't support it.
if (!client_->read_only() && ui::IsEmojiPanelSupported()) {
menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
- menu->InsertItemWithStringIdAt(0, IDS_CONTENT_CONTEXT_EMOJI,
+ menu->InsertItemWithStringIdAt(0, static_cast<int>(Command::kEmoji),
IDS_CONTENT_CONTEXT_EMOJI);
}
}
@@ -29,7 +33,7 @@ ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
ViewsTextServicesContextMenuBase::~ViewsTextServicesContextMenuBase() {}
bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
- return command_id == IDS_CONTENT_CONTEXT_EMOJI;
+ return command_id == static_cast<int>(Command::kEmoji);
}
bool ViewsTextServicesContextMenuBase::IsCommandIdChecked(
@@ -39,15 +43,18 @@ bool ViewsTextServicesContextMenuBase::IsCommandIdChecked(
bool ViewsTextServicesContextMenuBase::IsCommandIdEnabled(
int command_id) const {
- if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+ if (command_id == static_cast<int>(Command::kEmoji))
return true;
return false;
}
void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) {
- if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+ if (command_id == static_cast<int>(Command::kEmoji)) {
ui::ShowEmojiPanel();
+ UMA_HISTOGRAM_ENUMERATION(kViewsTextServicesContextMenuHistogram,
+ Command::kEmoji);
+ }
}
-} // namespace views \ No newline at end of file
+} // namespace views
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 7c39439b83a..08e5a7ad0c0 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
@@ -30,6 +30,9 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu {
Textfield* client() const { return client_; }
private:
+ // Do not change the values in this enum as they are used by UMA.
+ enum class Command { kEmoji = 0, kMaxValue = kEmoji };
+
// The view associated with the menu. Weak. Owns |this|.
Textfield* client_ = nullptr;
diff --git a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
index 5fb08006306..ece41dd5f53 100644
--- a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
+++ b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm
@@ -13,8 +13,8 @@ namespace views {
void UnhandledKeyboardEventHandler::HandleNativeKeyboardEvent(
gfx::NativeEvent event,
FocusManager* focus_manager) {
- [base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>([event window])
- redispatchKeyEvent:event];
+ [[base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>([event window])
+ commandDispatcher] redispatchKeyEvent:event];
}
} // namespace views
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc
index 323d9f895f8..90a9612cf61 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.cc
+++ b/chromium/ui/views/controls/webview/web_dialog_view.cc
@@ -271,10 +271,11 @@ bool WebDialogView::HandleContextMenu(
////////////////////////////////////////////////////////////////////////////////
// content::WebContentsDelegate implementation:
-void WebDialogView::MoveContents(WebContents* source, const gfx::Rect& pos) {
+void WebDialogView::SetContentsBounds(WebContents* source,
+ const gfx::Rect& bounds) {
// The contained web page wishes to resize itself. We let it do this because
// if it's a dialog we know about, we trust it not to be mean to the user.
- GetWidget()->SetBounds(pos);
+ GetWidget()->SetBounds(bounds);
}
// A simplified version of BrowserView::HandleKeyboardEvent().
diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h
index bfba06181e7..0fd7bc03288 100644
--- a/chromium/ui/views/controls/webview/web_dialog_view.h
+++ b/chromium/ui/views/controls/webview/web_dialog_view.h
@@ -94,8 +94,8 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView,
bool HandleContextMenu(const content::ContextMenuParams& params) override;
// Overridden from content::WebContentsDelegate:
- void MoveContents(content::WebContents* source,
- const gfx::Rect& pos) override;
+ void SetContentsBounds(content::WebContents* source,
+ const gfx::Rect& bounds) override;
void HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;
diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc
index 161185af3b8..74ddbc9b3a4 100644
--- a/chromium/ui/views/controls/webview/webview.cc
+++ b/chromium/ui/views/controls/webview/webview.cc
@@ -91,6 +91,8 @@ void WebView::SetWebContents(content::WebContents* replacement) {
}
AttachWebContents();
NotifyAccessibilityWebContentsChanged();
+
+ MaybeEnableAutoResize();
}
void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
@@ -109,6 +111,14 @@ void WebView::SetFastResize(bool fast_resize) {
holder_->set_fast_resize(fast_resize);
}
+void WebView::EnableSizingFromWebContents(const gfx::Size& min_size,
+ const gfx::Size& max_size) {
+ DCHECK(!max_size.IsEmpty());
+ min_size_ = min_size;
+ max_size_ = max_size;
+ MaybeEnableAutoResize();
+}
+
void WebView::SetResizeBackgroundColor(SkColor resize_background_color) {
holder_->set_resize_background_color(resize_background_color);
}
@@ -271,6 +281,10 @@ bool WebView::EmbedsFullscreenWidget() const {
////////////////////////////////////////////////////////////////////////////////
// WebView, content::WebContentsObserver implementation:
+void WebView::RenderViewCreated(content::RenderViewHost* render_view_host) {
+ MaybeEnableAutoResize();
+}
+
void WebView::RenderViewReady() {
UpdateCrashedOverlayView();
NotifyAccessibilityWebContentsChanged();
@@ -283,6 +297,8 @@ void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
content::RenderViewHost* new_host) {
+ MaybeEnableAutoResize();
+
if (HasFocus())
OnFocus();
NotifyAccessibilityWebContentsChanged();
@@ -326,6 +342,14 @@ void WebView::RenderProcessGone(base::TerminationStatus status) {
NotifyAccessibilityWebContentsChanged();
}
+void WebView::ResizeDueToAutoResize(content::WebContents* source,
+ const gfx::Size& new_size) {
+ if (source != web_contents())
+ return;
+
+ SetPreferredSize(new_size);
+}
+
////////////////////////////////////////////////////////////////////////////////
// WebView, private:
@@ -377,14 +401,7 @@ void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
}
void WebView::UpdateCrashedOverlayView() {
- // TODO(dmazzoni): Fix WebContents::IsCrashed() so we can call that
- // instead of checking termination status codes.
- if (web_contents() &&
- web_contents()->GetCrashedStatus() !=
- base::TERMINATION_STATUS_NORMAL_TERMINATION &&
- web_contents()->GetCrashedStatus() !=
- base::TERMINATION_STATUS_STILL_RUNNING &&
- crashed_overlay_view_) {
+ if (web_contents() && web_contents()->IsCrashed() && crashed_overlay_view_) {
SetFocusBehavior(FocusBehavior::NEVER);
crashed_overlay_view_->SetVisible(true);
return;
@@ -418,4 +435,15 @@ std::unique_ptr<content::WebContents> WebView::CreateWebContents(
return contents;
}
+void WebView::MaybeEnableAutoResize() {
+ if (max_size_.IsEmpty() || !web_contents() ||
+ !web_contents()->GetRenderWidgetHostView()) {
+ return;
+ }
+
+ content::RenderWidgetHostView* render_widget_host_view =
+ web_contents()->GetRenderWidgetHostView();
+ render_widget_host_view->EnableAutoResize(min_size_, max_size_);
+}
+
} // namespace views
diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h
index 87d9a1e7259..994520b83b1 100644
--- a/chromium/ui/views/controls/webview/webview.h
+++ b/chromium/ui/views/controls/webview/webview.h
@@ -76,6 +76,11 @@ class WEBVIEW_EXPORT WebView : public View,
// resizing performance during interactive resizes and animations.
void SetFastResize(bool fast_resize);
+ // If enabled, this will make the WebView's preferred size dependent on the
+ // WebContents' size.
+ void EnableSizingFromWebContents(const gfx::Size& min_size,
+ const gfx::Size& max_size);
+
// Set the background color to use while resizing with a clip. This is white
// by default.
void SetResizeBackgroundColor(SkColor resize_background_color);
@@ -94,6 +99,10 @@ class WEBVIEW_EXPORT WebView : public View,
// Overridden from View:
const char* GetClassName() const override;
+ // Overridden from content::WebContentsDelegate:
+ void ResizeDueToAutoResize(content::WebContents* source,
+ const gfx::Size& new_size) override;
+
NativeViewHost* holder() { return holder_; }
using WebContentsCreator =
base::RepeatingCallback<std::unique_ptr<content::WebContents>(
@@ -123,6 +132,9 @@ class WEBVIEW_EXPORT WebView : public View,
virtual void OnLetterboxingChanged() {}
bool is_letterboxing() const { return is_letterboxing_; }
+ const gfx::Size& min_size() const { return min_size_; }
+ const gfx::Size& max_size() const { return max_size_; }
+
// Overridden from View:
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
void ViewHierarchyChanged(
@@ -138,6 +150,7 @@ class WEBVIEW_EXPORT WebView : public View,
bool EmbedsFullscreenWidget() const override;
// Overridden from content::WebContentsObserver:
+ void RenderViewCreated(content::RenderViewHost* render_view_host) override;
void RenderViewReady() override;
void RenderViewDeleted(content::RenderViewHost* render_view_host) override;
void RenderViewHostChanged(content::RenderViewHost* old_host,
@@ -167,6 +180,11 @@ class WEBVIEW_EXPORT WebView : public View,
void UpdateCrashedOverlayView();
void NotifyAccessibilityWebContentsChanged();
+ // Registers for ResizeDueToAutoResize() notifications from the
+ // RenderWidgetHostView whenever it is created or changes, if
+ // EnableSizingFromWebContents() has been called.
+ void MaybeEnableAutoResize();
+
// Create a regular or test web contents (based on whether we're running
// in a unit test or not).
std::unique_ptr<content::WebContents> CreateWebContents(
@@ -188,6 +206,11 @@ class WEBVIEW_EXPORT WebView : public View,
bool allow_accelerators_;
View* crashed_overlay_view_ = nullptr;
+ // Minimum and maximum sizes to determine WebView bounds for auto-resizing.
+ // Empty if auto resize is not enabled.
+ gfx::Size min_size_;
+ gfx::Size max_size_;
+
DISALLOW_COPY_AND_ASSIGN(WebView);
};
diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn
index 661329c2482..c757e20b7ff 100644
--- a/chromium/ui/views/examples/BUILD.gn
+++ b/chromium/ui/views/examples/BUILD.gn
@@ -111,9 +111,9 @@ executable("views_examples_exe") {
"//base",
"//base:i18n",
"//base/test:test_support",
- "//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest",
"//components/viz/host",
+ "//components/viz/service",
"//ui/base",
"//ui/compositor",
"//ui/compositor:test_support",
@@ -172,7 +172,6 @@ executable("views_examples_with_content_exe") {
":copy_content_resources",
":views_examples_with_content_lib",
"//base",
- "//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest",
"//content",
"//content:sandbox_helper_win",
diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS
index c55d1e8778f..38ff08c5007 100644
--- a/chromium/ui/views/examples/DEPS
+++ b/chromium/ui/views/examples/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/viz/host",
+ "+components/viz/service", # In-process viz service.
"+content/public",
"+content/shell",
"+sandbox",
diff --git a/chromium/ui/views/examples/box_layout_example.cc b/chromium/ui/views/examples/box_layout_example.cc
index 2c4fba15fb2..a17fd295dee 100644
--- a/chromium/ui/views/examples/box_layout_example.cc
+++ b/chromium/ui/views/examples/box_layout_example.cc
@@ -331,10 +331,10 @@ void BoxLayoutExample::CreateExampleView(View* container) {
border_insets_[i] =
CreateRawTextfield(horizontal_pos, border_insets_[0]->y(), true);
- collapse_margins_ = new Checkbox(base::ASCIIToUTF16("Collapse margins"));
+ collapse_margins_ =
+ new Checkbox(base::ASCIIToUTF16("Collapse margins"), this);
collapse_margins_->SetPosition(gfx::Point(kPadding, vertical_pos));
collapse_margins_->SizeToPreferredSize();
- collapse_margins_->set_listener(this);
control_panel_->AddChildView(collapse_margins_);
UpdateLayoutManager();
diff --git a/chromium/ui/views/examples/checkbox_example.cc b/chromium/ui/views/examples/checkbox_example.cc
index b28a6b66526..c5a5dc49f94 100644
--- a/chromium/ui/views/examples/checkbox_example.cc
+++ b/chromium/ui/views/examples/checkbox_example.cc
@@ -20,8 +20,7 @@ CheckboxExample::~CheckboxExample() {
}
void CheckboxExample::CreateExampleView(View* container) {
- button_ = new Checkbox(base::ASCIIToUTF16("Checkbox"));
- button_->set_listener(this);
+ button_ = new Checkbox(base::ASCIIToUTF16("Checkbox"), this);
container->SetLayoutManager(std::make_unique<FillLayout>());
container->AddChildView(button_);
}
diff --git a/chromium/ui/views/examples/dialog_example.cc b/chromium/ui/views/examples/dialog_example.cc
index 48426144dfa..e10f2fadd5b 100644
--- a/chromium/ui/views/examples/dialog_example.cc
+++ b/chromium/ui/views/examples/dialog_example.cc
@@ -205,8 +205,7 @@ void DialogExample::StartTextfieldRow(GridLayout* layout,
}
void DialogExample::AddCheckbox(GridLayout* layout, Checkbox** member) {
- Checkbox* checkbox = new Checkbox(base::string16());
- checkbox->set_listener(this);
+ Checkbox* checkbox = new Checkbox(base::string16(), this);
checkbox->SetChecked(true);
layout->AddView(checkbox);
*member = checkbox;
diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc
index 9ddb537f8c5..4dd22a71197 100644
--- a/chromium/ui/views/examples/examples_main.cc
+++ b/chromium/ui/views/examples/examples_main.cc
@@ -17,6 +17,8 @@
#include "base/test/test_discardable_memory_allocator.h"
#include "build/build_config.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"
#include "ui/base/ime/input_method_initializer.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
@@ -68,7 +70,8 @@ int main(int argc, char** argv) {
// The ContextFactory must exist before any Compositors are created.
viz::HostFrameSinkManager host_frame_sink_manager;
- viz::FrameSinkManagerImpl frame_sink_manager;
+ viz::ServerSharedBitmapManager shared_bitmap_manager;
+ viz::FrameSinkManagerImpl frame_sink_manager(&shared_bitmap_manager);
host_frame_sink_manager.SetLocalManager(&frame_sink_manager);
frame_sink_manager.SetLocalClient(&host_frame_sink_manager);
auto context_factory = std::make_unique<ui::InProcessContextFactory>(
diff --git a/chromium/ui/views/examples/label_example.cc b/chromium/ui/views/examples/label_example.cc
index 4af92326d72..bdf6644cc79 100644
--- a/chromium/ui/views/examples/label_example.cc
+++ b/chromium/ui/views/examples/label_example.cc
@@ -200,14 +200,11 @@ void LabelExample::AddCustomLabel(View* container) {
column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
GridLayout::USE_PREF, 0, 0);
layout->StartRow(0, 1);
- multiline_ = new Checkbox(base::ASCIIToUTF16("Multiline"));
- multiline_->set_listener(this);
+ multiline_ = new Checkbox(base::ASCIIToUTF16("Multiline"), this);
layout->AddView(multiline_);
- shadows_ = new Checkbox(base::ASCIIToUTF16("Shadows"));
- shadows_->set_listener(this);
+ shadows_ = new Checkbox(base::ASCIIToUTF16("Shadows"), this);
layout->AddView(shadows_);
- selectable_ = new Checkbox(base::ASCIIToUTF16("Selectable"));
- selectable_->set_listener(this);
+ selectable_ = new Checkbox(base::ASCIIToUTF16("Selectable"), this);
layout->AddView(selectable_);
layout->AddPaddingRow(0, 8);
diff --git a/chromium/ui/views/examples/multiline_example.cc b/chromium/ui/views/examples/multiline_example.cc
index 4e5225bd99a..7ecd6834d67 100644
--- a/chromium/ui/views/examples/multiline_example.cc
+++ b/chromium/ui/views/examples/multiline_example.cc
@@ -145,14 +145,12 @@ void MultilineExample::CreateExampleView(View* container) {
label_->SetMultiLine(true);
label_->SetBorder(CreateSolidBorder(2, SK_ColorCYAN));
- label_checkbox_ = new Checkbox(ASCIIToUTF16("views::Label:"));
+ label_checkbox_ = new Checkbox(ASCIIToUTF16("views::Label:"), this);
label_checkbox_->SetChecked(true);
- label_checkbox_->set_listener(this);
label_checkbox_->set_request_focus_on_press(false);
- elision_checkbox_ = new Checkbox(ASCIIToUTF16("elide text?"));
+ elision_checkbox_ = new Checkbox(ASCIIToUTF16("elide text?"), this);
elision_checkbox_->SetChecked(false);
- elision_checkbox_->set_listener(this);
elision_checkbox_->set_request_focus_on_press(false);
textfield_ = new Textfield();
diff --git a/chromium/ui/views/examples/radio_button_example.cc b/chromium/ui/views/examples/radio_button_example.cc
index 4f6e05b475d..65826da1509 100644
--- a/chromium/ui/views/examples/radio_button_example.cc
+++ b/chromium/ui/views/examples/radio_button_example.cc
@@ -17,10 +17,7 @@
namespace views {
namespace examples {
-RadioButtonExample::RadioButtonExample()
- : ExampleBase("Radio Button"),
- count_(0) {
-}
+RadioButtonExample::RadioButtonExample() : ExampleBase("Radio Button") {}
RadioButtonExample::~RadioButtonExample() {
}
@@ -35,7 +32,6 @@ void RadioButtonExample::CreateExampleView(View* container) {
base::UTF8ToUTF16(base::StringPrintf(
"Radio %d in group %d", static_cast<int>(i) + 1, group)),
group);
- radio_buttons_[i]->set_listener(this);
}
GridLayout* layout = container->SetLayoutManager(
@@ -63,8 +59,6 @@ void RadioButtonExample::ButtonPressed(Button* sender, const ui::Event& event) {
BoolToOnOff(radio_buttons_[0]->checked()),
BoolToOnOff(radio_buttons_[1]->checked()),
BoolToOnOff(radio_buttons_[2]->checked()));
- } else {
- PrintStatus("Pressed! count:%d", ++count_);
}
}
diff --git a/chromium/ui/views/examples/radio_button_example.h b/chromium/ui/views/examples/radio_button_example.h
index 7dfd81c87ef..0cb2edf0934 100644
--- a/chromium/ui/views/examples/radio_button_example.h
+++ b/chromium/ui/views/examples/radio_button_example.h
@@ -38,9 +38,6 @@ class VIEWS_EXAMPLES_EXPORT RadioButtonExample : public ExampleBase,
LabelButton* select_;
LabelButton* status_;
- // The number of times the button is pressed.
- int count_;
-
DISALLOW_COPY_AND_ASSIGN(RadioButtonExample);
};
diff --git a/chromium/ui/views/examples/table_example.cc b/chromium/ui/views/examples/table_example.cc
index 1fd544f7d78..68b17a21d86 100644
--- a/chromium/ui/views/examples/table_example.cc
+++ b/chromium/ui/views/examples/table_example.cc
@@ -41,22 +41,18 @@ TableExample::~TableExample() {
}
void TableExample::CreateExampleView(View* container) {
- column1_visible_checkbox_ = new Checkbox(
- ASCIIToUTF16("Fruit column visible"));
+ column1_visible_checkbox_ =
+ new Checkbox(ASCIIToUTF16("Fruit column visible"), this);
column1_visible_checkbox_->SetChecked(true);
- column1_visible_checkbox_->set_listener(this);
- column2_visible_checkbox_ = new Checkbox(
- ASCIIToUTF16("Color column visible"));
+ column2_visible_checkbox_ =
+ new Checkbox(ASCIIToUTF16("Color column visible"), this);
column2_visible_checkbox_->SetChecked(true);
- column2_visible_checkbox_->set_listener(this);
- column3_visible_checkbox_ = new Checkbox(
- ASCIIToUTF16("Origin column visible"));
+ column3_visible_checkbox_ =
+ new Checkbox(ASCIIToUTF16("Origin column visible"), this);
column3_visible_checkbox_->SetChecked(true);
- column3_visible_checkbox_->set_listener(this);
- column4_visible_checkbox_ = new Checkbox(
- ASCIIToUTF16("Price column visible"));
+ column4_visible_checkbox_ =
+ new Checkbox(ASCIIToUTF16("Price column visible"), this);
column4_visible_checkbox_->SetChecked(true);
- column4_visible_checkbox_->set_listener(this);
GridLayout* layout = container->SetLayoutManager(
std::make_unique<views::GridLayout>(container));
diff --git a/chromium/ui/views/examples/text_example.cc b/chromium/ui/views/examples/text_example.cc
index 17439cc8bc6..11db9591b83 100644
--- a/chromium/ui/views/examples/text_example.cc
+++ b/chromium/ui/views/examples/text_example.cc
@@ -135,8 +135,7 @@ TextExample::~TextExample() {
}
Checkbox* TextExample::AddCheckbox(GridLayout* layout, const char* name) {
- Checkbox* checkbox = new Checkbox(base::ASCIIToUTF16(name));
- checkbox->set_listener(this);
+ Checkbox* checkbox = new Checkbox(base::ASCIIToUTF16(name), this);
layout->AddView(checkbox);
return checkbox;
}
diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc
index 6847bae9d64..faded394493 100644
--- a/chromium/ui/views/focus/focus_manager.cc
+++ b/chromium/ui/views/focus/focus_manager.cc
@@ -133,6 +133,13 @@ void FocusManager::AdvanceFocus(bool reverse) {
DCHECK(v->GetWidget());
v->GetWidget()->GetFocusManager()->SetFocusedViewWithReason(
v, kReasonFocusTraversal);
+
+ // When moving focus from a child widget to a top-level widget,
+ // the top-level widget may report IsActive()==true because it's
+ // active even though it isn't focused. Explicitly activate the
+ // widget to ensure that case is handled.
+ if (v->GetWidget()->GetFocusManager() != this)
+ v->GetWidget()->Activate();
}
}
@@ -296,7 +303,8 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view,
// Easy, just clear the selection and press tab again.
// By calling with nullptr as the starting view, we'll start from either
// the starting views widget or |widget_|.
- Widget* widget = original_starting_view->GetWidget();
+ Widget* widget = starting_view ? starting_view->GetWidget()
+ : original_starting_view->GetWidget();
if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget())
widget = widget_;
return GetNextFocusableView(nullptr, widget, reverse, true);
diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc
index 3c1278fe2f3..5e7c7a6778e 100644
--- a/chromium/ui/views/focus/focus_manager_unittest.cc
+++ b/chromium/ui/views/focus/focus_manager_unittest.cc
@@ -14,6 +14,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/test_accelerator_target.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/bubble/bubble_dialog_delegate.h"
@@ -21,10 +22,17 @@
#include "ui/views/focus/focus_manager_factory.h"
#include "ui/views/focus/widget_focus_manager.h"
#include "ui/views/test/focus_manager_test.h"
+#include "ui/views/test/native_widget_factory.h"
+#include "ui/views/test/test_platform_native_widget.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_properties.h"
#include "ui/views/widget/widget.h"
+#if defined(USE_AURA)
+#include "ui/aura/client/focus_client.h"
+#include "ui/views/widget/native_widget_aura.h"
+#endif // USE_AURA
+
namespace views {
enum FocusTestEventType { ON_FOCUS = 0, ON_BLUR };
@@ -163,44 +171,13 @@ TEST_F(FocusManagerTest, WidgetFocusChangeListener) {
EXPECT_EQ(native_view2, widget_listener.focus_changes()[1]);
}
-// Counts accelerator calls.
-class TestAcceleratorTarget : public ui::AcceleratorTarget {
- public:
- explicit TestAcceleratorTarget(bool process_accelerator)
- : accelerator_count_(0),
- process_accelerator_(process_accelerator),
- can_handle_accelerators_(true) {}
-
- bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
- ++accelerator_count_;
- return process_accelerator_;
- }
-
- bool CanHandleAccelerators() const override {
- return can_handle_accelerators_;
- }
-
- int accelerator_count() const { return accelerator_count_; }
-
- void set_can_handle_accelerators(bool can_handle_accelerators) {
- can_handle_accelerators_ = can_handle_accelerators;
- }
-
- private:
- int accelerator_count_; // number of times that the accelerator is activated
- bool process_accelerator_; // return value of AcceleratorPressed
- bool can_handle_accelerators_; // return value of CanHandleAccelerators
-
- DISALLOW_COPY_AND_ASSIGN(TestAcceleratorTarget);
-};
-
TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
- TestAcceleratorTarget return_target(true);
- TestAcceleratorTarget escape_target(true);
+ ui::TestAcceleratorTarget return_target(true);
+ ui::TestAcceleratorTarget escape_target(true);
EXPECT_EQ(return_target.accelerator_count(), 0);
EXPECT_EQ(escape_target.accelerator_count(), 0);
@@ -223,7 +200,7 @@ TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {
EXPECT_EQ(escape_target.accelerator_count(), 1);
// Register another target for the return key.
- TestAcceleratorTarget return_target2(true);
+ ui::TestAcceleratorTarget return_target2(true);
EXPECT_EQ(return_target2.accelerator_count(), 0);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
@@ -235,7 +212,7 @@ TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {
EXPECT_EQ(return_target2.accelerator_count(), 1);
// Register a target that does not process the accelerator event.
- TestAcceleratorTarget return_target3(false);
+ ui::TestAcceleratorTarget return_target3(false);
EXPECT_EQ(return_target3.accelerator_count(), 0);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
@@ -275,8 +252,8 @@ TEST_F(FocusManagerTest, HighPriorityHandlers) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
- TestAcceleratorTarget escape_target_high(true);
- TestAcceleratorTarget escape_target_normal(true);
+ ui::TestAcceleratorTarget escape_target_high(true);
+ ui::TestAcceleratorTarget escape_target_normal(true);
EXPECT_EQ(escape_target_high.accelerator_count(), 0);
EXPECT_EQ(escape_target_normal.accelerator_count(), 0);
EXPECT_FALSE(focus_manager->HasPriorityHandler(escape_accelerator));
@@ -350,8 +327,8 @@ TEST_F(FocusManagerTest, CallsEnabledAcceleratorTargetsOnly) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
- TestAcceleratorTarget return_target1(true);
- TestAcceleratorTarget return_target2(true);
+ ui::TestAcceleratorTarget return_target1(true);
+ ui::TestAcceleratorTarget return_target2(true);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
@@ -385,28 +362,21 @@ TEST_F(FocusManagerTest, CallsEnabledAcceleratorTargetsOnly) {
}
// Unregisters itself when its accelerator is invoked.
-class SelfUnregisteringAcceleratorTarget : public ui::AcceleratorTarget {
+class SelfUnregisteringAcceleratorTarget : public ui::TestAcceleratorTarget {
public:
- SelfUnregisteringAcceleratorTarget(ui::Accelerator accelerator,
+ SelfUnregisteringAcceleratorTarget(const ui::Accelerator& accelerator,
FocusManager* focus_manager)
- : accelerator_(accelerator),
- focus_manager_(focus_manager),
- accelerator_count_(0) {}
+ : accelerator_(accelerator), focus_manager_(focus_manager) {}
+ // ui::TestAcceleratorTarget:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
- ++accelerator_count_;
focus_manager_->UnregisterAccelerator(accelerator, this);
- return true;
+ return ui::TestAcceleratorTarget::AcceleratorPressed(accelerator);
}
- bool CanHandleAccelerators() const override { return true; }
-
- int accelerator_count() const { return accelerator_count_; }
-
private:
ui::Accelerator accelerator_;
FocusManager* focus_manager_;
- int accelerator_count_;
DISALLOW_COPY_AND_ASSIGN(SelfUnregisteringAcceleratorTarget);
};
@@ -433,7 +403,7 @@ TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) {
TEST_F(FocusManagerTest, SuspendAccelerators) {
const ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE);
ui::Accelerator accelerator(event.key_code(), event.flags());
- TestAcceleratorTarget target(true);
+ ui::TestAcceleratorTarget target(true);
FocusManager* focus_manager = GetFocusManager();
focus_manager->RegisterAccelerator(
accelerator, ui::AcceleratorManager::kNormalPriority, &target);
@@ -869,10 +839,28 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
: BubbleDialogDelegateView(anchor, BubbleBorder::NONE) {}
~TestBubbleDialogDelegateView() override {}
+ // If this is called, the bubble will be forced to use a NativeWidgetAura.
+ // If not set, it might get a DesktopNativeWidgetAura depending on the
+ // platform and other factors.
+ void UseNativeWidgetAura() { use_native_widget_aura_ = true; }
+
// ui::DialogModel override.
int GetDialogButtons() const override { return 0; }
+ void OnBeforeBubbleWidgetInit(Widget::InitParams* params,
+ Widget* widget) const override {
+#if defined(USE_AURA)
+ if (use_native_widget_aura_) {
+ params->native_widget =
+ new test::TestPlatformNativeWidget<NativeWidgetAura>(widget, false,
+ nullptr);
+ }
+#endif // USE_AURA
+ }
+
private:
+ bool use_native_widget_aura_ = false;
+
DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
};
@@ -1047,4 +1035,71 @@ TEST_F(FocusManagerTest, AnchoredDialogOnContainerView) {
EXPECT_TRUE(parent3->HasFocus());
}
+// Desktop native widget Aura tests are for non Chrome OS platforms.
+// This test is specifically for the permutation where the main
+// widget is a DesktopNativeWidgetAura and the bubble is a
+// NativeWidgetAura. When focus moves back from the bubble to the
+// parent widget, ensure that the DNWA's aura window is focused.
+#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+TEST_F(FocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) {
+ Widget widget;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = gfx::Rect(0, 0, 1024, 768);
+ params.native_widget =
+ test::CreatePlatformDesktopNativeWidgetImpl(params, &widget, nullptr);
+ widget.Init(params);
+ widget.Show();
+ widget.Activate();
+
+ View* parent1 = new View();
+ View* parent2 = new View();
+
+ parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+
+ widget.GetRootView()->AddChildView(parent1);
+ widget.GetRootView()->AddChildView(parent2);
+
+ TestBubbleDialogDelegateView* bubble_delegate =
+ new TestBubbleDialogDelegateView(parent2);
+ bubble_delegate->UseNativeWidgetAura();
+ test::WidgetTest::WidgetAutoclosePtr bubble_widget(
+ BubbleDialogDelegateView::CreateBubble(bubble_delegate));
+ bubble_delegate->EnableFocusTraversalFromAnchorView();
+ View* child = new View();
+ child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+ bubble_widget->GetRootView()->AddChildView(child);
+ bubble_delegate->set_close_on_deactivate(false);
+ bubble_widget->Show();
+
+ widget.Activate();
+ parent1->RequestFocus();
+ base::RunLoop().RunUntilIdle();
+
+ // Initially the outer widget's window is focused.
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(widget.GetNativeView());
+ ASSERT_EQ(widget.GetNativeView(), focus_client->GetFocusedWindow());
+
+ // Navigate forwards
+ widget.GetFocusManager()->AdvanceFocus(false);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(parent2->HasFocus());
+ widget.GetFocusManager()->AdvanceFocus(false);
+ EXPECT_TRUE(child->HasFocus());
+
+ // Now the bubble widget's window is focused.
+ ASSERT_NE(widget.GetNativeView(), focus_client->GetFocusedWindow());
+ ASSERT_EQ(bubble_widget->GetNativeView(), focus_client->GetFocusedWindow());
+
+ // Navigate backwards
+ bubble_widget->GetFocusManager()->AdvanceFocus(true);
+ EXPECT_TRUE(parent2->HasFocus());
+
+ // Finally, the outer widget's window should be focused again.
+ ASSERT_EQ(widget.GetNativeView(), focus_client->GetFocusedWindow());
+}
+#endif // defined(USE_AURA) && !defined(OS_CHROMEOS)
+
} // namespace views
diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h
index 4721adf215f..cd046c70163 100644
--- a/chromium/ui/views/layout/grid_layout.h
+++ b/chromium/ui/views/layout/grid_layout.h
@@ -24,17 +24,17 @@
// columns->AddColumn(FILL, // Views are horizontally resized to fill column.
// FILL, // Views starting in this column are vertically
// // resized.
-// 1, // This column has a resize weight of 1.
+// 1.0, // This column has a resize weight of 1.
// USE_PREF, // Use the preferred size of the view.
// 0, // Ignored for USE_PREF.
// 0); // A minimum width of 0.
-// columns->AddPaddingColumn(0, // The padding column is not resizable.
-// 10); // And has a width of 10 pixels.
-// columns->AddColumn(FILL, FILL, 0, USE_PREF, 0, 0);
+// columns->AddPaddingColumn(kFixedSize, // The padding column is not resizable.
+// 10); // And has a width of 10 pixels.
+// columns->AddColumn(FILL, FILL, kFixedSize, USE_PREF, 0, 0);
// Now add the views:
// // First start a row.
-// layout->StartRow(0, // This row isn't vertically resizable.
-// 0); // The column set to use for this row.
+// layout->StartRow(kFixedSize, // This row isn't vertically resizable.
+// 0); // The column set to use for this row.
// layout->AddView(v1);
// Notice you need not skip over padding columns, that's done for you.
// layout->AddView(v2);
@@ -77,6 +77,10 @@ struct ViewState;
class VIEWS_EXPORT GridLayout : public LayoutManager {
public:
+ // Use for |resize_percent| or |vertical_resize| when the column or row is not
+ // resizable.
+ static constexpr float kFixedSize = 0.f;
+
// An enumeration of the possible alignments supported by GridLayout.
enum Alignment {
// Leading equates to left along the horizontal axis, and top along the
diff --git a/chromium/ui/views/layout/layout_provider.cc b/chromium/ui/views/layout/layout_provider.cc
index 822088d3723..caba8a5228e 100644
--- a/chromium/ui/views/layout/layout_provider.cc
+++ b/chromium/ui/views/layout/layout_provider.cc
@@ -146,22 +146,28 @@ int LayoutProvider::GetCornerRadiusMetric(EmphasisMetric emphasis_metric,
const bool is_touch =
ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
switch (emphasis_metric) {
+ case views::EMPHASIS_NONE:
+ NOTREACHED();
+ return 0;
case EMPHASIS_LOW:
- return is_touch ? 4 : 2;
case EMPHASIS_MEDIUM:
- return is_touch ? 8 : 4;
+ return is_touch ? 4 : 2;
case EMPHASIS_HIGH:
+ return is_touch ? 8 : 4;
+ case EMPHASIS_MAXIMUM:
return is_touch ? std::min(size.width(), size.height()) / 2 : 4;
- default:
- NOTREACHED();
- return 0;
}
}
int LayoutProvider::GetShadowElevationMetric(
EmphasisMetric emphasis_metric) const {
- // Just return a value for now.
- return 2;
+ // Return a value similar to the (deprecated) default shadow style for bubbles
+ // and dialogs.
+ return 3;
+}
+
+gfx::ShadowValues LayoutProvider::MakeShadowValues(int elevation) const {
+ return gfx::ShadowValue::MakeMdShadowValues(elevation);
}
} // namespace views
diff --git a/chromium/ui/views/layout/layout_provider.h b/chromium/ui/views/layout/layout_provider.h
index c61276edd2b..cf92ab718c9 100644
--- a/chromium/ui/views/layout/layout_provider.h
+++ b/chromium/ui/views/layout/layout_provider.h
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/shadow_value.h"
#include "ui/views/style/typography_provider.h"
#include "ui/views/views_export.h"
@@ -117,10 +118,12 @@ enum EmphasisMetric {
// Use this to indicate low-emphasis interactive elements such as buttons and
// text fields.
EMPHASIS_LOW,
- // Use this for components with medium emphasis, such as tabs or dialogs.
+ // Use this for components with medium emphasis, such the autofill dropdown.
EMPHASIS_MEDIUM,
- // High-emphasis components like the omnibox or rich suggestions.
+ // High-emphasis components, such as tabs or dialogs.
EMPHASIS_HIGH,
+ // Maximum emphasis components like the omnibox or rich suggestions.
+ EMPHASIS_MAXIMUM,
};
class VIEWS_EXPORT LayoutProvider {
@@ -170,6 +173,10 @@ class VIEWS_EXPORT LayoutProvider {
// Returns the shadow elevation metric for the given emphasis.
virtual int GetShadowElevationMetric(EmphasisMetric emphasis_metric) const;
+ // Creates shadows for the given elevation. Use GetShadowElevationMetric for
+ // the appropriate elevation.
+ virtual gfx::ShadowValues MakeShadowValues(int elevation) const;
+
private:
DefaultTypographyProvider typography_provider_;
diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h
index 07f30dfe53a..c1f14d5c65a 100644
--- a/chromium/ui/views/linux_ui/linux_ui.h
+++ b/chromium/ui/views/linux_ui/linux_ui.h
@@ -22,6 +22,8 @@
// The main entrypoint into Linux toolkit specific code. GTK code should only
// be executed behind this interface.
+class PrefService;
+
namespace aura {
class Window;
}
@@ -101,7 +103,9 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
virtual void Initialize() = 0;
virtual bool GetTint(int id, color_utils::HSL* tint) const = 0;
- virtual bool GetColor(int id, SkColor* color) const = 0;
+ virtual bool GetColor(int id,
+ SkColor* color,
+ PrefService* pref_service) const = 0;
// Returns the preferences that we pass to WebKit.
virtual SkColor GetFocusRingColor() const = 0;
@@ -165,9 +169,9 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
NonClientWindowFrameActionSourceType source) = 0;
// Notifies the window manager that start up has completed.
- // Normally Chromium opens a new window on startup and GTK does this
- // automatically. In case Chromium does not open a new window on startup,
- // e.g. an existing browser window already exists, this should be called.
+ // This needs to be called explicitly both on the primary and the "remote"
+ // instances (e.g. an existing browser window already exists), since we no
+ // longer use GTK (which did this automatically) for the main windows.
virtual void NotifyWindowManagerStartupComplete() = 0;
// Updates the device scale factor so that the default font size can be
diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn
index ed64ffdeced..0c9092d362b 100644
--- a/chromium/ui/views/mus/BUILD.gn
+++ b/chromium/ui/views/mus/BUILD.gn
@@ -17,8 +17,10 @@ jumbo_component("mus") {
sources = [
"aura_init.cc",
"aura_init.h",
- "clipboard_mus.cc",
- "clipboard_mus.h",
+ "ax_remote_host.cc",
+ "ax_remote_host.h",
+ "ax_tree_source_mus.cc",
+ "ax_tree_source_mus.h",
"desktop_window_tree_host_mus.cc",
"desktop_window_tree_host_mus.h",
"mus_client.cc",
@@ -26,6 +28,8 @@ jumbo_component("mus") {
"mus_client_observer.h",
"mus_export.h",
"mus_property_mirror.h",
+ "mus_views_delegate.cc",
+ "mus_views_delegate.h",
"pointer_watcher_event_router.cc",
"pointer_watcher_event_router.h",
"screen_mus.cc",
@@ -60,7 +64,10 @@ jumbo_component("mus") {
"//services/ui/public/interfaces",
"//skia",
"//third_party/icu",
+ "//ui/accessibility",
+ "//ui/accessibility/mojom",
"//ui/aura",
+ "//ui/base/mojo:lib",
"//ui/compositor",
"//ui/display",
"//ui/events",
@@ -78,13 +85,6 @@ jumbo_component("mus") {
"//ui/wm",
"//ui/wm/public",
]
-
- if (is_linux && !is_android) {
- deps += [ "//components/services/font/public/cpp" ]
- data_deps = [
- "//components/services/font:font_service",
- ]
- }
}
repack("resources") {
@@ -113,6 +113,7 @@ jumbo_static_library("test_support") {
sources = [
"../test/native_widget_factory_aura_mus.cc",
+ "mus_client_test_api.h",
"views_mus_test_suite.cc",
"views_mus_test_suite.h",
]
@@ -121,7 +122,7 @@ jumbo_static_library("test_support") {
":mus",
"//base",
"//base/test:test_support",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//services/catalog:lib",
"//services/service_manager/background:lib",
"//services/service_manager/public/cpp",
@@ -152,6 +153,8 @@ test("views_mus_unittests") {
testonly = true
sources = [
+ "ax_remote_host_unittest.cc",
+ "ax_tree_source_mus_unittest.cc",
"desktop_window_tree_host_mus_unittest.cc",
"pointer_watcher_event_router_unittest.cc",
"run_all_unittests_mus.cc",
@@ -173,6 +176,7 @@ test("views_mus_unittests") {
"//testing/gtest",
"//third_party/icu",
"//ui/accessibility",
+ "//ui/accessibility/mojom",
"//ui/aura",
"//ui/aura:test_support",
"//ui/base",
@@ -199,7 +203,7 @@ test("views_mus_unittests") {
data_deps = [
":views_mus_tests_catalog_copy",
"//services/ui/ime/test_ime_driver",
- "//services/ui/test_wm",
+ "//services/ui/test_ws",
]
if (is_win) {
@@ -244,7 +248,7 @@ test("views_mus_interactive_ui_tests") {
":mus",
":test_support",
"//base",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//testing/gmock",
"//testing/gtest",
"//ui/aura",
@@ -252,6 +256,7 @@ test("views_mus_interactive_ui_tests") {
"//ui/base",
"//ui/base:test_support",
"//ui/base/ime",
+ "//ui/base/mojo:lib",
"//ui/events:events_base",
"//ui/events:test_support",
"//ui/gl:test_support",
@@ -264,7 +269,7 @@ test("views_mus_interactive_ui_tests") {
data_deps = [
":views_mus_tests_catalog_copy",
- "//services/ui/test_wm",
+ "//services/ui/test_ws",
]
if (is_win) {
@@ -299,9 +304,10 @@ catalog("views_mus_tests_catalog") {
":interactive_ui_tests_manifest",
]
- standalone_services = [ "//services/ui/test_wm:manifest" ]
-
- catalog_deps = [ "//mash:catalog" ]
+ standalone_services = [
+ "//services/ui/test_ws:manifest",
+ "//services/ui/ime/test_ime_driver:manifest",
+ ]
}
copy("views_mus_tests_catalog_copy") {
diff --git a/chromium/ui/views/mus/DEPS b/chromium/ui/views/mus/DEPS
index f56df79ddca..8953b55dfbd 100644
--- a/chromium/ui/views/mus/DEPS
+++ b/chromium/ui/views/mus/DEPS
@@ -1,10 +1,9 @@
include_rules = [
"+cc",
- "+components/services/font/public",
"+components/gpu",
"+mojo/cc",
"+mojo/converters",
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
"+mojo/public",
"+services/catalog",
"+services/service_manager/public",
diff --git a/chromium/ui/views/mus/OWNERS b/chromium/ui/views/mus/OWNERS
index e2906750656..3a73ee3f4a5 100644
--- a/chromium/ui/views/mus/OWNERS
+++ b/chromium/ui/views/mus/OWNERS
@@ -1,3 +1,9 @@
+jamescook@chromium.org
+msw@chromium.org
+sky@chromium.org
+
+per-file ax_*=file://ui/accessibility/OWNERS
+
per-file interactive_ui_tests_manifest.json=set noparent
per-file interactive_ui_tests_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromium/ui/views/mus/aura_init.cc b/chromium/ui/views/mus/aura_init.cc
index 96e0ecfc398..56b6c3c0a1f 100644
--- a/chromium/ui/views/mus/aura_init.cc
+++ b/chromium/ui/views/mus/aura_init.cc
@@ -6,7 +6,6 @@
#include <utility>
-#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
@@ -19,135 +18,71 @@
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
-#include "ui/views/layout/layout_provider.h"
#include "ui/views/mus/mus_client.h"
-#include "ui/views/views_delegate.h"
-
-#if defined(OS_LINUX)
-#include "components/services/font/public/cpp/font_loader.h"
-#include "ui/gfx/platform_font_linux.h"
-#endif
+#include "ui/views/mus/mus_views_delegate.h"
namespace views {
-namespace {
-
-class MusViewsDelegate : public ViewsDelegate {
- public:
- MusViewsDelegate() {}
- ~MusViewsDelegate() override {}
-
- private:
-#if defined(OS_WIN)
- HICON GetSmallWindowIcon() const override { return nullptr; }
-#endif
- void OnBeforeWidgetInit(
- Widget::InitParams* params,
- internal::NativeWidgetDelegate* delegate) override {}
-
- LayoutProvider layout_provider_;
+AuraInit::InitParams::InitParams() : resource_file("views_mus_resources.pak") {}
- DISALLOW_COPY_AND_ASSIGN(MusViewsDelegate);
-};
-
-} // namespace
+AuraInit::InitParams::~InitParams() = default;
AuraInit::AuraInit() {
if (!ViewsDelegate::GetInstance())
views_delegate_ = std::make_unique<MusViewsDelegate>();
}
-AuraInit::~AuraInit() {
-#if defined(OS_LINUX)
- if (font_loader_.get()) {
- SkFontConfigInterface::SetGlobal(nullptr);
- // FontLoader is ref counted. We need to explicitly shutdown the background
- // thread, otherwise the background thread may be shutdown after the app is
- // torn down, when we're in a bad state.
- font_loader_->Shutdown();
- }
-#endif
-}
+AuraInit::~AuraInit() = default;
-std::unique_ptr<AuraInit> AuraInit::Create(
- service_manager::Connector* connector,
- const service_manager::Identity& identity,
- const std::string& resource_file,
- const std::string& resource_file_200,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- Mode mode,
- bool register_path_provider) {
+// static
+std::unique_ptr<AuraInit> AuraInit::Create(const InitParams& params) {
+ // Using 'new' to access a non-public constructor. go/totw/134
std::unique_ptr<AuraInit> aura_init = base::WrapUnique(new AuraInit());
- if (!aura_init->Init(connector, identity, resource_file, resource_file_200,
- io_task_runner, mode, register_path_provider)) {
+ if (!aura_init->Init(params))
aura_init.reset();
- }
return aura_init;
}
-bool AuraInit::Init(service_manager::Connector* connector,
- const service_manager::Identity& identity,
- const std::string& resource_file,
- const std::string& resource_file_200,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- Mode mode,
- bool register_path_provider) {
+bool AuraInit::Init(const InitParams& params) {
env_ = aura::Env::CreateInstance(aura::Env::Mode::MUS);
- if (mode == Mode::AURA_MUS || mode == Mode::AURA_MUS2) {
- MusClient::InitParams params;
- params.connector = connector;
- params.identity = identity;
- params.io_task_runner = io_task_runner;
- params.wtc_config = mode == Mode::AURA_MUS2
- ? aura::WindowTreeClient::Config::kMus2
- : aura::WindowTreeClient::Config::kMash;
- params.create_wm_state = true;
- mus_client_ = std::make_unique<MusClient>(params);
+ if (params.mode == Mode::AURA_MUS || params.mode == Mode::AURA_MUS2) {
+ MusClient::InitParams mus_params;
+ mus_params.connector = params.connector;
+ mus_params.identity = params.identity;
+ mus_params.io_task_runner = params.io_task_runner;
+ mus_params.wtc_config =
+ params.mode == Mode::AURA_MUS2
+ ? aura::WindowTreeClient::Config::kMus2
+ : aura::WindowTreeClient::Config::kMashDeprecated;
+ mus_params.create_wm_state = true;
+ mus_params.use_accessibility_host = params.use_accessibility_host;
+ mus_client_ = std::make_unique<MusClient>(mus_params);
}
// MaterialDesignController may have initialized already (such as happens
// in the utility process).
if (!ui::MaterialDesignController::is_mode_initialized())
ui::MaterialDesignController::Initialize();
- if (!InitializeResources(connector, resource_file, resource_file_200,
- register_path_provider)) {
- return false;
- }
-
-// Initialize the skia font code to go ask fontconfig underneath.
-#if defined(OS_LINUX)
- font_loader_ = sk_make_sp<font_service::FontLoader>(connector);
- SkFontConfigInterface::SetGlobal(font_loader_);
-
- // Initialize static default font, by running this now, before any other apps
- // load, we ensure all the state is set up.
- bool success = gfx::PlatformFontLinux::InitDefaultFont();
-
- // If a remote service manager has shut down, initializing the font will fail.
- if (!success)
+ if (!InitializeResources(params))
return false;
-#endif // defined(OS_LINUX)
ui::InitializeInputMethodForTesting();
return true;
}
-bool AuraInit::InitializeResources(service_manager::Connector* connector,
- const std::string& resource_file,
- const std::string& resource_file_200,
- bool register_path_provider) {
+bool AuraInit::InitializeResources(const InitParams& params) {
// Resources may have already been initialized (e.g. when chrome with mash is
// used to launch the current app).
if (ui::ResourceBundle::HasSharedInstance())
return true;
- std::set<std::string> resource_paths({resource_file});
- if (!resource_file_200.empty())
- resource_paths.insert(resource_file_200);
+ std::set<std::string> resource_paths({params.resource_file});
+ if (!params.resource_file_200.empty())
+ resource_paths.insert(params.resource_file_200);
catalog::ResourceLoader loader;
filesystem::mojom::DirectoryPtr directory;
- connector->BindInterface(catalog::mojom::kServiceName, &directory);
+ params.connector->BindInterface(catalog::mojom::kServiceName, &directory);
// TODO(jonross): if this proves useful in resolving the crash of
// mash_unittests then switch AuraInit to have an Init method, returning a
// bool for success. Then update all callsites to use this to determine the
@@ -157,17 +92,17 @@ bool AuraInit::InitializeResources(service_manager::Connector* connector,
// Calling services will shutdown ServiceContext as appropriate.
if (!loader.OpenFiles(std::move(directory), resource_paths))
return false;
- if (register_path_provider)
+ if (params.register_path_provider)
ui::RegisterPathProvider();
- base::File pak_file = loader.TakeFile(resource_file);
+ base::File pak_file = loader.TakeFile(params.resource_file);
base::File pak_file_2 = pak_file.Duplicate();
ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(
std::move(pak_file), base::MemoryMappedFile::Region::kWholeFile);
ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile(
std::move(pak_file_2), ui::SCALE_FACTOR_100P);
- if (!resource_file_200.empty())
+ if (!params.resource_file_200.empty())
ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile(
- loader.TakeFile(resource_file_200), ui::SCALE_FACTOR_200P);
+ loader.TakeFile(params.resource_file_200), ui::SCALE_FACTOR_200P);
return true;
}
diff --git a/chromium/ui/views/mus/aura_init.h b/chromium/ui/views/mus/aura_init.h
index c76c8314a24..c821deae031 100644
--- a/chromium/ui/views/mus/aura_init.h
+++ b/chromium/ui/views/mus/aura_init.h
@@ -9,8 +9,7 @@
#include <string>
#include "base/macros.h"
-#include "build/build_config.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
+#include "services/service_manager/public/cpp/identity.h"
#include "ui/aura/env.h"
#include "ui/views/mus/mus_export.h"
@@ -22,13 +21,8 @@ namespace base {
class SingleThreadTaskRunner;
}
-namespace font_service {
-class FontLoader;
-}
-
namespace service_manager {
class Connector;
-class Identity;
}
namespace views {
@@ -55,20 +49,28 @@ class VIEWS_MUS_EXPORT AuraInit {
~AuraInit();
+ struct VIEWS_MUS_EXPORT InitParams {
+ InitParams();
+ ~InitParams();
+ service_manager::Connector* connector = nullptr;
+ service_manager::Identity identity;
+ // File for strings and 1x icons. Defaults to views_mus_resources.pak.
+ std::string resource_file;
+ // File for 2x icons. Can be empty.
+ std::string resource_file_200;
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr;
+ Mode mode = Mode::AURA_MUS;
+ bool register_path_provider = true;
+ // When true the client application will connect to the accessibility host
+ // in the browser to supply AX node trees and handle AX actions (e.g. to
+ // support ChromeVox).
+ bool use_accessibility_host = false;
+ };
+
// Returns an AuraInit if initialization can be completed successfully,
// otherwise a nullptr is returned. If initialization fails then Aura is in an
// unusable state, and calling services should shutdown.
- // |resource_file| is the file to load strings and 1x icons from.
- // |resource_file_200| can be an empty string, otherwise it is the file to
- // load 2x icons from.
- static std::unique_ptr<AuraInit> Create(
- service_manager::Connector* connector,
- const service_manager::Identity& identity,
- const std::string& resource_file,
- const std::string& resource_file_200 = std::string(),
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
- Mode mode = Mode::AURA_MUS,
- bool register_path_provider = true);
+ static std::unique_ptr<AuraInit> Create(const InitParams& params);
// Only valid if Mode::AURA_MUS was passed to constructor.
MusClient* mus_client() { return mus_client_.get(); }
@@ -79,22 +81,10 @@ class VIEWS_MUS_EXPORT AuraInit {
// Returns true if AuraInit was able to successfully complete initialization.
// If this returns false, then Aura is in an unusable state, and calling
// services should shutdown.
- bool Init(service_manager::Connector* connector,
- const service_manager::Identity& identity,
- const std::string& resource_file,
- const std::string& resource_file_200,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- Mode mode,
- bool register_path_provider);
-
- bool InitializeResources(service_manager::Connector* connector,
- const std::string& resource_file,
- const std::string& resource_file_200,
- bool register_path_provider);
-
-#if defined(OS_LINUX)
- sk_sp<font_service::FontLoader> font_loader_;
-#endif
+ bool Init(const InitParams& params);
+
+ // Returns true on success.
+ bool InitializeResources(const InitParams& params);
std::unique_ptr<aura::Env> env_;
std::unique_ptr<MusClient> mus_client_;
diff --git a/chromium/ui/views/mus/ax_remote_host.cc b/chromium/ui/views/mus/ax_remote_host.cc
new file mode 100644
index 00000000000..bc1d4edb33e
--- /dev/null
+++ b/chromium/ui/views/mus/ax_remote_host.cc
@@ -0,0 +1,196 @@
+// Copyright 2018 The Chromium Authors. 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/mus/ax_remote_host.h"
+
+#include <stddef.h>
+
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/accessibility/ax_action_data.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_event.h"
+#include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/aura/window.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+#include "ui/views/mus/ax_tree_source_mus.h"
+#include "ui/views/mus/mus_client.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+
+// For external linkage.
+constexpr int AXRemoteHost::kRemoteAXTreeID;
+
+AXRemoteHost::AXRemoteHost() {
+ AXAuraObjCache::GetInstance()->SetDelegate(this);
+}
+
+AXRemoteHost::~AXRemoteHost() {
+ if (widget_)
+ StopMonitoringWidget();
+ AXAuraObjCache::GetInstance()->SetDelegate(nullptr);
+}
+
+void AXRemoteHost::Init(service_manager::Connector* connector) {
+ connector->BindInterface(ax::mojom::kAXHostServiceName, &ax_host_ptr_);
+ BindAndSetRemote();
+}
+
+void AXRemoteHost::InitForTesting(ax::mojom::AXHostPtr host_ptr) {
+ ax_host_ptr_ = std::move(host_ptr);
+ BindAndSetRemote();
+}
+
+void AXRemoteHost::StartMonitoringWidget(Widget* widget) {
+ if (!enabled_)
+ return;
+
+ // Check if we're already tracking a widget.
+ // TODO(jamescook): Support multiple widgets.
+ if (widget_)
+ return;
+ widget_ = widget;
+ widget_->AddObserver(this);
+
+ // The cache needs to track the root window to follow focus changes.
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+ cache->OnRootWindowObjCreated(widget_->GetNativeWindow());
+
+ // Start the AX tree with the contents view because the window frame is
+ // handled by the window manager in another process.
+ View* contents_view = widget_->widget_delegate()->GetContentsView();
+ AXAuraObjWrapper* contents_wrapper = cache->GetOrCreate(contents_view);
+
+ tree_source_ = std::make_unique<AXTreeSourceMus>(contents_wrapper);
+ tree_serializer_ = std::make_unique<AuraAXTreeSerializer>(tree_source_.get());
+
+ SendEvent(contents_wrapper, ax::mojom::Event::kLoadComplete);
+}
+
+void AXRemoteHost::StopMonitoringWidget() {
+ DCHECK(widget_);
+ DCHECK(widget_->HasObserver(this));
+ widget_->RemoveObserver(this);
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+ cache->OnRootWindowObjDestroyed(widget_->GetNativeWindow());
+ cache->Remove(widget_->widget_delegate()->GetContentsView());
+ widget_ = nullptr;
+ // Delete source and serializers to save memory.
+ tree_serializer_.reset();
+ tree_source_.reset();
+}
+
+void AXRemoteHost::HandleEvent(View* view, ax::mojom::Event event_type) {
+ if (!enabled_)
+ return;
+
+ AXAuraObjWrapper* aura_obj =
+ view ? AXAuraObjCache::GetInstance()->GetOrCreate(view)
+ : tree_source_->GetRoot();
+ SendEvent(aura_obj, event_type);
+}
+
+void AXRemoteHost::OnAutomationEnabled(bool enabled) {
+ if (enabled)
+ Enable();
+ else
+ Disable();
+}
+
+void AXRemoteHost::PerformAction(const ui::AXActionData& action) {
+ // TODO(jamescook): Support ax::mojom::Action::kHitTest.
+ tree_source_->HandleAccessibleAction(action);
+}
+
+void AXRemoteHost::OnWidgetDestroying(Widget* widget) {
+ DCHECK_EQ(widget_, widget);
+ StopMonitoringWidget();
+}
+
+void AXRemoteHost::OnChildWindowRemoved(AXAuraObjWrapper* parent) {
+ if (!enabled_)
+ return;
+
+ if (!parent)
+ parent = tree_source_->GetRoot();
+
+ SendEvent(parent, ax::mojom::Event::kChildrenChanged);
+}
+
+void AXRemoteHost::OnEvent(AXAuraObjWrapper* aura_obj,
+ ax::mojom::Event event_type) {
+ SendEvent(aura_obj, event_type);
+}
+
+void AXRemoteHost::FlushForTesting() {
+ ax_host_ptr_.FlushForTesting();
+}
+
+void AXRemoteHost::BindAndSetRemote() {
+ ax::mojom::AXRemoteHostPtr remote;
+ binding_.Bind(mojo::MakeRequest(&remote));
+ ax_host_ptr_->SetRemoteHost(std::move(remote));
+}
+
+void AXRemoteHost::Enable() {
+ // Extensions can send multiple enable events.
+ if (enabled_)
+ return;
+ enabled_ = true;
+
+ std::set<aura::Window*> roots =
+ MusClient::Get()->window_tree_client()->GetRoots();
+ if (roots.empty()) {
+ // Client hasn't opened any widgets yet.
+ return;
+ }
+
+ // TODO(jamescook): Support multiple roots.
+ aura::Window* root_window = *roots.begin();
+ DCHECK(root_window);
+ Widget* root_widget = Widget::GetWidgetForNativeWindow(root_window);
+ DCHECK(root_widget);
+ StartMonitoringWidget(root_widget);
+}
+
+void AXRemoteHost::Disable() {
+ if (!enabled_)
+ return;
+ enabled_ = false;
+ StopMonitoringWidget();
+}
+
+void AXRemoteHost::SendEvent(AXAuraObjWrapper* aura_obj,
+ ax::mojom::Event event_type) {
+ if (!enabled_ || !tree_serializer_)
+ return;
+
+ ui::AXTreeUpdate update;
+ if (!tree_serializer_->SerializeChanges(aura_obj, &update)) {
+ LOG(ERROR) << "Unable to serialize accessibility tree.";
+ return;
+ }
+
+ std::vector<ui::AXTreeUpdate> updates;
+ updates.push_back(update);
+
+ // Make sure the focused node is serialized.
+ AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus();
+ if (focus) {
+ ui::AXTreeUpdate focused_node_update;
+ tree_serializer_->SerializeChanges(focus, &focused_node_update);
+ updates.push_back(focused_node_update);
+ }
+
+ ui::AXEvent event;
+ event.id = aura_obj->GetUniqueId().Get();
+ event.event_type = event_type;
+ // Other fields are not used.
+
+ ax_host_ptr_->HandleAccessibilityEvent(kRemoteAXTreeID, updates, event);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/mus/ax_remote_host.h b/chromium/ui/views/mus/ax_remote_host.h
new file mode 100644
index 00000000000..f213060ab95
--- /dev/null
+++ b/chromium/ui/views/mus/ax_remote_host.h
@@ -0,0 +1,112 @@
+// Copyright 2018 The Chromium Authors. 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_MUS_AX_REMOTE_HOST_H_
+#define UI_VIEWS_MUS_AX_REMOTE_HOST_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/accessibility/ax_tree_serializer.h"
+#include "ui/accessibility/mojom/ax_host.mojom.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/mus/mus_export.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace service_manager {
+class Connector;
+}
+
+namespace ui {
+struct AXActionData;
+struct AXNodeData;
+struct AXTreeData;
+} // namespace ui
+
+namespace views {
+
+class AXAuraObjWrapper;
+class AXTreeSourceMus;
+class View;
+class Widget;
+
+// Manages a tree of automation nodes for a mojo app outside the browser process
+// (e.g. the keyboard shortcut viewer app).
+class VIEWS_MUS_EXPORT AXRemoteHost : public ax::mojom::AXRemoteHost,
+ public WidgetObserver,
+ public AXAuraObjCache::Delegate {
+ public:
+ // Well-known tree ID for the remote client.
+ // TODO(jamescook): Support different IDs for different clients.
+ static constexpr int kRemoteAXTreeID = -2;
+
+ AXRemoteHost();
+ ~AXRemoteHost() override;
+
+ // Initializes and adds ourself as a client of the host service.
+ void Init(service_manager::Connector* connector);
+
+ // Initializes with a fake host.
+ void InitForTesting(ax::mojom::AXHostPtr host_ptr);
+
+ // Sends the initial AX node tree to the host then starts monitoring for AX
+ // events and tree changes.
+ void StartMonitoringWidget(Widget* widget);
+ void StopMonitoringWidget();
+
+ // Handles an event fired upon a |view|.
+ void HandleEvent(View* view, ax::mojom::Event event_type);
+
+ // ax::mojom::AXRemoteHost:
+ void OnAutomationEnabled(bool enabled) override;
+ void PerformAction(const ui::AXActionData& action) override;
+
+ // WidgetObserver:
+ void OnWidgetDestroying(Widget* widget) override;
+
+ // AXAuraObjCache::Delegate:
+ void OnChildWindowRemoved(AXAuraObjWrapper* parent) override;
+ void OnEvent(AXAuraObjWrapper* aura_obj,
+ ax::mojom::Event event_type) override;
+
+ void FlushForTesting();
+
+ private:
+ // Registers this object as a remote host for the parent AXHost.
+ void BindAndSetRemote();
+
+ void Enable();
+ void Disable();
+
+ // Sends an event to the host.
+ void SendEvent(AXAuraObjWrapper* aura_obj, ax::mojom::Event event_type);
+
+ // Accessibility host service in the browser.
+ ax::mojom::AXHostPtr ax_host_ptr_;
+
+ mojo::Binding<ax::mojom::AXRemoteHost> binding_{this};
+
+ // Whether accessibility automation support is enabled.
+ bool enabled_ = false;
+
+ // Top-level widget being tracked.
+ Widget* widget_ = nullptr;
+
+ // Holds the active views-based accessibility tree. A tree consists of all
+ // views descendant to a Widget's content area.
+ std::unique_ptr<AXTreeSourceMus> tree_source_;
+
+ // Serializes incremental updates on the currently active |tree_source_|.
+ using AuraAXTreeSerializer =
+ ui::AXTreeSerializer<AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>;
+ std::unique_ptr<AuraAXTreeSerializer> tree_serializer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AXRemoteHost);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_MUS_AX_REMOTE_HOST_H_
diff --git a/chromium/ui/views/mus/ax_remote_host_unittest.cc b/chromium/ui/views/mus/ax_remote_host_unittest.cc
new file mode 100644
index 00000000000..cf93bb3d11e
--- /dev/null
+++ b/chromium/ui/views/mus/ax_remote_host_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2018 The Chromium Authors. 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/mus/ax_remote_host.h"
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/mojom/ax_host.mojom.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/mus/mus_client_test_api.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+namespace {
+
+// Simulates the AXHostService in the browser.
+class TestAXHostService : public ax::mojom::AXHost {
+ public:
+ explicit TestAXHostService(bool automation_enabled)
+ : automation_enabled_(automation_enabled) {}
+ ~TestAXHostService() override = default;
+
+ ax::mojom::AXHostPtr CreateInterfacePtr() {
+ ax::mojom::AXHostPtr ptr;
+ binding_.Bind(mojo::MakeRequest(&ptr));
+ return ptr;
+ }
+
+ // ax::mojom::AXHost:
+ void SetRemoteHost(ax::mojom::AXRemoteHostPtr client) override {
+ ++add_client_count_;
+ client->OnAutomationEnabled(automation_enabled_);
+ client.FlushForTesting();
+ }
+ void HandleAccessibilityEvent(int32_t tree_id,
+ const std::vector<ui::AXTreeUpdate>& updates,
+ const ui::AXEvent& event) override {
+ ++event_count_;
+ last_tree_id_ = tree_id;
+ last_event_ = event;
+ }
+
+ mojo::Binding<ax::mojom::AXHost> binding_{this};
+ bool automation_enabled_ = false;
+ int add_client_count_ = 0;
+ int event_count_ = 0;
+ int last_tree_id_ = 0;
+ ui::AXEvent last_event_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAXHostService);
+};
+
+// TestView senses accessibility actions.
+class TestView : public View {
+ public:
+ TestView() = default;
+ ~TestView() override = default;
+
+ // View:
+ bool HandleAccessibleAction(const ui::AXActionData& action) override {
+ ++action_count_;
+ last_action_ = action;
+ return true;
+ }
+
+ int action_count_ = 0;
+ ui::AXActionData last_action_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+AXRemoteHost* CreateRemote(TestAXHostService* service) {
+ std::unique_ptr<AXRemoteHost> remote = std::make_unique<AXRemoteHost>();
+ remote->InitForTesting(service->CreateInterfacePtr());
+ remote->FlushForTesting();
+ // Install the AXRemoteHost on MusClient so it monitors Widget creation.
+ MusClientTestApi::SetAXRemoteHost(std::move(remote));
+ return MusClient::Get()->ax_remote_host();
+}
+
+std::unique_ptr<Widget> CreateTestWidget() {
+ std::unique_ptr<Widget> widget = std::make_unique<Widget>();
+ Widget::InitParams params;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = gfx::Rect(1, 2, 333, 444);
+ widget->Init(params);
+ return widget;
+}
+
+using AXRemoteHostTest = ViewsTestBase;
+
+TEST_F(AXRemoteHostTest, CreateRemote) {
+ TestAXHostService service(false /*automation_enabled*/);
+ CreateRemote(&service);
+
+ // Client registered itself with service.
+ EXPECT_EQ(1, service.add_client_count_);
+}
+
+TEST_F(AXRemoteHostTest, AutomationEnabled) {
+ TestAXHostService service(true /*automation_enabled*/);
+ AXRemoteHost* remote = CreateRemote(&service);
+ std::unique_ptr<Widget> widget = CreateTestWidget();
+ remote->FlushForTesting();
+
+ // Event was sent with initial hierarchy.
+ EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type);
+ EXPECT_EQ(AXAuraObjCache::GetInstance()->GetID(
+ widget->widget_delegate()->GetContentsView()),
+ service.last_event_.id);
+}
+
+// Views can trigger accessibility events during Widget construction before the
+// AXRemoteHost starts monitoring the widget. This happens with the material
+// design focus ring on text fields. Verify we don't crash in this case.
+// https://crbug.com/862759
+TEST_F(AXRemoteHostTest, SendEventBeforeWidgetCreated) {
+ TestAXHostService service(true /*automation_enabled*/);
+ AXRemoteHost* remote = CreateRemote(&service);
+ views::View view;
+ remote->HandleEvent(&view, ax::mojom::Event::kLocationChanged);
+ // No crash.
+}
+
+TEST_F(AXRemoteHostTest, CreateWidgetThenEnableAutomation) {
+ TestAXHostService service(false /*automation_enabled*/);
+ AXRemoteHost* remote = CreateRemote(&service);
+ std::unique_ptr<Widget> widget = CreateTestWidget();
+ remote->FlushForTesting();
+
+ // No events were sent because automation isn't enabled.
+ EXPECT_EQ(0, service.event_count_);
+
+ remote->OnAutomationEnabled(true);
+ remote->FlushForTesting();
+
+ // Event was sent with initial hierarchy.
+ EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type);
+ EXPECT_EQ(AXAuraObjCache::GetInstance()->GetID(
+ widget->widget_delegate()->GetContentsView()),
+ service.last_event_.id);
+}
+
+TEST_F(AXRemoteHostTest, PerformAction) {
+ TestAXHostService service(true /*automation_enabled*/);
+ AXRemoteHost* remote = CreateRemote(&service);
+
+ // Create a view to sense the action.
+ TestView view;
+ AXAuraObjCache::GetInstance()->GetOrCreate(&view);
+
+ // Request an action on the view.
+ ui::AXActionData action;
+ action.action = ax::mojom::Action::kScrollDown;
+ action.target_node_id = AXAuraObjCache::GetInstance()->GetID(&view);
+ remote->PerformAction(action);
+
+ // View received the action.
+ EXPECT_EQ(1, view.action_count_);
+ EXPECT_EQ(ax::mojom::Action::kScrollDown, view.last_action_.action);
+}
+
+} // namespace
+} // namespace views
diff --git a/chromium/ui/views/mus/ax_tree_source_mus.cc b/chromium/ui/views/mus/ax_tree_source_mus.cc
new file mode 100644
index 00000000000..6acef96a413
--- /dev/null
+++ b/chromium/ui/views/mus/ax_tree_source_mus.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/mus/ax_tree_source_mus.h"
+
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/transform.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+#include "ui/views/mus/ax_remote_host.h"
+
+namespace views {
+
+AXTreeSourceMus::AXTreeSourceMus(AXAuraObjWrapper* root) : root_(root) {
+ DCHECK(root_);
+}
+
+AXTreeSourceMus::~AXTreeSourceMus() = default;
+
+bool AXTreeSourceMus::GetTreeData(ui::AXTreeData* tree_data) const {
+ tree_data->tree_id = AXRemoteHost::kRemoteAXTreeID;
+ return AXTreeSourceViews::GetTreeData(tree_data);
+}
+
+AXAuraObjWrapper* AXTreeSourceMus::GetRoot() const {
+ return root_;
+}
+
+void AXTreeSourceMus::SerializeNode(AXAuraObjWrapper* node,
+ ui::AXNodeData* out_data) const {
+ if (IsEqual(node, root_)) {
+ node->Serialize(out_data);
+ // Root is a contents view with an offset from the containing Widget.
+ // However, the contents view in the host (browser) already has an offset
+ // from its Widget, so the root should start at (0,0).
+ out_data->location.set_origin(gfx::PointF());
+ out_data->transform.reset();
+ return;
+ }
+
+ AXTreeSourceViews::SerializeNode(node, out_data);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/mus/ax_tree_source_mus.h b/chromium/ui/views/mus/ax_tree_source_mus.h
new file mode 100644
index 00000000000..340f14271d9
--- /dev/null
+++ b/chromium/ui/views/mus/ax_tree_source_mus.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_MUS_AX_TREE_SOURCE_MUS_H_
+#define UI_VIEWS_MUS_AX_TREE_SOURCE_MUS_H_
+
+#include "base/macros.h"
+#include "ui/views/accessibility/ax_tree_source_views.h"
+#include "ui/views/mus/mus_export.h"
+
+namespace views {
+
+class AXAuraObjWrapper;
+
+// This class exposes the views hierarchy as an accessibility tree permitting
+// use with other accessibility classes. Only used for out-of-process views
+// apps (e.g. Chrome OS shortcut_viewer app). The browser process uses
+// AXTreeSourceAura.
+class VIEWS_MUS_EXPORT AXTreeSourceMus : public AXTreeSourceViews {
+ public:
+ // |root| must outlive this object.
+ explicit AXTreeSourceMus(AXAuraObjWrapper* root);
+ ~AXTreeSourceMus() override;
+
+ // AXTreeSource:
+ bool GetTreeData(ui::AXTreeData* data) const override;
+ AXAuraObjWrapper* GetRoot() const override;
+ void SerializeNode(AXAuraObjWrapper* node,
+ ui::AXNodeData* out_data) const override;
+
+ private:
+ // The top-level object to use for the AX tree.
+ AXAuraObjWrapper* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(AXTreeSourceMus);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_MUS_AX_TREE_SOURCE_MUS_H_
diff --git a/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc b/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc
new file mode 100644
index 00000000000..d652b04712e
--- /dev/null
+++ b/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/mus/ax_tree_source_mus.h"
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_tree_data.h"
+#include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/views/accessibility/ax_aura_obj_cache.h"
+#include "ui/views/accessibility/ax_aura_obj_wrapper.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/mus/ax_remote_host.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+namespace {
+
+class AXTreeSourceMusTest : public ViewsTestBase {
+ public:
+ AXTreeSourceMusTest() = default;
+ ~AXTreeSourceMusTest() override = default;
+
+ // testing::Test:
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ widget_ = std::make_unique<Widget>();
+ Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = gfx::Rect(11, 22, 333, 444);
+ params.context = GetContext();
+ widget_->Init(params);
+ widget_->SetContentsView(new View());
+ label_ = new Label(base::ASCIIToUTF16("Label"));
+ label_->SetBounds(1, 1, 111, 111);
+ widget_->GetContentsView()->AddChildView(label_);
+ }
+
+ void TearDown() override {
+ widget_.reset();
+ ViewsTestBase::TearDown();
+ }
+
+ std::unique_ptr<Widget> widget_;
+ Label* label_ = nullptr; // Owned by views hierarchy.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AXTreeSourceMusTest);
+};
+
+TEST_F(AXTreeSourceMusTest, GetTreeData) {
+ AXAuraObjWrapper* root =
+ AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetContentsView());
+ AXTreeSourceMus tree(root);
+ ui::AXTreeData tree_data;
+ tree.GetTreeData(&tree_data);
+ EXPECT_EQ(AXRemoteHost::kRemoteAXTreeID, tree_data.tree_id);
+}
+
+TEST_F(AXTreeSourceMusTest, Serialize) {
+ AXAuraObjCache* cache = AXAuraObjCache::GetInstance();
+ AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView());
+
+ AXTreeSourceMus tree(root);
+ EXPECT_EQ(root, tree.GetRoot());
+
+ // Serialize the root.
+ ui::AXNodeData node_data;
+ tree.SerializeNode(root, &node_data);
+
+ // Root is at the origin and has no parent container.
+ EXPECT_EQ(gfx::RectF(0, 0, 333, 444), node_data.location);
+ EXPECT_EQ(-1, node_data.offset_container_id);
+
+ // Serialize a child.
+ tree.SerializeNode(cache->GetOrCreate(label_), &node_data);
+
+ // Child has relative position with the root as the container.
+ EXPECT_EQ(gfx::RectF(1, 1, 111, 111), node_data.location);
+ EXPECT_EQ(root->GetUniqueId().Get(), node_data.offset_container_id);
+}
+
+} // namespace
+} // namespace views
diff --git a/chromium/ui/views/mus/clipboard_mus.cc b/chromium/ui/views/mus/clipboard_mus.cc
deleted file mode 100644
index 65805757abf..00000000000
--- a/chromium/ui/views/mus/clipboard_mus.cc
+++ /dev/null
@@ -1,347 +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/views/mus/clipboard_mus.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/clipboard/custom_data_helper.h"
-#include "ui/gfx/codec/png_codec.h"
-
-namespace views {
-namespace {
-
-ui::mojom::Clipboard::Type GetType(ui::ClipboardType type) {
- switch (type) {
- case ui::CLIPBOARD_TYPE_COPY_PASTE:
- return ui::mojom::Clipboard::Type::COPY_PASTE;
- case ui::CLIPBOARD_TYPE_SELECTION:
- return ui::mojom::Clipboard::Type::SELECTION;
- case ui::CLIPBOARD_TYPE_DRAG:
- // Only OSX uses a drag clipboard.
- break;
- }
-
- NOTREACHED();
- return ui::mojom::Clipboard::Type::COPY_PASTE;
-}
-
-// The source URL of copied HTML.
-const char kInternalSourceURL[] = "chromium/internal-url";
-
-} // namespace
-
-ClipboardMus::ClipboardMus() {}
-
-ClipboardMus::~ClipboardMus() {}
-
-void ClipboardMus::Init(service_manager::Connector* connector) {
- connector->BindInterface(ui::mojom::kServiceName, &clipboard_);
-}
-
-// TODO(erg): This isn't optimal. It would be better to move the entire
-// FormatType system to mime types throughout chrome, but that's a very large
-// change.
-std::string ClipboardMus::GetMimeTypeFor(const FormatType& format) {
- if (format.Equals(GetUrlFormatType()) || format.Equals(GetUrlWFormatType()))
- return ui::mojom::kMimeTypeURIList;
- if (format.Equals(GetMozUrlFormatType()))
- return ui::mojom::kMimeTypeMozillaURL;
- if (format.Equals(GetPlainTextFormatType()) ||
- format.Equals(GetPlainTextWFormatType())) {
- return ui::mojom::kMimeTypeText;
- }
- if (format.Equals(GetHtmlFormatType()))
- return ui::mojom::kMimeTypeHTML;
- if (format.Equals(GetRtfFormatType()))
- return ui::mojom::kMimeTypeRTF;
- if (format.Equals(GetBitmapFormatType()))
- return ui::mojom::kMimeTypePNG;
- if (format.Equals(GetWebKitSmartPasteFormatType()))
- return kMimeTypeWebkitSmartPaste;
- if (format.Equals(GetWebCustomDataFormatType()))
- return kMimeTypeWebCustomData;
- if (format.Equals(GetPepperCustomDataFormatType()))
- return kMimeTypePepperCustomData;
-
- // TODO(erg): This isn't optimal, but it's the best we can do. On windows,
- // this will return strings that aren't MIME types, though they'll be
- // unique and should be serializable on the other side of the mojo
- // connection.
- return format.Serialize();
-}
-
-bool ClipboardMus::HasMimeType(const std::vector<std::string>& available_types,
- const std::string& type) const {
- return base::ContainsValue(available_types, type);
-}
-
-void ClipboardMus::OnPreShutdown() {}
-
-uint64_t ClipboardMus::GetSequenceNumber(ui::ClipboardType type) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- uint64_t sequence_number = 0;
- clipboard_->GetSequenceNumber(GetType(type), &sequence_number);
- return sequence_number;
-}
-
-bool ClipboardMus::IsFormatAvailable(const FormatType& format,
- ui::ClipboardType type) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
-
- uint64_t sequence_number = 0;
- std::vector<std::string> available_types;
- clipboard_->GetAvailableMimeTypes(GetType(type), &sequence_number,
- &available_types);
-
- std::string format_in_mime = GetMimeTypeFor(format);
- return base::ContainsValue(available_types, format_in_mime);
-}
-
-void ClipboardMus::Clear(ui::ClipboardType type) {
- // Sends the data to mus server.
- uint64_t sequence_number = 0;
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- clipboard_->WriteClipboardData(GetType(type), base::nullopt,
- &sequence_number);
-}
-
-void ClipboardMus::ReadAvailableTypes(ui::ClipboardType type,
- std::vector<base::string16>* types,
- bool* contains_filenames) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
-
- uint64_t sequence_number = 0;
- std::vector<std::string> available_types;
- clipboard_->GetAvailableMimeTypes(GetType(type), &sequence_number,
- &available_types);
-
- types->clear();
- if (HasMimeType(available_types, ui::mojom::kMimeTypeText))
- types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeText));
- if (HasMimeType(available_types, ui::mojom::kMimeTypeHTML))
- types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeHTML));
- if (HasMimeType(available_types, ui::mojom::kMimeTypeRTF))
- types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypeRTF));
- if (HasMimeType(available_types, ui::mojom::kMimeTypePNG))
- types->push_back(base::UTF8ToUTF16(ui::mojom::kMimeTypePNG));
-
- if (HasMimeType(available_types, kMimeTypeWebCustomData)) {
- base::Optional<std::vector<uint8_t>> custom_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), kMimeTypeWebCustomData,
- &sequence_number, &custom_data) &&
- custom_data.has_value()) {
- ui::ReadCustomDataTypes(&custom_data->front(), custom_data->size(),
- types);
- }
- }
-
- *contains_filenames = false;
-}
-
-void ClipboardMus::ReadText(ui::ClipboardType type,
- base::string16* result) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> text_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), ui::mojom::kMimeTypeText,
- &sequence_number, &text_data) &&
- text_data) {
- *result = base::UTF8ToUTF16(base::StringPiece(
- reinterpret_cast<char*>(text_data->data()), text_data->size()));
- }
-}
-
-void ClipboardMus::ReadAsciiText(ui::ClipboardType type,
- std::string* result) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> text_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), ui::mojom::kMimeTypeText,
- &sequence_number, &text_data) &&
- text_data) {
- result->assign(text_data->begin(), text_data->end());
- }
-}
-
-void ClipboardMus::ReadHTML(ui::ClipboardType type,
- base::string16* markup,
- std::string* src_url,
- uint32_t* fragment_start,
- uint32_t* fragment_end) const {
- markup->clear();
- if (src_url)
- src_url->clear();
- *fragment_start = 0;
- *fragment_end = 0;
-
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> html_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), ui::mojom::kMimeTypeHTML,
- &sequence_number, &html_data) &&
- html_data) {
- *markup = base::UTF8ToUTF16(base::StringPiece(
- reinterpret_cast<char*>(html_data->data()), html_data->size()));
- *fragment_end = static_cast<uint32_t>(markup->length());
-
- // We only bother fetching the source url if we were the ones who wrote
- // this html data to the clipboard.
- base::Optional<std::vector<uint8_t>> url_data;
- if (clipboard_->ReadClipboardData(GetType(type), kInternalSourceURL,
- &sequence_number, &url_data) &&
- url_data) {
- src_url->assign(url_data->begin(), url_data->end());
- }
- }
-}
-
-void ClipboardMus::ReadRTF(ui::ClipboardType type, std::string* result) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> rtf_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), ui::mojom::kMimeTypeRTF,
- &sequence_number, &rtf_data) &&
- rtf_data) {
- result->assign(rtf_data->begin(), rtf_data->end());
- }
-}
-
-SkBitmap ClipboardMus::ReadImage(ui::ClipboardType type) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(type), ui::mojom::kMimeTypePNG,
- &sequence_number, &data) &&
- data.has_value()) {
- SkBitmap bitmap;
- if (gfx::PNGCodec::Decode(&data->front(), data->size(), &bitmap))
- return SkBitmap(bitmap);
- }
-
- return SkBitmap();
-}
-
-void ClipboardMus::ReadCustomData(ui::ClipboardType clipboard_type,
- const base::string16& type,
- base::string16* result) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> custom_data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(GetType(clipboard_type),
- kMimeTypeWebCustomData, &sequence_number,
- &custom_data) &&
- custom_data.has_value()) {
- ui::ReadCustomDataForType(&custom_data->front(), custom_data->size(), type,
- result);
- }
-}
-
-void ClipboardMus::ReadBookmark(base::string16* title, std::string* url) const {
- // TODO(erg): This is NOTIMPLEMENTED() on all linux platforms?
- NOTIMPLEMENTED();
-}
-
-void ClipboardMus::ReadData(const FormatType& format,
- std::string* result) const {
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- base::Optional<std::vector<uint8_t>> data;
- uint64_t sequence_number = 0;
- if (clipboard_->ReadClipboardData(ui::mojom::Clipboard::Type::COPY_PASTE,
- GetMimeTypeFor(format), &sequence_number,
- &data) &&
- data) {
- result->assign(data->begin(), data->end());
- }
-}
-
-void ClipboardMus::WriteObjects(ui::ClipboardType type,
- const ObjectMap& objects) {
- current_clipboard_.emplace();
- for (const auto& p : objects)
- DispatchObject(static_cast<ObjectType>(p.first), p.second);
-
- // Sends the data to mus server.
- uint64_t sequence_number = 0;
- mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
- clipboard_->WriteClipboardData(GetType(type), std::move(current_clipboard_),
- &sequence_number);
-}
-
-void ClipboardMus::WriteText(const char* text_data, size_t text_len) {
- DCHECK(current_clipboard_);
- current_clipboard_.value()[ui::mojom::kMimeTypeText] =
- std::vector<uint8_t>(text_data, text_data + text_len);
-}
-
-void ClipboardMus::WriteHTML(const char* markup_data,
- size_t markup_len,
- const char* url_data,
- size_t url_len) {
- DCHECK(current_clipboard_);
- current_clipboard_.value()[ui::mojom::kMimeTypeHTML] =
- std::vector<uint8_t>(markup_data, markup_data + markup_len);
- if (url_len > 0) {
- current_clipboard_.value()[kInternalSourceURL] =
- std::vector<uint8_t>(url_data, url_data + url_len);
- }
-}
-
-void ClipboardMus::WriteRTF(const char* rtf_data, size_t data_len) {
- DCHECK(current_clipboard_);
- current_clipboard_.value()[ui::mojom::kMimeTypeRTF] =
- std::vector<uint8_t>(rtf_data, rtf_data + data_len);
-}
-
-void ClipboardMus::WriteBookmark(const char* title_data,
- size_t title_len,
- const char* url_data,
- size_t url_len) {
- // Writes a Mozilla url (UTF16: URL, newline, title)
- base::string16 bookmark =
- base::UTF8ToUTF16(base::StringPiece(url_data, url_len)) +
- base::ASCIIToUTF16("\n") +
- base::UTF8ToUTF16(base::StringPiece(title_data, title_len));
-
- DCHECK(current_clipboard_);
- current_clipboard_.value()[ui::mojom::kMimeTypeMozillaURL] =
- std::vector<uint8_t>(
- reinterpret_cast<const uint8_t*>(bookmark.data()),
- reinterpret_cast<const uint8_t*>(bookmark.data() + bookmark.size()));
-}
-
-void ClipboardMus::WriteWebSmartPaste() {
- DCHECK(current_clipboard_);
- current_clipboard_.value()[kMimeTypeWebkitSmartPaste] =
- std::vector<uint8_t>();
-}
-
-void ClipboardMus::WriteBitmap(const SkBitmap& bitmap) {
- DCHECK(current_clipboard_);
- // Encode the bitmap as a PNG for transport.
- std::vector<unsigned char> output;
- if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) {
- current_clipboard_.value()[ui::mojom::kMimeTypePNG] = std::move(output);
- }
-}
-
-void ClipboardMus::WriteData(const FormatType& format,
- const char* data_data,
- size_t data_len) {
- DCHECK(current_clipboard_);
- current_clipboard_.value()[GetMimeTypeFor(format)] =
- std::vector<uint8_t>(data_data, data_data + data_len);
-}
-
-} // namespace views
diff --git a/chromium/ui/views/mus/clipboard_mus.h b/chromium/ui/views/mus/clipboard_mus.h
deleted file mode 100644
index 54cbe4abea7..00000000000
--- a/chromium/ui/views/mus/clipboard_mus.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_MUS_CLIPBOARD_MUS_H_
-#define UI_VIEWS_MUS_CLIPBOARD_MUS_H_
-
-#include "base/containers/flat_map.h"
-#include "services/ui/public/interfaces/clipboard.mojom.h"
-#include "ui/base/clipboard/clipboard.h"
-#include "ui/views/mus/mus_export.h"
-
-namespace service_manager {
-class Connector;
-}
-
-namespace views {
-
-// An adaptor class which translates the ui::Clipboard interface to the
-// clipboard provided by mus.
-class VIEWS_MUS_EXPORT ClipboardMus : public ui::Clipboard {
- public:
- ClipboardMus();
- ~ClipboardMus() override;
-
- void Init(service_manager::Connector* connector);
-
- private:
- bool HasMimeType(const std::vector<std::string>& available_types,
- const std::string& type) const;
-
- // Clipboard overrides:
- void OnPreShutdown() override;
- uint64_t GetSequenceNumber(ui::ClipboardType type) const override;
- bool IsFormatAvailable(const FormatType& format,
- ui::ClipboardType type) const override;
- void Clear(ui::ClipboardType type) override;
- void ReadAvailableTypes(ui::ClipboardType type,
- std::vector<base::string16>* types,
- bool* contains_filenames) const override;
- void ReadText(ui::ClipboardType type, base::string16* result) const override;
- void ReadAsciiText(ui::ClipboardType type,
- std::string* result) const override;
- void ReadHTML(ui::ClipboardType type,
- base::string16* markup,
- std::string* src_url,
- uint32_t* fragment_start,
- uint32_t* fragment_end) const override;
- void ReadRTF(ui::ClipboardType type, std::string* result) const override;
- SkBitmap ReadImage(ui::ClipboardType type) const override;
- void ReadCustomData(ui::ClipboardType clipboard_type,
- const base::string16& type,
- base::string16* result) const override;
- void ReadBookmark(base::string16* title, std::string* url) const override;
- void ReadData(const FormatType& format, std::string* result) const override;
- void WriteObjects(ui::ClipboardType type, const ObjectMap& objects) override;
- void WriteText(const char* text_data, size_t text_len) override;
- void WriteHTML(const char* markup_data,
- size_t markup_len,
- const char* url_data,
- size_t url_len) override;
- void WriteRTF(const char* rtf_data, size_t data_len) override;
- void WriteBookmark(const char* title_data,
- size_t title_len,
- const char* url_data,
- size_t url_len) override;
- void WriteWebSmartPaste() override;
- void WriteBitmap(const SkBitmap& bitmap) override;
- void WriteData(const FormatType& format,
- const char* data_data,
- size_t data_len) override;
-
- static std::string GetMimeTypeFor(const FormatType& format);
-
- ui::mojom::ClipboardPtr clipboard_;
-
- // Internal buffer used to accumulate data types. The public interface is
- // WriteObjects(), which then calls our base class DispatchObject() which
- // then calls into each data type specific Write() function. Once we've
- // collected all the data types, we then pass this to the mus server.
- base::Optional<base::flat_map<std::string, std::vector<uint8_t>>>
- current_clipboard_;
-
- DISALLOW_COPY_AND_ASSIGN(ClipboardMus);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_MUS_CLIPBOARD_MUS_H_
diff --git a/chromium/ui/views/mus/clipboard_unittest.cc b/chromium/ui/views/mus/clipboard_unittest.cc
index b04292e94b7..a00f61588e4 100644
--- a/chromium/ui/views/mus/clipboard_unittest.cc
+++ b/chromium/ui/views/mus/clipboard_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/views/mus/clipboard_mus.h"
+#include "ui/base/mojo/clipboard_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/platform/platform_event_source.h"
@@ -11,117 +11,11 @@
namespace ui {
-// So we can't make ScopedViewsTestHelper a global. We must set up
-// ScopedViewsTestHelper on every test (which will create the connection to
-// mus). And we can't modify PlatformClipboardTraits to not be a pure static
-// struct. So to solve these lifetime issues, create an adapter that owns the
-// ScopedViewsTestHelper and then .
-class ForwardingTestingClipboard : public ui::Clipboard {
- public:
- ForwardingTestingClipboard()
- : test_helper_(new views::ScopedViewsTestHelper),
- clipboard_to_test_(Clipboard::GetForCurrentThread()) {
- // If we don't have a window manager connection, we will get the default
- // platform clipboard instead.
- EXPECT_TRUE(views::MusClient::Exists());
- }
-
- ~ForwardingTestingClipboard() override {
- Clipboard::DestroyClipboardForCurrentThread();
- }
+namespace {
- void Destroy() {
- delete this;
- }
+std::unique_ptr<views::ScopedViewsTestHelper> g_scoped_views_test_helper;
- protected:
- // Overridden from ui::Clipboard:
- void OnPreShutdown() override {}
-
- uint64_t GetSequenceNumber(ClipboardType type) const override {
- return clipboard_to_test_->GetSequenceNumber(type);
- }
- bool IsFormatAvailable(const FormatType& format,
- ClipboardType type) const override {
- return clipboard_to_test_->IsFormatAvailable(format, type);
- }
- void Clear(ClipboardType type) override {
- clipboard_to_test_->Clear(type);
- }
- void ReadAvailableTypes(ClipboardType type,
- std::vector<base::string16>* types,
- bool* contains_filenames) const override {
- clipboard_to_test_->ReadAvailableTypes(type, types, contains_filenames);
- }
- void ReadText(ClipboardType type, base::string16* result) const override {
- clipboard_to_test_->ReadText(type, result);
- }
- void ReadAsciiText(ClipboardType type, std::string* result) const override {
- clipboard_to_test_->ReadAsciiText(type, result);
- }
- void ReadHTML(ClipboardType type, base::string16* markup,
- std::string* src_url, uint32_t* fragment_start,
- uint32_t* fragment_end) const override {
- clipboard_to_test_->ReadHTML(type, markup, src_url,
- fragment_start, fragment_end);
- }
- void ReadRTF(ClipboardType type, std::string* result) const override {
- clipboard_to_test_->ReadRTF(type, result);
- }
- SkBitmap ReadImage(ClipboardType type) const override {
- return clipboard_to_test_->ReadImage(type);
- }
- void ReadCustomData(ClipboardType clipboard_type,
- const base::string16& type,
- base::string16* result) const override {
- clipboard_to_test_->ReadCustomData(clipboard_type, type, result);
- }
- void ReadBookmark(base::string16* title, std::string* url) const override {
- clipboard_to_test_->ReadBookmark(title, url);
- }
- void ReadData(const FormatType& format, std::string* result) const override {
- clipboard_to_test_->ReadData(format, result);
- }
- void WriteObjects(ClipboardType type, const ObjectMap& objects) override {
- clipboard_to_test_->WriteObjects(type, objects);
- }
- void WriteText(const char* text_data, size_t text_len) override {
- clipboard_to_test_->WriteText(text_data, text_len);
- }
- void WriteHTML(const char* markup_data,
- size_t markup_len,
- const char* url_data,
- size_t url_len) override {
- clipboard_to_test_->WriteHTML(markup_data, markup_len, url_data, url_len);
- }
- void WriteRTF(const char* rtf_data, size_t data_len) override {
- clipboard_to_test_->WriteRTF(rtf_data, data_len);
- }
- void WriteBookmark(const char* title_data,
- size_t title_len,
- const char* url_data,
- size_t url_len) override {
- clipboard_to_test_->WriteBookmark(title_data, title_len,
- url_data, url_len);
- }
- void WriteWebSmartPaste() override {
- clipboard_to_test_->WriteWebSmartPaste();
- }
- void WriteBitmap(const SkBitmap& bitmap) override {
- clipboard_to_test_->WriteBitmap(bitmap);
- }
- void WriteData(const FormatType& format,
- const char* data_data,
- size_t data_len) override {
- clipboard_to_test_->WriteData(format, data_data, data_len);
- }
-
- private:
- std::unique_ptr<views::ScopedViewsTestHelper> test_helper_;
- ui::Clipboard* clipboard_to_test_;
-
- DISALLOW_COPY_AND_ASSIGN(ForwardingTestingClipboard);
-};
+} // namespace
struct PlatformClipboardTraits {
static std::unique_ptr<PlatformEventSource> GetEventSource() {
@@ -129,13 +23,14 @@ struct PlatformClipboardTraits {
}
static Clipboard* Create() {
- return new ForwardingTestingClipboard();
+ g_scoped_views_test_helper =
+ std::make_unique<views::ScopedViewsTestHelper>();
+ EXPECT_TRUE(views::MusClient::Exists());
+ return Clipboard::GetForCurrentThread();
}
- static bool IsMusTest() { return true; }
-
static void Destroy(Clipboard* clipboard) {
- static_cast<ForwardingTestingClipboard*>(clipboard)->Destroy();
+ g_scoped_views_test_helper.reset();
}
};
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
index 0233bc336cd..67be4ae4d87 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -21,6 +21,7 @@
#include "ui/base/hit_test.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
+#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/corewm/tooltip_aura.h"
#include "ui/views/mus/mus_client.h"
#include "ui/views/mus/mus_property_mirror.h"
@@ -46,7 +47,11 @@ namespace {
class ClientSideNonClientFrameView : public NonClientFrameView {
public:
explicit ClientSideNonClientFrameView(views::Widget* widget)
- : widget_(widget) {}
+ : widget_(widget) {
+ // Not part of the accessibility node hierarchy because the window frame is
+ // provided by the window manager.
+ GetViewAccessibility().set_is_ignored(true);
+ }
~ClientSideNonClientFrameView() override {}
private:
@@ -57,6 +62,11 @@ class ClientSideNonClientFrameView : public NonClientFrameView {
return is_maximized ? values.maximized_insets : values.normal_insets;
}
+ // View:
+ const char* GetClassName() const override {
+ return "ClientSideNonClientFrameView";
+ }
+
// NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override {
gfx::Rect result(GetLocalBounds());
@@ -294,15 +304,10 @@ bool DesktopWindowTreeHostMus::ShouldSendClientAreaToServer() const {
}
void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) {
- // |TYPE_WINDOW| and |TYPE_PANEL| are forced to transparent as otherwise the
- // window is opaque and the client decorations drawn by the window manager
- // would not be seen.
- const bool transparent =
- params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW ||
- params.type == Widget::InitParams::TYPE_WINDOW ||
- params.type == Widget::InitParams::TYPE_PANEL;
- content_window()->SetTransparent(transparent);
- window()->SetTransparent(transparent);
+ const bool translucent =
+ MusClient::ShouldMakeWidgetWindowsTranslucent(params);
+ content_window()->SetTransparent(translucent);
+ window()->SetTransparent(translucent);
window()->SetProperty(aura::client::kShowStateKey, params.show_state);
@@ -316,9 +321,40 @@ void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) {
NativeWidgetAura::SetShadowElevationFromInitParams(window(), params);
- // Transient parents are connected using the Window created by WindowTreeHost,
- // which is owned by the window manager. This way the window manager can
- // properly identify and honor transients.
+ // Widget's |InitParams::parent| has different meanings depending on the
+ // NativeWidgetPrivate implementation that the Widget creates (each Widget
+ // creates a NativeWidgetPrivate). When DesktopNativeWidgetAura is used as
+ // the NativeWidgetPrivate implementation, |InitParams::parent| means the
+ // entirety of the contents of the new Widget should be stacked above the
+ // entirety of the contents of the Widget for |InitParams::parent|, and
+ // the new Widget should be deleted when the Widget for
+ // |InitParams::parent| is deleted. Aura and mus provide support for
+ // transient windows, which provides both the stacking and ownership needed to
+ // support |InitParams::parent|.
+ //
+ // DesktopNativeWidgetAura internally creates two aura::Windows (one by
+ // WindowTreeHost, the other in |DesktopNativeWidgetAura::content_window_|).
+ // To have the entirety of the contents of the Widget appear on top of the
+ // entirety of the contents of another Widget, the stacking is done on the
+ // WindowTreeHost's window. For these reasons, the following code uses the
+ // Window associated with the WindowTreeHost of the |params.parent|.
+ //
+ // Views/Aura provide support for child-modal windows. Child-modal windows
+ // are windows that are modal to their transient parent. Because this code
+ // implements |InitParams::parent| in terms of transient parents, it means
+ // it is not possible to support both |InitParams::parent| as well as a
+ // child-modal window. This is *only* an issue if a Widget that uses a
+ // DesktopNativeWidgetAura needs to be child-modal to another window. At
+ // the current time NativeWidgetAura is always used for child-modal windows,
+ // so this isn't an issue.
+ //
+ // If we end up needing to use DesktopNativeWidgetAura for child-modal
+ // Widgets then we need something different. Possibilities include:
+ // . Have mus ignore child-modal windows and instead implement child-modal
+ // entirely in the client (this is what we do on Windows). To get this
+ // right likely means we need the ability to disable windows (see
+ // HWNDMessageHandler::InitModalType() for how Windows OS does this).
+ // . Implement |InitParams::parent| using a different (new) API.
if (params.parent && params.parent->GetHost()) {
aura::client::GetTransientWindowClient()->AddTransientChild(
params.parent->GetHost()->window(), window());
@@ -367,6 +403,16 @@ void DesktopWindowTreeHostMus::OnWidgetInitDone() {
MusClient::Get()->OnCaptureClientSet(
aura::client::GetCaptureClient(window()));
+
+ // These views are not part of the accessibility node hierarchy because the
+ // window frame is provided by the window manager.
+ Widget* widget = native_widget_delegate_->AsWidget();
+ if (widget->non_client_view())
+ widget->non_client_view()->GetViewAccessibility().set_is_ignored(true);
+ if (widget->client_view())
+ widget->client_view()->GetViewAccessibility().set_is_ignored(true);
+
+ MusClient::Get()->OnWidgetInitDone(widget);
}
std::unique_ptr<corewm::Tooltip> DesktopWindowTreeHostMus::CreateTooltip() {
@@ -388,6 +434,9 @@ void DesktopWindowTreeHostMus::Close() {
// (otherwise events may be processed, which is unexpected).
Hide();
+ // This has to happen *after* Hide() above, otherwise animations won't work.
+ content_window()->Hide();
+
// Close doesn't delete this immediately, as 'this' may still be on the stack
// resulting in possible crashes when the stack unwindes.
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -640,8 +689,14 @@ bool DesktopWindowTreeHostMus::IsVisibleOnAllWorkspaces() const {
}
bool DesktopWindowTreeHostMus::SetWindowTitle(const base::string16& title) {
- if (window()->GetTitle() == title)
+ WidgetDelegate* widget_delegate =
+ native_widget_delegate_->AsWidget()->widget_delegate();
+ const bool show = widget_delegate && widget_delegate->ShouldShowWindowTitle();
+ if (window()->GetTitle() == title &&
+ window()->GetProperty(aura::client::kTitleShownKey) == show) {
return false;
+ }
+ window()->SetProperty(aura::client::kTitleShownKey, show);
window()->SetTitle(title);
return true;
}
@@ -727,6 +782,9 @@ void DesktopWindowTreeHostMus::SetWindowIcons(const gfx::ImageSkia& window_icon,
}
void DesktopWindowTreeHostMus::InitModalType(ui::ModalType modal_type) {
+ // See comment in Init() related to |InitParams::parent| as to why this DCHECK
+ // is here.
+ DCHECK_NE(modal_type, ui::MODAL_TYPE_CHILD);
window()->SetProperty(aura::client::kModalKey, modal_type);
}
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.h b/chromium/ui/views/mus/desktop_window_tree_host_mus.h
index def36405a77..92d4e468695 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.h
@@ -114,6 +114,7 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override {}
void SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) override;
void InitModalType(ui::ModalType modal_type) override;
diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
index 769904d73e1..79aef70d12d 100644
--- a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
+++ b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -4,9 +4,7 @@
#include "ui/views/mus/desktop_window_tree_host_mus.h"
-#include "base/debug/stack_trace.h"
-#include "base/run_loop.h"
-
+#include "base/strings/utf_string_conversions.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/focus_client.h"
@@ -22,6 +20,7 @@
#include "ui/aura/window.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
+#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/mus/mus_client.h"
#include "ui/views/mus/screen_mus.h"
#include "ui/views/test/views_test_base.h"
@@ -80,9 +79,7 @@ class ExpectsNullCursorClientDuringTearDown : public aura::WindowObserver {
window_->AddObserver(this);
}
- ~ExpectsNullCursorClientDuringTearDown() override {
- EXPECT_FALSE(window_);
- }
+ ~ExpectsNullCursorClientDuringTearDown() override { EXPECT_FALSE(window_); }
private:
// aura::WindowObserver:
@@ -160,7 +157,8 @@ TEST_F(DesktopWindowTreeHostMusTest, Capture) {
->capture_window());
}
-TEST_F(DesktopWindowTreeHostMusTest, Deactivate) {
+// TODO(http://crbug.com/864614): Fails flakily in mus with ws2.
+TEST_F(DesktopWindowTreeHostMusTest, DISABLED_Deactivate) {
std::unique_ptr<Widget> widget1(CreateWidget());
widget1->Show();
@@ -260,8 +258,7 @@ TEST_F(DesktopWindowTreeHostMusTest, StackAtTop) {
std::unique_ptr<Widget> widget2(CreateWidget());
widget2->Show();
- aura::test::ChangeCompletionWaiter waiter(
- aura::ChangeType::REORDER, true);
+ aura::test::ChangeCompletionWaiter waiter(aura::ChangeType::REORDER, true);
widget1->StackAtTop();
waiter.Wait();
@@ -277,21 +274,20 @@ TEST_F(DesktopWindowTreeHostMusTest, StackAtTopAlreadyOnTop) {
std::unique_ptr<Widget> widget2(CreateWidget());
widget2->Show();
- aura::test::ChangeCompletionWaiter waiter(
- aura::ChangeType::REORDER, true);
+ aura::test::ChangeCompletionWaiter waiter(aura::ChangeType::REORDER, true);
widget2->StackAtTop();
waiter.Wait();
}
-TEST_F(DesktopWindowTreeHostMusTest, StackAbove) {
+// TODO(http://crbug.com/864615): Fails consistently in mus with ws2.
+TEST_F(DesktopWindowTreeHostMusTest, DISABLED_StackAbove) {
std::unique_ptr<Widget> widget1(CreateWidget(nullptr));
widget1->Show();
std::unique_ptr<Widget> widget2(CreateWidget(nullptr));
widget2->Show();
- aura::test::ChangeCompletionWaiter waiter(
- aura::ChangeType::REORDER, true);
+ aura::test::ChangeCompletionWaiter waiter(aura::ChangeType::REORDER, true);
widget1->StackAboveWidget(widget2.get());
waiter.Wait();
}
@@ -396,4 +392,132 @@ TEST_F(DesktopWindowTreeHostMusTest, GetWindowBoundsInScreen) {
EXPECT_EQ(gfx::Rect(800, 0, 100, 100), widget2.GetWindowBoundsInScreen());
}
+// WidgetDelegate implementation that allows setting window-title and whether
+// the title should be shown.
+class WindowTitleWidgetDelegate : public WidgetDelegateView {
+ public:
+ WindowTitleWidgetDelegate() = default;
+ ~WindowTitleWidgetDelegate() override = default;
+
+ void set_window_title(const base::string16& title) { window_title_ = title; }
+ void set_should_show_window_title(bool value) {
+ should_show_window_title_ = value;
+ }
+
+ // WidgetDelegateView:
+ base::string16 GetWindowTitle() const override { return window_title_; }
+ bool ShouldShowWindowTitle() const override {
+ return should_show_window_title_;
+ }
+
+ private:
+ base::string16 window_title_;
+ bool should_show_window_title_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTitleWidgetDelegate);
+};
+
+TEST_F(DesktopWindowTreeHostMusTest, WindowTitle) {
+ // Owned by |widget|.
+ WindowTitleWidgetDelegate* delegate = new WindowTitleWidgetDelegate();
+ std::unique_ptr<Widget> widget(CreateWidget(delegate));
+ aura::Window* window = widget->GetNativeWindow()->GetRootWindow();
+
+ // Set the title in the delegate and verify it propagates.
+ const base::string16 title1 = base::ASCIIToUTF16("X");
+ delegate->set_window_title(title1);
+ widget->UpdateWindowTitle();
+ EXPECT_TRUE(window->GetProperty(aura::client::kTitleShownKey));
+ EXPECT_EQ(title1, window->GetTitle());
+
+ // Hiding the title should not change the title.
+ delegate->set_should_show_window_title(false);
+ widget->UpdateWindowTitle();
+ EXPECT_FALSE(window->GetProperty(aura::client::kTitleShownKey));
+ EXPECT_EQ(title1, window->GetTitle());
+
+ // Show the title again with a different value.
+ delegate->set_should_show_window_title(true);
+ const base::string16 title2 = base::ASCIIToUTF16("Z");
+ delegate->set_window_title(title2);
+ widget->UpdateWindowTitle();
+ EXPECT_TRUE(window->GetProperty(aura::client::kTitleShownKey));
+ EXPECT_EQ(title2, window->GetTitle());
+}
+
+TEST_F(DesktopWindowTreeHostMusTest, Accessibility) {
+ std::unique_ptr<Widget> widget = CreateWidget();
+ // Widget frame views do not participate in accessibility node hierarchy
+ // because the frame is provided by the window manager.
+ views::NonClientView* non_client_view = widget->non_client_view();
+ EXPECT_TRUE(non_client_view->GetViewAccessibility().is_ignored());
+ EXPECT_TRUE(
+ non_client_view->frame_view()->GetViewAccessibility().is_ignored());
+ EXPECT_TRUE(widget->client_view()->GetViewAccessibility().is_ignored());
+}
+
+// Used to ensure the visibility of the root window is changed before that of
+// the content window. This is necessary else close/hide animations end up
+// animating a hidden (black) window.
+class WidgetWindowVisibilityObserver : public aura::WindowObserver {
+ public:
+ explicit WidgetWindowVisibilityObserver(Widget* widget)
+ : content_window_(widget->GetNativeWindow()),
+ root_window_(content_window_->GetRootWindow()) {
+ EXPECT_NE(content_window_, root_window_);
+ content_window_->AddObserver(this);
+ root_window_->AddObserver(this);
+ EXPECT_TRUE(content_window_->IsVisible());
+ EXPECT_TRUE(root_window_->IsVisible());
+ }
+
+ ~WidgetWindowVisibilityObserver() override {
+ content_window_->RemoveObserver(this);
+ root_window_->RemoveObserver(this);
+ }
+
+ bool got_content_window_hidden() const { return got_content_window_hidden_; }
+
+ bool got_root_window_hidden() const { return got_root_window_hidden_; }
+
+ private:
+ // aura::WindowObserver:
+ void OnWindowVisibilityChanging(aura::Window* window, bool visible) override {
+ if (visible)
+ return;
+
+ if (!got_root_window_hidden_) {
+ EXPECT_EQ(window, root_window_);
+ got_root_window_hidden_ = true;
+ } else if (!got_content_window_hidden_) {
+ EXPECT_EQ(window, content_window_);
+ got_content_window_hidden_ = true;
+ }
+ }
+
+ aura::Window* content_window_;
+ aura::Window* root_window_;
+
+ // Set to true when |content_window_| is hidden. This is only checked after
+ // the |root_window_| is hidden.
+ bool got_content_window_hidden_ = false;
+
+ // Set to true when |root_window_| is hidden.
+ bool got_root_window_hidden_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(WidgetWindowVisibilityObserver);
+};
+
+// See comments above WidgetWindowVisibilityObserver for details on what this
+// verifies.
+TEST_F(DesktopWindowTreeHostMusTest,
+ HideChangesRootWindowVisibilityBeforeContentWindowVisibility) {
+ std::unique_ptr<Widget> widget(CreateWidget());
+ widget->Show();
+ WidgetWindowVisibilityObserver observer(widget.get());
+ widget->Close();
+ EXPECT_TRUE(observer.got_content_window_hidden());
+ EXPECT_TRUE(observer.got_root_window_hidden());
+}
+
} // namespace views
diff --git a/chromium/ui/views/mus/drag_interactive_uitest.cc b/chromium/ui/views/mus/drag_interactive_uitest.cc
index 50bb37c5d49..d222948c84c 100644
--- a/chromium/ui/views/mus/drag_interactive_uitest.cc
+++ b/chromium/ui/views/mus/drag_interactive_uitest.cc
@@ -145,7 +145,8 @@ void DragTest_Part1(int64_t display_id,
base::BindOnce(&DragTest_Part2, display_id, quit_closure));
}
-TEST_F(DragTestInteractive, DragTest) {
+// TODO(http://crbug.com/864616): Hangs indefinitely in mus with ws2.
+TEST_F(DragTestInteractive, DISABLED_DragTest) {
Widget* source_widget = CreateTopLevelFramelessPlatformWidget();
View* source_view = new DraggableView;
source_widget->SetContentsView(source_view);
diff --git a/chromium/ui/views/mus/interactive_ui_tests_manifest.json b/chromium/ui/views/mus/interactive_ui_tests_manifest.json
index a961e49f56c..36a5b66daf1 100644
--- a/chromium/ui/views/mus/interactive_ui_tests_manifest.json
+++ b/chromium/ui/views/mus/interactive_ui_tests_manifest.json
@@ -4,7 +4,8 @@
"interface_provider_specs": {
"service_manager:connector": {
"requires": {
- "*": [ "app", "test" ]
+ "*": [ "app", "test" ],
+ "ui": [ "window_manager" ]
}
}
}
diff --git a/chromium/ui/views/mus/mus_client.cc b/chromium/ui/views/mus/mus_client.cc
index b8d4dc77bfd..09eb2fcd5bc 100644
--- a/chromium/ui/views/mus/mus_client.cc
+++ b/chromium/ui/views/mus/mus_client.cc
@@ -24,8 +24,9 @@
#include "ui/aura/mus/window_tree_host_mus_init_params.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
+#include "ui/base/mojo/clipboard_client.h"
#include "ui/views/mus/aura_init.h"
-#include "ui/views/mus/clipboard_mus.h"
+#include "ui/views/mus/ax_remote_host.h"
#include "ui/views/mus/desktop_window_tree_host_mus.h"
#include "ui/views/mus/mus_property_mirror.h"
#include "ui/views/mus/pointer_watcher_event_router.h"
@@ -104,10 +105,8 @@ MusClient::MusClient(const InitParams& params) : identity_(params.identity) {
wm_state_ = std::make_unique<wm::WMState>();
service_manager::Connector* connector = params.connector;
- if (params.bind_test_ws_interfaces) {
- connector->BindInterface(ui::mojom::kServiceName, &server_test_ptr_);
+ if (params.bind_test_ws_interfaces)
connector->BindInterface(ui::mojom::kServiceName, &event_injector_);
- }
if (!params.window_tree_client) {
DCHECK(io_task_runner);
@@ -131,11 +130,20 @@ MusClient::MusClient(const InitParams& params) : identity_(params.identity) {
input_device_client_->Connect(std::move(input_device_server));
screen_ = std::make_unique<ScreenMus>(this);
- screen_->Init(connector);
-
- std::unique_ptr<ClipboardMus> clipboard = std::make_unique<ClipboardMus>();
- clipboard->Init(connector);
- ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard));
+ if (params.wtc_config == aura::WindowTreeClient::Config::kMashDeprecated)
+ screen_->InitDeprecated(connector);
+ else
+ window_tree_client_->WaitForDisplays();
+
+ ui::mojom::ClipboardHostPtr clipboard_host_ptr;
+ connector->BindInterface(ui::mojom::kServiceName, &clipboard_host_ptr);
+ ui::Clipboard::SetClipboardForCurrentThread(
+ std::make_unique<ui::ClipboardClient>(std::move(clipboard_host_ptr)));
+
+ if (params.use_accessibility_host) {
+ ax_remote_host_ = std::make_unique<AXRemoteHost>();
+ ax_remote_host_->Init(connector);
+ }
}
ViewsDelegate::GetInstance()->set_native_widget_factory(
@@ -173,6 +181,16 @@ bool MusClient::ShouldCreateDesktopNativeWidgetAura(
}
// static
+bool MusClient::ShouldMakeWidgetWindowsTranslucent(
+ const Widget::InitParams& params) {
+ // |TYPE_WINDOW| and |TYPE_PANEL| are forced to translucent so that the
+ // window manager can draw the client decorations.
+ return params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW ||
+ params.type == Widget::InitParams::TYPE_WINDOW ||
+ params.type == Widget::InitParams::TYPE_PANEL;
+}
+
+// static
std::map<std::string, std::vector<uint8_t>>
MusClient::ConfigurePropertiesFromParams(
const Widget::InitParams& init_params) {
@@ -190,8 +208,8 @@ MusClient::ConfigurePropertiesFromParams(
mojo::ConvertTo<TransportType>(init_params.CanActivate());
properties[WindowManager::kTranslucent_InitProperty] =
- mojo::ConvertTo<TransportType>(init_params.opacity ==
- Widget::InitParams::TRANSLUCENT_WINDOW);
+ mojo::ConvertTo<TransportType>(
+ ShouldMakeWidgetWindowsTranslucent(init_params));
if (!init_params.bounds.IsEmpty()) {
properties[WindowManager::kBounds_InitProperty] =
@@ -226,6 +244,18 @@ MusClient::ConfigurePropertiesFromParams(
init_params.delegate->GetResizeBehavior()));
}
+ if (init_params.delegate->ShouldShowWindowTitle()) {
+ properties[WindowManager::kWindowTitleShown_Property] =
+ mojo::ConvertTo<TransportType>(static_cast<PrimitiveType>(
+ init_params.delegate->ShouldShowWindowTitle()));
+ }
+
+ if (!init_params.delegate->GetWindowTitle().empty()) {
+ properties[WindowManager::kWindowTitle_Property] =
+ mojo::ConvertTo<TransportType>(
+ init_params.delegate->GetWindowTitle());
+ }
+
// TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia].
gfx::ImageSkia app_icon = init_params.delegate->GetWindowAppIcon();
SkBitmap app_bitmap = app_icon.GetRepresentation(1.f).sk_bitmap();
@@ -233,6 +263,7 @@ MusClient::ConfigurePropertiesFromParams(
properties[WindowManager::kAppIcon_Property] =
mojo::ConvertTo<TransportType>(app_bitmap);
}
+
// TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia].
gfx::ImageSkia window_icon = init_params.delegate->GetWindowIcon();
SkBitmap window_bitmap = window_icon.GetRepresentation(1.f).sk_bitmap();
@@ -265,6 +296,12 @@ NativeWidget* MusClient::CreateNativeWidget(
return native_widget;
}
+void MusClient::OnWidgetInitDone(Widget* widget) {
+ // Start tracking the widget for accessibility.
+ if (ax_remote_host_)
+ ax_remote_host_->StartMonitoringWidget(widget);
+}
+
void MusClient::OnCaptureClientSet(
aura::client::CaptureClient* capture_client) {
pointer_watcher_event_router_->AttachToCaptureClient(capture_client);
@@ -286,6 +323,7 @@ void MusClient::AddObserver(MusClientObserver* observer) {
void MusClient::RemoveObserver(MusClientObserver* observer) {
observer_list_.RemoveObserver(observer);
}
+
void MusClient::SetMusPropertyMirror(
std::unique_ptr<MusPropertyMirror> mirror) {
mus_property_mirror_ = std::move(mirror);
@@ -299,13 +337,6 @@ void MusClient::CloseAllWidgets() {
}
}
-ui::mojom::WindowServerTest* MusClient::GetTestingInterface() const {
- // This will only be set in tests. CHECK to ensure it doesn't get used
- // elsewhere.
- CHECK(server_test_ptr_);
- return server_test_ptr_.get();
-}
-
ui::mojom::EventInjector* MusClient::GetTestingEventInjector() const {
CHECK(event_injector_);
return event_injector_.get();
@@ -345,6 +376,14 @@ void MusClient::OnPointerEventObserved(const ui::PointerEvent& event,
target);
}
+void MusClient::OnDisplaysChanged(
+ std::vector<ui::mojom::WsDisplayPtr> ws_displays,
+ int64_t primary_display_id,
+ int64_t internal_display_id) {
+ screen_->OnDisplaysChanged(std::move(ws_displays), primary_display_id,
+ internal_display_id);
+}
+
void MusClient::OnWindowManagerFrameValuesChanged() {
for (auto& observer : observer_list_)
observer.OnWindowManagerFrameValuesChanged();
diff --git a/chromium/ui/views/mus/mus_client.h b/chromium/ui/views/mus/mus_client.h
index 60239f46548..cb3d6b6cef5 100644
--- a/chromium/ui/views/mus/mus_client.h
+++ b/chromium/ui/views/mus/mus_client.h
@@ -14,7 +14,6 @@
#include "base/macros.h"
#include "services/service_manager/public/cpp/identity.h"
#include "services/ui/public/interfaces/event_injector.mojom.h"
-#include "services/ui/public/interfaces/window_server_test.mojom.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/mus/window_tree_client_delegate.h"
@@ -48,6 +47,7 @@ class WMState;
namespace views {
+class AXRemoteHost;
class DesktopNativeWidgetAura;
class MusClientObserver;
class MusPropertyMirror;
@@ -76,7 +76,7 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
service_manager::Identity identity;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr;
aura::WindowTreeClient::Config wtc_config =
- aura::WindowTreeClient::Config::kMash;
+ aura::WindowTreeClient::Config::kMashDeprecated;
// Create a wm::WMState. Some processes (e.g. the browser) may already
// have one.
@@ -89,6 +89,10 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
// If provided, MusClient will not create the WindowTreeClient. Not owned.
// Must outlive MusClient.
aura::WindowTreeClient* window_tree_client = nullptr;
+
+ // Connect to the accessibility host service in the browser (e.g. to support
+ // ChromeVox).
+ bool use_accessibility_host = false;
};
// Most clients should use AuraInit, which creates a MusClient.
@@ -104,6 +108,10 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
static bool ShouldCreateDesktopNativeWidgetAura(
const Widget::InitParams& init_params);
+ // Returns true if the windows backing the Widget should be made translucent.
+ static bool ShouldMakeWidgetWindowsTranslucent(
+ const Widget::InitParams& params);
+
// Returns the properties to supply to mus when creating a window.
static std::map<std::string, std::vector<uint8_t>>
ConfigurePropertiesFromParams(const Widget::InitParams& init_params);
@@ -114,6 +122,8 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
return pointer_watcher_event_router_.get();
}
+ AXRemoteHost* ax_remote_host() { return ax_remote_host_.get(); }
+
// Getter for type safety. Most code can use display::Screen::GetScreen().
ScreenMus* screen() { return screen_.get(); }
@@ -122,6 +132,8 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
// NativeWidget has not been explicitly set.
NativeWidget* CreateNativeWidget(const Widget::InitParams& init_params,
internal::NativeWidgetDelegate* delegate);
+ void OnWidgetInitDone(Widget* widget);
+
// Called when the capture client has been set for a window to notify
// PointerWatcherEventRouter and CaptureSynchronizer.
void OnCaptureClientSet(aura::client::CaptureClient* capture_client);
@@ -141,16 +153,13 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
// Close all widgets this client knows.
void CloseAllWidgets();
- // Returns an interface to test drawing in mus. Only available when created
- // with MusClientTestingState::CREATE_TESTING_STATE.
- ui::mojom::WindowServerTest* GetTestingInterface() const;
-
// Returns an interface to inject events into the Window Service. Only
// available when created with MusClientTestingState::CREATE_TESTING_STATE.
ui::mojom::EventInjector* GetTestingEventInjector() const;
private:
friend class AuraInit;
+ friend class MusClientTestApi;
// Creates a DesktopWindowTreeHostMus. This is set as the factory function
// ViewsDelegate such that if DesktopNativeWidgetAura is created without a
@@ -169,6 +178,9 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
int64_t display_id,
aura::Window* target) override;
aura::PropertyConverter* GetPropertyConverter() override;
+ void OnDisplaysChanged(std::vector<ui::mojom::WsDisplayPtr> ws_displays,
+ int64_t primary_display_id,
+ int64_t internal_display_id) override;
// ScreenMusDelegate:
void OnWindowManagerFrameValuesChanged() override;
@@ -206,7 +218,11 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate,
// Gives services transparent remote access the InputDeviceManager.
std::unique_ptr<ui::InputDeviceClient> input_device_client_;
- ui::mojom::WindowServerTestPtr server_test_ptr_;
+ // Forwards accessibility events to extensions in the browser. Can be null for
+ // apps that do not need accessibility support and for the browser itself
+ // under OopAsh.
+ std::unique_ptr<AXRemoteHost> ax_remote_host_;
+
ui::mojom::EventInjectorPtr event_injector_;
DISALLOW_COPY_AND_ASSIGN(MusClient);
diff --git a/chromium/ui/views/mus/mus_client_test_api.h b/chromium/ui/views/mus/mus_client_test_api.h
new file mode 100644
index 00000000000..e3052276ce8
--- /dev/null
+++ b/chromium/ui/views/mus/mus_client_test_api.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_MUS_MUS_CLIENT_TEST_API_H_
+#define UI_VIEWS_MUS_MUS_CLIENT_TEST_API_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "ui/views/mus/mus_client.h"
+
+namespace views {
+
+class AXRemoteHost;
+
+class MusClientTestApi {
+ public:
+ static void SetAXRemoteHost(std::unique_ptr<AXRemoteHost> client) {
+ MusClient::Get()->ax_remote_host_ = std::move(client);
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MusClientTestApi);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_MUS_MUS_CLIENT_TEST_API_H_
diff --git a/chromium/ui/views/mus/mus_views_delegate.cc b/chromium/ui/views/mus/mus_views_delegate.cc
new file mode 100644
index 00000000000..927c065ee5f
--- /dev/null
+++ b/chromium/ui/views/mus/mus_views_delegate.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. 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/mus/mus_views_delegate.h"
+
+#include "ui/views/mus/ax_remote_host.h"
+#include "ui/views/mus/mus_client.h"
+
+namespace views {
+
+MusViewsDelegate::MusViewsDelegate() = default;
+
+MusViewsDelegate::~MusViewsDelegate() = default;
+
+void MusViewsDelegate::NotifyAccessibilityEvent(View* view,
+ ax::mojom::Event event_type) {
+ // Null in AuraInit::Mode::AURA_MUS_WINDOW_MANAGER which is used in mash.
+ if (MusClient::Get() && MusClient::Get()->ax_remote_host())
+ MusClient::Get()->ax_remote_host()->HandleEvent(view, event_type);
+}
+
+} // namespace views
diff --git a/chromium/ui/views/mus/mus_views_delegate.h b/chromium/ui/views/mus/mus_views_delegate.h
new file mode 100644
index 00000000000..b7d55503ac1
--- /dev/null
+++ b/chromium/ui/views/mus/mus_views_delegate.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_MUS_MUS_VIEWS_DELEGATE_H_
+#define UI_VIEWS_MUS_MUS_VIEWS_DELEGATE_H_
+
+#include "base/macros.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/mus/mus_export.h"
+#include "ui/views/views_delegate.h"
+
+namespace views {
+
+class VIEWS_MUS_EXPORT MusViewsDelegate : public ViewsDelegate {
+ public:
+ MusViewsDelegate();
+ ~MusViewsDelegate() override;
+
+ // ViewsDelegate:
+ void NotifyAccessibilityEvent(View* view,
+ ax::mojom::Event event_type) override;
+
+ private:
+ LayoutProvider layout_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(MusViewsDelegate);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_MUS_MUS_VIEWS_DELEGATE_H_
diff --git a/chromium/ui/views/mus/remote_view/BUILD.gn b/chromium/ui/views/mus/remote_view/BUILD.gn
index a5232997d72..8b333326484 100644
--- a/chromium/ui/views/mus/remote_view/BUILD.gn
+++ b/chromium/ui/views/mus/remote_view/BUILD.gn
@@ -8,7 +8,7 @@ source_set("remote_view_host") {
"remote_view_host.h",
]
- deps = [
+ public_deps = [
"//base",
"//ui/aura",
"//ui/views",
diff --git a/chromium/ui/views/mus/screen_mus.cc b/chromium/ui/views/mus/screen_mus.cc
index 8c8dd07f7e9..1238f81aefe 100644
--- a/chromium/ui/views/mus/screen_mus.cc
+++ b/chromium/ui/views/mus/screen_mus.cc
@@ -47,7 +47,7 @@ ScreenMus::~ScreenMus() {
display::Screen::SetScreenInstance(nullptr);
}
-void ScreenMus::Init(service_manager::Connector* connector) {
+void ScreenMus::InitDeprecated(service_manager::Connector* connector) {
connector->BindInterface(ui::mojom::kServiceName, &screen_provider_);
ui::mojom::ScreenProviderObserverPtr observer;
@@ -72,28 +72,6 @@ void ScreenMus::Init(service_manager::Connector* connector) {
}
}
-display::Display ScreenMus::GetDisplayNearestWindow(
- gfx::NativeWindow window) const {
- aura::WindowTreeHostMus* window_tree_host_mus =
- aura::WindowTreeHostMus::ForWindow(window);
- if (!window_tree_host_mus)
- return GetPrimaryDisplay();
- return window_tree_host_mus->GetDisplay();
-}
-
-gfx::Point ScreenMus::GetCursorScreenPoint() {
- return aura::Env::GetInstance()->last_mouse_location();
-}
-
-bool ScreenMus::IsWindowUnderCursor(gfx::NativeWindow window) {
- return window && window->IsVisible() &&
- window->GetBoundsInScreen().Contains(GetCursorScreenPoint());
-}
-
-aura::Window* ScreenMus::GetWindowAtScreenPoint(const gfx::Point& point) {
- return delegate_->GetWindowAtScreenPoint(point);
-}
-
void ScreenMus::OnDisplaysChanged(
std::vector<ui::mojom::WsDisplayPtr> ws_displays,
int64_t primary_display_id,
@@ -154,4 +132,26 @@ void ScreenMus::OnDisplaysChanged(
}
}
+display::Display ScreenMus::GetDisplayNearestWindow(
+ gfx::NativeWindow window) const {
+ aura::WindowTreeHostMus* window_tree_host_mus =
+ aura::WindowTreeHostMus::ForWindow(window);
+ if (!window_tree_host_mus)
+ return GetPrimaryDisplay();
+ return window_tree_host_mus->GetDisplay();
+}
+
+gfx::Point ScreenMus::GetCursorScreenPoint() {
+ return aura::Env::GetInstance()->last_mouse_location();
+}
+
+bool ScreenMus::IsWindowUnderCursor(gfx::NativeWindow window) {
+ return window && window->IsVisible() &&
+ window->GetBoundsInScreen().Contains(GetCursorScreenPoint());
+}
+
+aura::Window* ScreenMus::GetWindowAtScreenPoint(const gfx::Point& point) {
+ return delegate_->GetWindowAtScreenPoint(point);
+}
+
} // namespace views
diff --git a/chromium/ui/views/mus/screen_mus.h b/chromium/ui/views/mus/screen_mus.h
index 42c4d150573..ad94f9f1090 100644
--- a/chromium/ui/views/mus/screen_mus.h
+++ b/chromium/ui/views/mus/screen_mus.h
@@ -25,7 +25,13 @@ class VIEWS_MUS_EXPORT ScreenMus : public display::ScreenBase,
explicit ScreenMus(ScreenMusDelegate* delegate);
~ScreenMus() override;
- void Init(service_manager::Connector* connector);
+ // TODO(sky): not used with ws2. Remove. https://crbug.com/842365.
+ void InitDeprecated(service_manager::Connector* connector);
+
+ // ui::mojom::ScreenProviderObserver:
+ void OnDisplaysChanged(std::vector<ui::mojom::WsDisplayPtr> ws_displays,
+ int64_t primary_display_id,
+ int64_t internal_display_id) override;
private:
friend class ScreenMusTestApi;
@@ -37,11 +43,6 @@ class VIEWS_MUS_EXPORT ScreenMus : public display::ScreenBase,
bool IsWindowUnderCursor(gfx::NativeWindow window) override;
aura::Window* GetWindowAtScreenPoint(const gfx::Point& point) override;
- // ui::mojom::ScreenProvider:
- void OnDisplaysChanged(std::vector<ui::mojom::WsDisplayPtr> ws_displays,
- int64_t primary_display_id,
- int64_t internal_display_id) override;
-
ScreenMusDelegate* delegate_;
ui::mojom::ScreenProviderPtr screen_provider_;
mojo::Binding<ui::mojom::ScreenProviderObserver>
diff --git a/chromium/ui/views/mus/views_mus_test_suite.cc b/chromium/ui/views/mus/views_mus_test_suite.cc
index 304d7625316..959f5ad7ede 100644
--- a/chromium/ui/views/mus/views_mus_test_suite.cc
+++ b/chromium/ui/views/mus/views_mus_test_suite.cc
@@ -15,8 +15,8 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/core/embedder/scoped_ipc_support.h"
#include "services/catalog/catalog.h"
#include "services/service_manager/background/background_service_manager.h"
#include "services/service_manager/public/cpp/connector.h"
@@ -72,12 +72,12 @@ class ServiceManagerConnection {
ipc_thread_("IPC thread") {
catalog::Catalog::LoadDefaultCatalogManifest(
base::FilePath(kCatalogFilename));
- mojo::edk::Init();
+ mojo::core::Init();
ipc_thread_.StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
- ipc_support_ = std::make_unique<mojo::edk::ScopedIPCSupport>(
+ ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
ipc_thread_.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+ mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
@@ -104,6 +104,7 @@ class ServiceManagerConnection {
params.connector = GetConnector();
params.identity = service_manager_identity_;
params.bind_test_ws_interfaces = true;
+ params.wtc_config = aura::WindowTreeClient::Config::kMus2;
return std::make_unique<MusClient>(params);
}
@@ -136,11 +137,7 @@ class ServiceManagerConnection {
service_manager::Identity(
GetTestName(), service_manager::mojom::kRootUserID),
std::move(service), nullptr);
-
- // ui/views/mus requires a WindowManager running, so launch test_wm.
- service_manager::Connector* connector = context_->connector();
- connector->StartService("test_wm");
- service_manager_connector_ = connector->Clone();
+ service_manager_connector_ = context_->connector()->Clone();
service_manager_identity_ = context_->identity();
wait->Signal();
}
@@ -150,19 +147,18 @@ class ServiceManagerConnection {
wait->Signal();
}
- // Returns the name of the test executable, e.g.
- // "views_mus_unittests".
+ // Returns the name of the test executable, e.g. "views_mus_unittests".
std::string GetTestName() {
base::FilePath executable = base::CommandLine::ForCurrentProcess()
->GetProgram()
.BaseName()
.RemoveExtension();
- return std::string("") + executable.MaybeAsASCII();
+ return executable.MaybeAsASCII();
}
base::Thread thread_;
base::Thread ipc_thread_;
- std::unique_ptr<mojo::edk::ScopedIPCSupport> ipc_support_;
+ std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_;
std::unique_ptr<service_manager::BackgroundServiceManager>
background_service_manager_;
std::unique_ptr<service_manager::ServiceContext> context_;
diff --git a/chromium/ui/views/mus/window_manager_constants_converters.h b/chromium/ui/views/mus/window_manager_constants_converters.h
index 25764a8c474..e9493eb6c84 100644
--- a/chromium/ui/views/mus/window_manager_constants_converters.h
+++ b/chromium/ui/views/mus/window_manager_constants_converters.h
@@ -5,7 +5,7 @@
#ifndef UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_
#define UI_VIEWS_MUS_WINDOW_MANAGER_CONSTANTS_CONVERTERS_H_
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/views/mus/mus_export.h"
#include "ui/views/widget/widget.h"
diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc
index d93eacd40f1..24bb9e2a566 100644
--- a/chromium/ui/views/painter.cc
+++ b/chromium/ui/views/painter.cc
@@ -28,7 +28,10 @@ namespace {
// of the background.
class SolidRoundRectPainter : public Painter {
public:
- SolidRoundRectPainter(SkColor bg_color, SkColor stroke_color, float radius);
+ SolidRoundRectPainter(SkColor bg_color,
+ SkColor stroke_color,
+ float radius,
+ const gfx::Insets& insets);
~SolidRoundRectPainter() override;
// Painter:
@@ -39,14 +42,19 @@ class SolidRoundRectPainter : public Painter {
const SkColor bg_color_;
const SkColor stroke_color_;
const float radius_;
+ const gfx::Insets insets_;
DISALLOW_COPY_AND_ASSIGN(SolidRoundRectPainter);
};
SolidRoundRectPainter::SolidRoundRectPainter(SkColor bg_color,
SkColor stroke_color,
- float radius)
- : bg_color_(bg_color), stroke_color_(stroke_color), radius_(radius) {}
+ float radius,
+ const gfx::Insets& insets)
+ : bg_color_(bg_color),
+ stroke_color_(stroke_color),
+ radius_(radius),
+ insets_(insets) {}
SolidRoundRectPainter::~SolidRoundRectPainter() {}
@@ -58,7 +66,9 @@ void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
gfx::ScopedCanvas scoped_canvas(canvas);
const float scale = canvas->UndoDeviceScaleFactor();
- gfx::RectF border_rect_f(gfx::ScaleToEnclosingRect(gfx::Rect(size), scale));
+ gfx::Rect inset_rect(size);
+ inset_rect.Inset(insets_);
+ gfx::RectF border_rect_f(gfx::ScaleToEnclosingRect(inset_rect, scale));
const SkScalar scaled_corner_radius = SkFloatToScalar(radius_ * scale);
cc::PaintFlags flags;
@@ -253,10 +263,12 @@ void Painter::PaintFocusPainter(View* view,
}
// static
-std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainter(SkColor color,
- float radius) {
+std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainter(
+ SkColor color,
+ float radius,
+ const gfx::Insets& insets) {
return std::make_unique<SolidRoundRectPainter>(color, SK_ColorTRANSPARENT,
- radius);
+ radius, insets);
}
// static
@@ -264,8 +276,8 @@ std::unique_ptr<Painter> Painter::CreateRoundRectWith1PxBorderPainter(
SkColor bg_color,
SkColor stroke_color,
float radius) {
- return std::make_unique<SolidRoundRectPainter>(bg_color, stroke_color,
- radius);
+ return std::make_unique<SolidRoundRectPainter>(bg_color, stroke_color, radius,
+ gfx::Insets());
}
// static
diff --git a/chromium/ui/views/painter.h b/chromium/ui/views/painter.h
index cd9672304d8..cb3f495dc8d 100644
--- a/chromium/ui/views/painter.h
+++ b/chromium/ui/views/painter.h
@@ -13,12 +13,12 @@
#include "base/macros.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/nine_image_painter_factory.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/views/views_export.h"
namespace gfx {
class Canvas;
class ImageSkia;
-class Insets;
class InsetsF;
class Rect;
class Size;
@@ -54,8 +54,10 @@ class VIEWS_EXPORT Painter {
// Creates a painter that draws a RoundRect with a solid color and given
// corner radius.
- static std::unique_ptr<Painter> CreateSolidRoundRectPainter(SkColor color,
- float radius);
+ static std::unique_ptr<Painter> CreateSolidRoundRectPainter(
+ SkColor color,
+ float radius,
+ const gfx::Insets& insets = gfx::Insets());
// Creates a painter that draws a RoundRect with a solid color and a given
// corner radius, and also adds a 1px border (inset) in the given color.
diff --git a/chromium/ui/views/run_all_unittests_main.cc b/chromium/ui/views/run_all_unittests_main.cc
index 51d21871cbc..5e7810477a4 100644
--- a/chromium/ui/views/run_all_unittests_main.cc
+++ b/chromium/ui/views/run_all_unittests_main.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/core/embedder/embedder.h"
#include "ui/views/views_test_suite.h"
int main(int argc, char** argv) {
- mojo::edk::Init();
+ mojo::core::Init();
return views::ViewsTestSuite(argc, argv).RunTests();
}
diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc
index ef68fb58bbb..564d6e6440b 100644
--- a/chromium/ui/views/style/platform_style.cc
+++ b/chromium/ui/views/style/platform_style.cc
@@ -40,6 +40,11 @@ const bool PlatformStyle::kIsOkButtonLeading = true;
const bool PlatformStyle::kIsOkButtonLeading = false;
#endif
+// Set kFocusHaloInset to negative half of kFocusHaloThickness to draw half of
+// the focus ring inside and half outside the parent elmeent
+const float PlatformStyle::kFocusHaloThickness = 2.f;
+const float PlatformStyle::kFocusHaloInset = -1.f;
+
#if !defined(OS_MACOSX)
const int PlatformStyle::kMinLabelButtonWidth = 70;
@@ -57,9 +62,6 @@ const bool PlatformStyle::kUseRipples = true;
const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = false;
const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = true;
const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = false;
-const float PlatformStyle::kFocusHaloThickness = 2.f;
-const float PlatformStyle::kFocusHaloInset =
- -PlatformStyle::kFocusHaloThickness;
const bool PlatformStyle::kPreferFocusRings = false;
// static
diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm
index 2106dccefa2..945f4b2efcf 100644
--- a/chromium/ui/views/style/platform_style_mac.mm
+++ b/chromium/ui/views/style/platform_style_mac.mm
@@ -42,8 +42,6 @@ const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = false;
const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true;
const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = true;
const bool PlatformStyle::kUseRipples = false;
-const float PlatformStyle::kFocusHaloThickness = 4.f;
-const float PlatformStyle::kFocusHaloInset = -2.f;
const bool PlatformStyle::kPreferFocusRings = true;
const Button::NotifyAction PlatformStyle::kMenuNotifyActivationAction =
diff --git a/chromium/ui/views/style/typography_provider.cc b/chromium/ui/views/style/typography_provider.cc
index e208380993d..2f0f2718059 100644
--- a/chromium/ui/views/style/typography_provider.cc
+++ b/chromium/ui/views/style/typography_provider.cc
@@ -115,7 +115,7 @@ void DefaultTypographyProvider::GetDefaultFont(int context,
*size_delta = ui::kTitleFontSizeDelta;
break;
case style::CONTEXT_TOUCH_MENU:
- *size_delta = -1;
+ *size_delta = 2;
break;
default:
*size_delta = ui::kLabelFontSizeDelta;
diff --git a/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc b/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc
index 3b283a149a9..df2048dfd10 100644
--- a/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc
+++ b/chromium/ui/views/touchui/touch_selection_menu_runner_views.cc
@@ -131,6 +131,11 @@ TouchSelectionMenuRunnerViews::Menu::Menu(TouchSelectionMenuRunnerViews* owner,
bounds.AdjustToFit(work_area);
widget->SetBounds(bounds);
}
+ // Using BubbleDialogDelegateView engages its CreateBubbleWidget() which
+ // invokes widget->StackAbove(context). That causes the bubble to stack
+ // _immediately_ above |context|; below any already-existing bubbles. That
+ // doesn't make sense for a menu, so put it back on top.
+ widget->StackAtTop();
widget->Show();
}
diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc
index 655e745ee61..c53f79d424e 100644
--- a/chromium/ui/views/view.cc
+++ b/chromium/ui/views/view.cc
@@ -325,6 +325,8 @@ void View::SetBoundsRect(const gfx::Rect& bounds) {
if (bounds == bounds_) {
if (needs_layout_) {
needs_layout_ = false;
+ TRACE_EVENT1("views", "View::Layout(set_bounds)", "class",
+ GetClassName());
Layout();
}
return;
@@ -1419,12 +1421,14 @@ bool View::HandleAccessibleAction(const ui::AXActionData& action_data) {
break;
case ax::mojom::Action::kDoDefault: {
const gfx::Point center = GetLocalBounds().CenterPoint();
- OnMousePressed(ui::MouseEvent(
- ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
- ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
- OnMouseReleased(ui::MouseEvent(
- ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
- ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+ ui::MouseEvent press(ui::ET_MOUSE_PRESSED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON);
+ OnEvent(&press);
+ ui::MouseEvent release(ui::ET_MOUSE_RELEASED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON);
+ OnEvent(&release);
return true;
}
case ax::mojom::Action::kFocus:
@@ -2185,6 +2189,8 @@ void View::BoundsChanged(const gfx::Rect& previous_bounds) {
if (needs_layout_ || previous_bounds.size() != size()) {
needs_layout_ = false;
+ TRACE_EVENT1("views", "View::Layout(bounds_changed)", "class",
+ GetClassName());
Layout();
}
diff --git a/chromium/ui/views/view_model.cc b/chromium/ui/views/view_model.cc
index b0e49c2ece5..baa6cfb2b1b 100644
--- a/chromium/ui/views/view_model.cc
+++ b/chromium/ui/views/view_model.cc
@@ -67,8 +67,7 @@ int ViewModelBase::GetIndexOfView(const View* view) const {
return -1;
}
-ViewModelBase::ViewModelBase() {
-}
+ViewModelBase::ViewModelBase() = default;
void ViewModelBase::AddUnsafe(View* view, int index) {
DCHECK_LE(index, static_cast<int>(entries_.size()));
diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc
index 7b303dac473..846e579237c 100644
--- a/chromium/ui/views/views_delegate.cc
+++ b/chromium/ui/views/views_delegate.cc
@@ -80,6 +80,10 @@ HICON ViewsDelegate::GetDefaultWindowIcon() const {
return nullptr;
}
+HICON ViewsDelegate::GetSmallWindowIcon() const {
+ return nullptr;
+}
+
bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const {
return false;
}
@@ -100,6 +104,10 @@ void ViewsDelegate::AddRef() {
void ViewsDelegate::ReleaseRef() {
}
+void ViewsDelegate::OnBeforeWidgetInit(
+ Widget::InitParams* params,
+ internal::NativeWidgetDelegate* delegate) {}
+
base::TimeDelta ViewsDelegate::GetTextfieldPasswordRevealDuration() {
return base::TimeDelta();
}
diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h
index 8f703482084..3f16d177c21 100644
--- a/chromium/ui/views/views_delegate.h
+++ b/chromium/ui/views/views_delegate.h
@@ -151,7 +151,7 @@ class VIEWS_EXPORT ViewsDelegate {
// Retrieves the default window icon to use for windows if none is specified.
virtual HICON GetDefaultWindowIcon() const;
// Retrieves the small window icon to use for windows if none is specified.
- virtual HICON GetSmallWindowIcon() const = 0;
+ virtual HICON GetSmallWindowIcon() const;
// Returns true if the window passed in is in the Windows 8 metro
// environment.
virtual bool IsWindowInMetro(gfx::NativeWindow window) const;
@@ -171,7 +171,7 @@ class VIEWS_EXPORT ViewsDelegate {
// Gives the platform a chance to modify the properties of a Widget.
virtual void OnBeforeWidgetInit(Widget::InitParams* params,
- internal::NativeWidgetDelegate* delegate) = 0;
+ internal::NativeWidgetDelegate* delegate);
// Returns the password reveal duration for Textfield.
virtual base::TimeDelta GetTextfieldPasswordRevealDuration();
diff --git a/chromium/ui/views/views_perftests.cc b/chromium/ui/views/views_perftests.cc
index 6740c887c41..3e06f188cb0 100644
--- a/chromium/ui/views/views_perftests.cc
+++ b/chromium/ui/views/views_perftests.cc
@@ -3,12 +3,12 @@
// found in the LICENSE file.
#include "base/test/launcher/unit_test_launcher.h"
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/core/embedder/embedder.h"
#include "ui/views/views_test_suite.h"
int main(int argc, char** argv) {
views::ViewsTestSuite test_suite(argc, argv);
- mojo::edk::Init();
+ mojo::core::Init();
return base::LaunchUnitTestsSerially(
argc, argv,
base::BindOnce(&views::ViewsTestSuite::Run,
diff --git a/chromium/ui/views/widget/DEPS b/chromium/ui/views/widget/DEPS
index 74d84ca36a1..a244cf72baf 100644
--- a/chromium/ui/views/widget/DEPS
+++ b/chromium/ui/views/widget/DEPS
@@ -1,5 +1,5 @@
specific_include_rules = {
"widget_interactive_uitest\.cc": [
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
]
}
diff --git a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm
index a6baee39626..7e79906a819 100644
--- a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm
+++ b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm
@@ -96,9 +96,9 @@ class TestWidgetDelegate : public test::TestDesktopWidgetDelegate {
constexpr char TestWidgetDelegate::kAccessibleWindowTitle[];
-class NativeWidgetMacAccessibilityTest : public test::WidgetTest {
+class AXNativeWidgetMacTest : public test::WidgetTest {
public:
- NativeWidgetMacAccessibilityTest() {}
+ AXNativeWidgetMacTest() {}
void SetUp() override {
test::WidgetTest::SetUp();
@@ -198,14 +198,14 @@ class NativeWidgetMacAccessibilityTest : public test::WidgetTest {
private:
TestWidgetDelegate widget_delegate_;
- DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacAccessibilityTest);
+ DISALLOW_COPY_AND_ASSIGN(AXNativeWidgetMacTest);
};
} // namespace
// Test that all methods in the NSAccessibility informal protocol can be called
// on a retained accessibility object after the source view is deleted.
-TEST_F(NativeWidgetMacAccessibilityTest, Lifetime) {
+TEST_F(AXNativeWidgetMacTest, Lifetime) {
Textfield* view = AddChildTextfield(widget()->GetContentsView()->size());
base::scoped_nsobject<NSObject> ax_node(view->GetNativeViewAccessible(),
base::scoped_policy::RETAIN);
@@ -286,7 +286,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, Lifetime) {
}
// Check that potentially keyboard-focusable elements are always leaf nodes.
-TEST_F(NativeWidgetMacAccessibilityTest, FocusableElementsAreLeafNodes) {
+TEST_F(AXNativeWidgetMacTest, FocusableElementsAreLeafNodes) {
// LabelButtons will have a label inside the button. The label should be
// ignored because the button is potentially keyboard focusable.
TestLabelButton* button = new TestLabelButton();
@@ -323,7 +323,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, FocusableElementsAreLeafNodes) {
// Test for NSAccessibilityChildrenAttribute, and ensure it excludes ignored
// children from the accessibility tree.
-TEST_F(NativeWidgetMacAccessibilityTest, ChildrenAttribute) {
+TEST_F(AXNativeWidgetMacTest, ChildrenAttribute) {
// Check childless views don't have accessibility children.
EXPECT_EQ(0u,
[AttributeValueAtMidpoint(NSAccessibilityChildrenAttribute) count]);
@@ -346,7 +346,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ChildrenAttribute) {
// Test for NSAccessibilityParentAttribute, including for a Widget with no
// parent.
-TEST_F(NativeWidgetMacAccessibilityTest, ParentAttribute) {
+TEST_F(AXNativeWidgetMacTest, ParentAttribute) {
Textfield* child = AddChildTextfield(widget()->GetContentsView()->size());
// Views with Widget parents will have a NSWindow parent.
@@ -373,7 +373,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ParentAttribute) {
// Test for NSAccessibilityPositionAttribute, including on Widget movement
// updates.
-TEST_F(NativeWidgetMacAccessibilityTest, PositionAttribute) {
+TEST_F(AXNativeWidgetMacTest, PositionAttribute) {
NSValue* widget_origin =
[NSValue valueWithPoint:gfx::ScreenPointToNSPoint(
GetWidgetBounds().bottom_left())];
@@ -390,7 +390,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, PositionAttribute) {
}
// Test for NSAccessibilityHelpAttribute.
-TEST_F(NativeWidgetMacAccessibilityTest, HelpAttribute) {
+TEST_F(AXNativeWidgetMacTest, HelpAttribute) {
Label* label = new Label(base::SysNSStringToUTF16(kTestStringValue));
label->SetSize(GetWidgetBounds().size());
EXPECT_NSEQ(@"", AttributeValueAtMidpoint(NSAccessibilityHelpAttribute));
@@ -402,7 +402,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, HelpAttribute) {
// Test view properties that should report the native NSWindow, and test
// specific properties on that NSWindow.
-TEST_F(NativeWidgetMacAccessibilityTest, NativeWindowProperties) {
+TEST_F(AXNativeWidgetMacTest, NativeWindowProperties) {
FlexibleRoleTestView* view =
new FlexibleRoleTestView(ax::mojom::Role::kGroup);
view->SetSize(GetWidgetBounds().size());
@@ -422,7 +422,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, NativeWindowProperties) {
// Tests for accessibility attributes on a views::Textfield.
// TODO(patricialor): Test against Cocoa-provided attributes as well to ensure
// consistency between Cocoa and toolkit-views.
-TEST_F(NativeWidgetMacAccessibilityTest, TextfieldGenericAttributes) {
+TEST_F(AXNativeWidgetMacTest, TextfieldGenericAttributes) {
Textfield* textfield = AddChildTextfield(GetWidgetBounds().size());
// NSAccessibilityEnabledAttribute.
@@ -480,7 +480,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldGenericAttributes) {
NSAccessibilitySizeAttribute) sizeValue]));
}
-TEST_F(NativeWidgetMacAccessibilityTest, TextfieldEditableAttributes) {
+TEST_F(AXNativeWidgetMacTest, TextfieldEditableAttributes) {
Textfield* textfield = AddChildTextfield(GetWidgetBounds().size());
textfield->set_placeholder_text(
base::SysNSStringToUTF16(kTestPlaceholderText));
@@ -539,7 +539,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldEditableAttributes) {
// Test writing accessibility attributes via an accessibility client for normal
// Views.
-TEST_F(NativeWidgetMacAccessibilityTest, ViewWritableAttributes) {
+TEST_F(AXNativeWidgetMacTest, ViewWritableAttributes) {
FlexibleRoleTestView* view =
new FlexibleRoleTestView(ax::mojom::Role::kGroup);
view->SetSize(GetWidgetBounds().size());
@@ -566,7 +566,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ViewWritableAttributes) {
// Test writing accessibility attributes via an accessibility client for
// editable controls (in this case, views::Textfields).
-TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) {
+TEST_F(AXNativeWidgetMacTest, TextfieldWritableAttributes) {
Textfield* textfield = AddChildTextfield(GetWidgetBounds().size());
// Get the Textfield accessibility object.
@@ -668,7 +668,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) {
}
// Test parameterized text attributes.
-TEST_F(NativeWidgetMacAccessibilityTest, TextParameterizedAttributes) {
+TEST_F(AXNativeWidgetMacTest, TextParameterizedAttributes) {
AddChildTextfield(GetWidgetBounds().size());
id ax_node = A11yElementAtMidpoint();
EXPECT_TRUE(ax_node);
@@ -723,7 +723,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextParameterizedAttributes) {
}
// Test performing a 'click' on Views with clickable roles work.
-TEST_F(NativeWidgetMacAccessibilityTest, PressAction) {
+TEST_F(AXNativeWidgetMacTest, PressAction) {
FlexibleRoleTestView* view =
new FlexibleRoleTestView(ax::mojom::Role::kButton);
widget()->GetContentsView()->AddChildView(view);
@@ -740,7 +740,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, PressAction) {
// Test text-specific attributes that should not be supported for protected
// textfields.
-TEST_F(NativeWidgetMacAccessibilityTest, ProtectedTextfields) {
+TEST_F(AXNativeWidgetMacTest, ProtectedTextfields) {
Textfield* textfield = AddChildTextfield(GetWidgetBounds().size());
textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
@@ -776,9 +776,8 @@ TEST_F(NativeWidgetMacAccessibilityTest, ProtectedTextfields) {
if (base::mac::IsAtLeastOS10_10()) {
// Check Cocoa's attribute values for PlaceHolder and Value here separately
// - these are using the new NSAccessibility protocol.
- EXPECT_TRUE([cocoa_secure_textfield
- isAccessibilitySelectorAllowed:@selector(
- accessibilityPlaceholderValue)]);
+ EXPECT_TRUE([cocoa_secure_textfield isAccessibilitySelectorAllowed:@selector
+ (accessibilityPlaceholderValue)]);
EXPECT_TRUE([cocoa_secure_textfield
isAccessibilitySelectorAllowed:@selector(accessibilityValue)]);
}
@@ -795,8 +794,9 @@ TEST_F(NativeWidgetMacAccessibilityTest, ProtectedTextfields) {
[ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]);
EXPECT_NSEQ(NSAccessibilityTextFieldRole, AXRoleString());
- NSString* kShownValue = @"•"
- @"••••••••••••••••";
+ NSString* kShownValue =
+ @"•"
+ @"••••••••••••••••";
// Sanity check.
EXPECT_EQ(kTestStringLength, static_cast<int>([kShownValue length]));
EXPECT_NSEQ(kShownValue, AXValue());
@@ -825,7 +825,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ProtectedTextfields) {
}
// Test text-specific attributes of Labels.
-TEST_F(NativeWidgetMacAccessibilityTest, Label) {
+TEST_F(AXNativeWidgetMacTest, Label) {
Label* label = new Label;
label->SetText(base::SysNSStringToUTF16(kTestStringValue));
label->SetSize(GetWidgetBounds().size());
@@ -872,7 +872,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, Label) {
}
// Labels used as title bars should be exposed as normal static text on Mac.
-TEST_F(NativeWidgetMacAccessibilityTest, LabelUsedAsTitleBar) {
+TEST_F(AXNativeWidgetMacTest, LabelUsedAsTitleBar) {
Label* label = new Label(base::SysNSStringToUTF16(kTestStringValue),
style::CONTEXT_DIALOG_TITLE, style::STYLE_PRIMARY);
label->SetSize(GetWidgetBounds().size());
@@ -902,7 +902,7 @@ class TestComboboxModel : public ui::ComboboxModel {
};
// Test a11y attributes of Comboboxes.
-TEST_F(NativeWidgetMacAccessibilityTest, Combobox) {
+TEST_F(AXNativeWidgetMacTest, Combobox) {
Combobox* combobox = new Combobox(std::make_unique<TestComboboxModel>());
combobox->SetSize(GetWidgetBounds().size());
widget()->GetContentsView()->AddChildView(combobox);
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 42c838cb065..b482b7781c4 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
@@ -8,7 +8,7 @@
#include "base/macros.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/drag_drop_client.h"
@@ -737,7 +737,6 @@ void DesktopNativeWidgetAura::Close() {
return;
content_window_->SuppressPaint();
- content_window_->Hide();
desktop_window_tree_host_->Close();
}
@@ -789,8 +788,20 @@ bool DesktopNativeWidgetAura::IsVisible() const {
}
void DesktopNativeWidgetAura::Activate() {
- if (content_window_)
+ if (content_window_) {
+ bool was_active = IsActive();
desktop_window_tree_host_->Activate();
+
+ // If the whole window tree host was already active,
+ // treat this as a request to focus |content_window_|.
+ //
+ // Note: it might make sense to always focus |content_window_|,
+ // since if client code is calling Widget::Activate() they probably
+ // want that particular widget to be activated, not just something
+ // within that widget hierarchy.
+ if (was_active && focus_client_->GetFocusedWindow() != content_window_)
+ focus_client_->FocusWindow(content_window_);
+ }
}
void DesktopNativeWidgetAura::Deactivate() {
@@ -858,6 +869,11 @@ void DesktopNativeWidgetAura::SetOpacity(float opacity) {
desktop_window_tree_host_->SetOpacity(opacity);
}
+void DesktopNativeWidgetAura::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ if (content_window_)
+ desktop_window_tree_host_->SetAspectRatio(aspect_ratio);
+}
+
void DesktopNativeWidgetAura::FlashFrame(bool flash_frame) {
if (content_window_)
desktop_window_tree_host_->FlashFrame(flash_frame);
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 be8fd0378c6..40d5b1bfb7e 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
@@ -164,6 +164,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
void FlashFrame(bool flash_frame) override;
void RunShellDrag(View* view,
const ui::OSExchangeData& data,
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 8fd5d379d34..e90710c2c39 100644
--- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -216,8 +216,13 @@ display::Display DesktopScreenX11::GetDisplayNearestWindow(
if (host) {
DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
host->GetAcceleratedWidget());
- if (rwh)
- return GetDisplayMatching(rwh->GetX11RootWindowBounds());
+ if (rwh) {
+ const float scale = 1.0f / GetDeviceScaleFactor();
+ const gfx::Rect pixel_rect = rwh->GetX11RootWindowBounds();
+ return GetDisplayMatching(
+ gfx::Rect(gfx::ScaleToFlooredPoint(pixel_rect.origin(), scale),
+ gfx::ScaleToCeiledSize(pixel_rect.size(), scale)));
+ }
}
return GetPrimaryDisplay();
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 25c923b7e75..546411eaf7e 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
@@ -9,7 +9,7 @@
#include <memory>
#include "base/macros.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
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 437a0294a4d..a6e2eca3abd 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
@@ -141,6 +141,8 @@ class VIEWS_EXPORT DesktopWindowTreeHost {
virtual void SetOpacity(float opacity) = 0;
+ virtual void SetAspectRatio(const gfx::SizeF& aspect_ratio) = 0;
+
virtual void SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) = 0;
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 60d2c66cca0..2081478b174 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
@@ -10,13 +10,48 @@
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_init_properties.h"
#include "ui/views/corewm/tooltip_aura.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/widget_aura_utils.h"
#include "ui/views/window/native_frame_view.h"
#include "ui/wm/core/window_util.h"
namespace views {
+namespace {
+
+ui::PlatformWindowInitProperties ConvertWidgetInitParamsToInitProperties(
+ const Widget::InitParams& params) {
+ ui::PlatformWindowInitProperties properties;
+
+ switch (params.type) {
+ case Widget::InitParams::TYPE_WINDOW:
+ properties.type = ui::PlatformWindowType::kWindow;
+ break;
+
+ case Widget::InitParams::TYPE_MENU:
+ properties.type = ui::PlatformWindowType::kMenu;
+ break;
+
+ case Widget::InitParams::TYPE_TOOLTIP:
+ properties.type = ui::PlatformWindowType::kTooltip;
+ break;
+
+ default:
+ properties.type = ui::PlatformWindowType::kPopup;
+ break;
+ }
+
+ properties.bounds = params.bounds;
+
+ if (params.parent && params.parent->GetHost())
+ properties.parent_widget = params.parent->GetHost()->GetAcceleratedWidget();
+
+ return properties;
+}
+
+} // namespace
////////////////////////////////////////////////////////////////////////////////
// DesktopWindowTreeHostPlatform:
@@ -41,7 +76,10 @@ void DesktopWindowTreeHostPlatform::SetBoundsInDIP(
}
void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) {
- CreateAndSetDefaultPlatformWindow();
+ ui::PlatformWindowInitProperties properties =
+ ConvertWidgetInitParamsToInitProperties(params);
+
+ CreateAndSetPlatformWindow(std::move(properties));
CreateCompositor(viz::FrameSinkId(), params.force_software_compositing);
aura::WindowTreeHost::OnAcceleratedWidgetAvailable();
InitHost();
@@ -76,6 +114,8 @@ void DesktopWindowTreeHostPlatform::Close() {
if (waiting_for_close_now_)
return;
+ desktop_native_widget_aura_->content_window()->Hide();
+
// Hide while waiting for the close.
// Please note that it's better to call WindowTreeHost::Hide, which also calls
// PlatformWindow::Hide and Compositor::SetVisible(false).
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 551bcb598b8..cac4832e63a 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
@@ -77,6 +77,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override {}
void SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) override;
void InitModalType(ui::ModalType modal_type) 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 288b92c3ead..9c6b588516f 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
@@ -21,6 +21,7 @@
#include "ui/display/win/screen_win.h"
#include "ui/events/keyboard_hook.h"
#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/native_widget_types.h"
@@ -173,6 +174,8 @@ DesktopWindowTreeHostWin::CreateDragDropClient(
}
void DesktopWindowTreeHostWin::Close() {
+ content_window()->Hide();
+
// TODO(beng): Move this entire branch to DNWA so it can be shared with X11.
if (should_animate_window_close_) {
pending_close_ = true;
@@ -446,6 +449,12 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) {
content_window()->layer()->SetOpacity(opacity);
}
+void DesktopWindowTreeHostWin::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ DCHECK(!aspect_ratio.IsEmpty());
+ message_handler_->SetAspectRatio(aspect_ratio.width() /
+ aspect_ratio.height());
+}
+
void DesktopWindowTreeHostWin::SetWindowIcons(
const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) {
message_handler_->SetWindowIcons(window_icon, app_icon);
@@ -587,8 +596,7 @@ bool DesktopWindowTreeHostWin::IsKeyLocked(ui::DomCode dom_code) {
base::flat_map<std::string, std::string>
DesktopWindowTreeHostWin::GetKeyboardLayoutMap() {
- NOTIMPLEMENTED();
- return {};
+ return ui::GenerateDomKeyboardLayoutMap();
}
void DesktopWindowTreeHostWin::SetCursorNative(gfx::NativeCursor cursor) {
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 327e4fb5df8..022de4037ff 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
@@ -110,6 +110,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
void SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) override;
void InitModalType(ui::ModalType modal_type) override;
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
index cbfbfcbdcdb..22a9914957a 100644
--- 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
@@ -40,7 +40,6 @@
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/null_event_targeter.h"
#include "ui/events/platform/platform_event_source.h"
-#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/image/image_skia.h"
@@ -451,6 +450,8 @@ DesktopWindowTreeHostX11::CreateDragDropClient(
}
void DesktopWindowTreeHostX11::Close() {
+ content_window()->Hide();
+
// TODO(erg): Might need to do additional hiding tasks here.
delayed_resize_task_.Cancel();
@@ -799,11 +800,10 @@ bool DesktopWindowTreeHostX11::IsActive() const {
}
void DesktopWindowTreeHostX11::Maximize() {
- if (ui::HasWMSpecProperty(window_properties_,
+ if (ui::HasWMSpecProperty(window_properties_in_server_,
gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"))) {
// Unfullscreen the window if it is fullscreen.
- ui::SetWMSpecState(xwindow_, false,
- gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None);
+ SetWMSpecState(false, gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None);
// Resize the window so that it does not have the same size as a monitor.
// (Otherwise, some window managers immediately put the window back in
@@ -823,9 +823,8 @@ void DesktopWindowTreeHostX11::Maximize() {
// heuristics that are in the PropertyNotify and ConfigureNotify handlers.
restored_bounds_in_pixels_ = bounds_in_pixels_;
- ui::SetWMSpecState(xwindow_, true,
- gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
- gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
+ SetWMSpecState(true, gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
+ gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
if (IsMinimized())
ShowWindowWithState(ui::SHOW_STATE_NORMAL);
}
@@ -837,22 +836,21 @@ void DesktopWindowTreeHostX11::Minimize() {
void DesktopWindowTreeHostX11::Restore() {
should_maximize_after_map_ = false;
- ui::SetWMSpecState(xwindow_, false,
- gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
- gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
+ SetWMSpecState(false, gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
+ gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
if (IsMinimized())
ShowWindowWithState(ui::SHOW_STATE_NORMAL);
}
bool DesktopWindowTreeHostX11::IsMaximized() const {
- return (ui::HasWMSpecProperty(window_properties_,
+ return (ui::HasWMSpecProperty(window_properties_in_server_,
gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT")) &&
- ui::HasWMSpecProperty(window_properties_,
+ ui::HasWMSpecProperty(window_properties_in_server_,
gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")));
}
bool DesktopWindowTreeHostX11::IsMinimized() const {
- return ui::HasWMSpecProperty(window_properties_,
+ return ui::HasWMSpecProperty(window_properties_in_server_,
gfx::GetAtom("_NET_WM_STATE_HIDDEN"));
}
@@ -862,8 +860,7 @@ bool DesktopWindowTreeHostX11::HasCapture() const {
void DesktopWindowTreeHostX11::SetAlwaysOnTop(bool always_on_top) {
is_always_on_top_ = always_on_top;
- ui::SetWMSpecState(xwindow_, always_on_top,
- gfx::GetAtom("_NET_WM_STATE_ABOVE"), x11::None);
+ SetWMSpecState(always_on_top, gfx::GetAtom("_NET_WM_STATE_ABOVE"), x11::None);
}
bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const {
@@ -878,8 +875,8 @@ void DesktopWindowTreeHostX11::SetVisible(bool visible) {
}
void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) {
- ui::SetWMSpecState(xwindow_, always_visible,
- gfx::GetAtom("_NET_WM_STATE_STICKY"), x11::None);
+ SetWMSpecState(always_visible, gfx::GetAtom("_NET_WM_STATE_STICKY"),
+ x11::None);
int new_desktop = 0;
if (always_visible) {
@@ -1018,8 +1015,8 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
if (unmaximize_and_remaximize)
Restore();
- ui::SetWMSpecState(xwindow_, fullscreen,
- gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None);
+ SetWMSpecState(fullscreen, gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"),
+ x11::None);
if (unmaximize_and_remaximize)
Maximize();
@@ -1039,7 +1036,7 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
OnHostMovedInPixels(bounds_in_pixels_.origin());
OnHostResizedInPixels(bounds_in_pixels_.size());
- if (ui::HasWMSpecProperty(window_properties_,
+ if (ui::HasWMSpecProperty(window_properties_in_server_,
gfx::GetAtom("_NET_WM_STATE_FULLSCREEN")) ==
fullscreen) {
Relayout();
@@ -1075,6 +1072,18 @@ void DesktopWindowTreeHostX11::SetOpacity(float opacity) {
}
}
+void DesktopWindowTreeHostX11::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ XSizeHints size_hints;
+ size_hints.flags = 0;
+ long supplied_return;
+
+ XGetWMNormalHints(xdisplay_, xwindow_, &size_hints, &supplied_return);
+ size_hints.flags |= PAspect;
+ size_hints.min_aspect.x = size_hints.max_aspect.x = aspect_ratio.width();
+ size_hints.min_aspect.y = size_hints.max_aspect.y = aspect_ratio.height();
+ XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
+}
+
void DesktopWindowTreeHostX11::SetWindowIcons(
const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) {
// TODO(erg): The way we handle icons across different versions of chrome
@@ -1542,6 +1551,8 @@ void DesktopWindowTreeHostX11::InitX11Window(
// SetWMSpecState) has no effect here since the window has not yet been
// mapped. So we manually change the state.
if (!state_atom_list.empty()) {
+ DCHECK(window_properties_in_client_.empty());
+ window_properties_in_client_ = state_atom_list;
ui::SetAtomArrayProperty(xwindow_,
"_NET_WM_STATE",
"ATOM",
@@ -1639,6 +1650,21 @@ gfx::Size DesktopWindowTreeHostX11::AdjustSize(
return size_in_pixels;
}
+void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled,
+ XAtom state1,
+ XAtom state2) {
+ if (IsVisible())
+ ui::SetWMSpecState(xwindow_, enabled, state1, state2);
+ for (XAtom atom : {state1, state2}) {
+ if (atom != x11::None) {
+ if (enabled)
+ window_properties_in_client_.insert(atom);
+ else
+ window_properties_in_client_.erase(atom);
+ }
+ }
+}
+
void DesktopWindowTreeHostX11::OnWMStateUpdated() {
std::vector< ::Atom> atom_list;
// Ignore the return value of gfx::GetAtomArrayProperty(). Fluxbox removes the
@@ -1648,9 +1674,10 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() {
bool was_minimized = IsMinimized();
bool was_maximized = IsMaximized();
- window_properties_.clear();
+ window_properties_in_server_.clear();
std::copy(atom_list.begin(), atom_list.end(),
- inserter(window_properties_, window_properties_.begin()));
+ inserter(window_properties_in_server_,
+ window_properties_in_server_.begin()));
bool is_minimized = IsMinimized();
bool is_maximized = IsMaximized();
@@ -1699,7 +1726,7 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() {
// do preprocessing before the x window's fullscreen state is toggled.
is_always_on_top_ = ui::HasWMSpecProperty(
- window_properties_, gfx::GetAtom("_NET_WM_STATE_ABOVE"));
+ window_properties_in_server_, gfx::GetAtom("_NET_WM_STATE_ABOVE"));
if (was_maximized != is_maximized)
OnMaximizedStateChanged();
@@ -1955,13 +1982,13 @@ void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) {
1);
}
- ui::X11EventSource* event_source = ui::X11EventSource::GetInstance();
- DCHECK(event_source);
-
UpdateMinAndMaxSize();
XMapWindow(xdisplay_, xwindow_);
window_mapped_in_client_ = true;
+
+ for (XAtom atom : window_properties_in_client_)
+ ui::SetWMSpecState(xwindow_, true, atom, x11::None);
}
void DesktopWindowTreeHostX11::SetWindowTransparency() {
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
index f424728b967..c5e6df7ce81 100644
--- 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
@@ -146,6 +146,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
void SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) override;
void InitModalType(ui::ModalType modal_type) override;
@@ -191,6 +192,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
private:
friend class DesktopWindowTreeHostX11HighDPITest;
+
// Initializes our X11 surface to draw on. This method performs all
// initialization related to talking to the X11 server.
void InitX11Window(const Widget::InitParams& params);
@@ -204,6 +206,11 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// fullscreen.
gfx::Size AdjustSize(const gfx::Size& requested_size);
+ // If mapped, sends a message to the window manager to enable or disable the
+ // states |state1| and |state2|. Otherwise, the states will be enabled or
+ // disabled on the next map.
+ void SetWMSpecState(bool enabled, XAtom state1, XAtom state2);
+
// Called when |xwindow_|'s _NET_WM_STATE property is updated.
void OnWMStateUpdated();
@@ -336,8 +343,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// _NET_WM_DESKTOP is unset.
base::Optional<int> workspace_;
- // The window manager state bits.
- base::flat_set<::Atom> window_properties_;
+ // The window manager state bits as indicated by the server. May be
+ // out-of-sync. May include bits set by non-Chrome apps.
+ base::flat_set<::Atom> window_properties_in_server_;
+
+ // The window manager state bits that Chrome has set.
+ base::flat_set<::Atom> window_properties_in_client_;
// Whether |xwindow_| was requested to be fullscreen via SetFullscreen().
bool is_fullscreen_;
diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter.cc b/chromium/ui/views/widget/desktop_aura/window_event_filter.cc
index 2d3170ca5cd..e2d9e3e8a6f 100644
--- a/chromium/ui/views/widget/desktop_aura/window_event_filter.cc
+++ b/chromium/ui/views/widget/desktop_aura/window_event_filter.cc
@@ -4,7 +4,7 @@
#include "ui/views/widget/desktop_aura/window_event_filter.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
diff --git a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc
index ccde928898a..3d5ecdc2cfe 100644
--- a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc
+++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc
@@ -4,7 +4,7 @@
#include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc
index 8c0bbd9db9b..0b3370a18ac 100644
--- a/chromium/ui/views/widget/native_widget_aura.cc
+++ b/chromium/ui/views/widget/native_widget_aura.cc
@@ -11,7 +11,6 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "services/ui/public/interfaces/window_manager.mojom.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/capture_client.h"
@@ -201,13 +200,15 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) {
}
// SetAlwaysOnTop before SetParent so that always-on-top container is used.
SetAlwaysOnTop(params.keep_on_top);
+
// Make sure we have a real |window_bounds|.
- if (parent && window_bounds == gfx::Rect()) {
- // If a parent is specified but no bounds are given,
- // use the origin of the parent's display so that the widget
- // will be added to the same display as the parent.
+ aura::Window* parent_or_context = parent ? parent : context;
+ if (parent_or_context && window_bounds == gfx::Rect()) {
+ // If a parent or context is specified but no bounds are given, use the
+ // origin of the display so that the widget will be added to the same
+ // display as the parent or context.
gfx::Rect bounds = display::Screen::GetScreen()
- ->GetDisplayNearestWindow(parent)
+ ->GetDisplayNearestWindow(parent_or_context)
.bounds();
window_bounds.set_origin(bounds.origin());
}
@@ -671,6 +672,16 @@ void NativeWidgetAura::SetOpacity(float opacity) {
window_->layer()->SetOpacity(opacity);
}
+void NativeWidgetAura::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ DCHECK(!aspect_ratio.IsEmpty());
+ if (window_) {
+ // aura::client::kAspectRatio is owned, which allows for passing in this
+ // raw pointer.
+ window_->SetProperty(aura::client::kAspectRatio,
+ new gfx::SizeF(aspect_ratio));
+ }
+}
+
void NativeWidgetAura::FlashFrame(bool flash) {
if (window_)
window_->SetProperty(aura::client::kDrawAttentionKey, flash);
@@ -1065,12 +1076,6 @@ void Widget::CloseAllSecondaryWidgets() {
#endif
}
-bool Widget::ConvertRect(const Widget* source,
- const Widget* target,
- gfx::Rect* rect) {
- return false;
-}
-
const ui::NativeTheme* Widget::GetNativeTheme() const {
#if defined(USE_X11)
const LinuxUI* linux_ui = LinuxUI::instance();
diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h
index 74bd738bcf9..b0276c0158b 100644
--- a/chromium/ui/views/widget/native_widget_aura.h
+++ b/chromium/ui/views/widget/native_widget_aura.h
@@ -123,6 +123,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate,
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
void FlashFrame(bool flash_frame) override;
void RunShellDrag(View* view,
const ui::OSExchangeData& data,
diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc
index e767025b6fc..b1ef3a6805f 100644
--- a/chromium/ui/views/widget/native_widget_aura_unittest.cc
+++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc
@@ -9,7 +9,7 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h
index c1d97e3ac54..d515251a082 100644
--- a/chromium/ui/views/widget/native_widget_mac.h
+++ b/chromium/ui/views/widget/native_widget_mac.h
@@ -51,6 +51,9 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
// from the bottom of the window.
virtual int SheetPositionY();
+ // Notifies that the widget starts to enter or exit fullscreen mode.
+ virtual void OnWindowFullscreenStateChange() {}
+
// internal::NativeWidgetPrivate:
void InitNativeWidget(const Widget::InitParams& params) override;
void OnWidgetInitDone() override;
@@ -113,6 +116,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
void SetFullscreen(bool fullscreen) override;
bool IsFullscreen() const override;
void SetOpacity(float opacity) override;
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
void FlashFrame(bool flash_frame) override;
void RunShellDrag(View* view,
const ui::OSExchangeData& data,
diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm
index c0e28737b88..3cf9af6b957 100644
--- a/chromium/ui/views/widget/native_widget_mac.mm
+++ b/chromium/ui/views/widget/native_widget_mac.mm
@@ -8,8 +8,7 @@
#include <utility>
-#include "base/command_line.h"
-#import "base/mac/bind_objc_block.h"
+#include "base/bind.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
@@ -17,7 +16,6 @@
#include "components/crash/core/common/crash_key.h"
#import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
#import "ui/base/cocoa/window_size_constants.h"
-#include "ui/base/ui_base_switches.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/font_list.h"
@@ -50,11 +48,6 @@
namespace views {
namespace {
-bool AreModalAnimationsEnabled() {
- return !base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableModalAnimations);
-}
-
NSInteger StyleMaskForParams(const Widget::InitParams& params) {
// If the Widget is modal, it will be displayed as a sheet. This works best if
// it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the
@@ -392,13 +385,17 @@ void NativeWidgetMac::Close() {
// sheet has finished animating, it will call sheetDidEnd: on the parent
// window's delegate. Note it still needs to be asynchronous, since code
// calling Widget::Close() doesn't expect things to be deleted upon return.
- [NSApp performSelector:@selector(endSheet:) withObject:window afterDelay:0];
+ // Ensure |window| is retained by a block. Note in some cases during
+ // teardown, [window sheetParent] may be nil.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(base::RetainBlock(^{
+ [NSApp endSheet:window];
+ })));
return;
}
// For other modal types, animate the close.
- if (bridge_->animate() && AreModalAnimationsEnabled() &&
- delegate_->IsModal()) {
+ if (bridge_->ShouldRunCustomAnimationFor(Widget::ANIMATE_HIDE)) {
[ViewsNSWindowCloseAnimator closeWindowWithAnimation:window];
return;
}
@@ -416,9 +413,10 @@ void NativeWidgetMac::Close() {
// Many tests assume that base::RunLoop().RunUntilIdle() is always sufficient
// to execute a close. However, in rare cases, -performSelector:..afterDelay:0
// does not do this. So post a regular task.
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindBlock(^{
- [window close];
- }));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(base::RetainBlock(^{
+ [window close];
+ })));
}
void NativeWidgetMac::CloseNow() {
@@ -557,6 +555,11 @@ void NativeWidgetMac::SetOpacity(float opacity) {
[GetNativeWindow() setAlphaValue:opacity];
}
+void NativeWidgetMac::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ [GetNativeWindow() setContentAspectRatio:NSMakeSize(aspect_ratio.width(),
+ aspect_ratio.height())];
+}
+
void NativeWidgetMac::FlashFrame(bool flash_frame) {
NOTIMPLEMENTED();
}
@@ -623,7 +626,7 @@ void NativeWidgetMac::EndMoveLoop() {
void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
if (bridge_)
- bridge_->set_animate(value);
+ bridge_->SetAnimationEnabled(value);
}
void NativeWidgetMac::SetVisibilityAnimationDuration(
@@ -633,7 +636,8 @@ void NativeWidgetMac::SetVisibilityAnimationDuration(
void NativeWidgetMac::SetVisibilityAnimationTransition(
Widget::VisibilityTransition transition) {
- NOTIMPLEMENTED();
+ if (bridge_)
+ bridge_->set_transitions_to_animate(transition);
}
bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
@@ -694,12 +698,6 @@ void Widget::CloseAllSecondaryWidgets() {
}
}
-bool Widget::ConvertRect(const Widget* source,
- const Widget* target,
- gfx::Rect* rect) {
- return false;
-}
-
const ui::NativeTheme* Widget::GetNativeTheme() const {
return ui::NativeTheme::GetInstanceForNativeUi();
}
diff --git a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm
index faa01ce577b..f6d68885a92 100644
--- a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm
+++ b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm
@@ -13,6 +13,7 @@
#import "ui/base/test/windowed_nsnotification_observer.h"
#import "ui/events/test/cocoa_test_event_utils.h"
#include "ui/views/bubble/bubble_dialog_delegate.h"
+#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/test/test_widget_observer.h"
#include "ui/views/test/views_interactive_ui_test_base.h"
#include "ui/views/test/widget_test.h"
@@ -282,6 +283,40 @@ TEST_F(NativeWidgetMacInteractiveUITest, BubbleDismiss) {
parent_widget->CloseNow();
}
+// Ensure BridgedContentView's inputContext can handle its window being torn
+// away mid-way through event processing. Toolkit-views guarantees to move focus
+// away from any Widget when the window is torn down. This test ensures that
+// global references AppKit may have held on to are also updated.
+TEST_F(NativeWidgetMacInteractiveUITest, GlobalNSTextInputContextUpdates) {
+ Widget* widget = CreateNativeDesktopWidget();
+ Textfield* textfield = new Textfield;
+ textfield->SetBounds(0, 0, 100, 100);
+ widget->GetContentsView()->AddChildView(textfield);
+ textfield->RequestFocus();
+ {
+ WidgetActivationWaiter wait_for_first_active(widget, true);
+ widget->Show();
+ wait_for_first_active.Wait();
+ }
+ EXPECT_TRUE([widget->GetNativeView() inputContext]);
+ EXPECT_EQ([widget->GetNativeView() inputContext],
+ [NSTextInputContext currentInputContext]);
+
+ widget->GetContentsView()->RemoveChildView(textfield);
+
+ // NSTextInputContext usually only updates at the end of an AppKit event loop
+ // iteration. We just tore out the inputContext, so ensure the raw, weak
+ // global pointer that AppKit likes to keep around has been updated manually.
+ EXPECT_EQ(nil, [NSTextInputContext currentInputContext]);
+ EXPECT_FALSE([widget->GetNativeView() inputContext]);
+
+ // RemoveChildView() doesn't delete the view.
+ delete textfield;
+
+ widget->Close();
+ base::RunLoop().RunUntilIdle();
+}
+
INSTANTIATE_TEST_CASE_P(NativeWidgetMacInteractiveUITestInstance,
NativeWidgetMacInteractiveUITest,
::testing::Bool());
diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm
index e8c6ebeeaa7..8fcb213c579 100644
--- a/chromium/ui/views/widget/native_widget_mac_unittest.mm
+++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm
@@ -23,6 +23,7 @@
#import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
#import "ui/base/cocoa/window_size_constants.h"
#import "ui/base/test/scoped_fake_full_keyboard_access.h"
+#include "ui/compositor/recyclable_compositor_mac.h"
#import "ui/events/test/cocoa_test_event_utils.h"
#include "ui/events/test/event_generator.h"
#import "ui/gfx/mac/coordinate_conversion.h"
@@ -59,9 +60,11 @@
@interface NativeWidgetMacTestWindow : NativeWidgetMacNSWindow {
@private
int invalidateShadowCount_;
+ int orderWindowCount_;
bool* deallocFlag_;
}
@property(readonly, nonatomic) int invalidateShadowCount;
+@property(readonly, nonatomic) int orderWindowCount;
@property(assign, nonatomic) bool* deallocFlag;
@end
@@ -102,7 +105,7 @@ class BridgedNativeWidgetTestApi {
const float kScaleFactor = 1.0f;
ui::CALayerFrameSink* ca_layer_frame_sink =
ui::CALayerFrameSink::FromAcceleratedWidget(
- bridge_->compositor_widget_->accelerated_widget());
+ bridge_->compositor_->widget()->accelerated_widget());
gfx::CALayerParams ca_layer_params;
ca_layer_params.is_empty = false;
ca_layer_params.pixel_size = size;
@@ -467,6 +470,44 @@ TEST_F(NativeWidgetMacTest, DISABLED_OrderFrontAfterMiniaturize) {
widget->Close();
}
+// Test that ShowInactive() on already-visible child widgets is ignored, since
+// it may cause a space transition. See https://crbug.com/866760.
+TEST_F(NativeWidgetMacTest, ShowInactiveOnChildWidget) {
+ NativeWidgetMacTestWindow* parent_window;
+ NativeWidgetMacTestWindow* child_window;
+
+ Widget::InitParams init_params =
+ CreateParams(Widget::InitParams::TYPE_WINDOW);
+ init_params.bounds = gfx::Rect(100, 100, 200, 200);
+ Widget* parent = CreateWidgetWithTestWindow(init_params, &parent_window);
+
+ // CreateWidgetWithTestWindow calls Show()
+ EXPECT_EQ(1, [parent_window orderWindowCount]);
+
+ init_params.parent = parent->GetNativeView();
+ Widget* child = CreateWidgetWithTestWindow(init_params, &child_window);
+
+ // The child is ordered twice, once by Show() and again (by AppKit) when it is
+ // registered as a child window.
+ EXPECT_EQ(2, [child_window orderWindowCount]);
+
+ // Parent is unchanged.
+ EXPECT_EQ(1, [parent_window orderWindowCount]);
+
+ // ShowInactive() on a visible regular window may serve to raise its stacking
+ // order without taking focus, so it should invoke -[NSWindow orderWindow:..].
+ parent->ShowInactive();
+ EXPECT_EQ(2, [parent_window orderWindowCount]); // Increases.
+
+ // However, ShowInactive() on the child should have no effect. It should
+ // already be in a correct stacking order and we must avoid a Space switch.
+ child->ShowInactive();
+ EXPECT_EQ(2, [child_window orderWindowCount]); // No change.
+ EXPECT_EQ(2, [parent_window orderWindowCount]); // Parent also unchanged.
+
+ parent->CloseNow();
+}
+
// Test minimized states triggered externally, implied visibility and restored
// bounds whilst minimized.
TEST_F(NativeWidgetMacTest, MiniaturizeExternally) {
@@ -861,7 +902,8 @@ TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) {
}
// Tests visibility for child of native NSWindow, reshowing after -[NSApp hide].
-TEST_F(NativeWidgetMacTest, VisibleAfterNativeParentShow) {
+// Occasionally flaky (maybe due to [NSApp hide]). See https://crbug.com/777247.
+TEST_F(NativeWidgetMacTest, DISABLED_VisibleAfterNativeParentShow) {
NSWindow* native_parent = MakeNativeParent();
Widget* child = AttachPopupToNativeParent(native_parent);
child->Show();
@@ -1216,12 +1258,24 @@ TEST_F(NativeWidgetMacTest, ShowAnimationControl) {
retained_animation.reset();
// Disable animations and show again.
- modal_dialog_widget->SetVisibilityChangedAnimationsEnabled(false);
+ modal_dialog_widget->SetVisibilityAnimationTransition(Widget::ANIMATE_NONE);
modal_dialog_widget->Show();
EXPECT_FALSE(test_api.show_animation()); // No animation this time.
modal_dialog_widget->Hide();
// Test after re-enabling.
+ modal_dialog_widget->SetVisibilityAnimationTransition(Widget::ANIMATE_BOTH);
+ modal_dialog_widget->Show();
+ EXPECT_TRUE(test_api.show_animation());
+ retained_animation.reset(test_api.show_animation(),
+ base::scoped_policy::RETAIN);
+
+ // Test whether disabling native animations also disables custom modal ones.
+ modal_dialog_widget->SetVisibilityChangedAnimationsEnabled(false);
+ modal_dialog_widget->Show();
+ EXPECT_FALSE(test_api.show_animation()); // No animation this time.
+ modal_dialog_widget->Hide();
+ // Renable.
modal_dialog_widget->SetVisibilityChangedAnimationsEnabled(true);
modal_dialog_widget->Show();
EXPECT_TRUE(test_api.show_animation());
@@ -1353,6 +1407,7 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) {
TEST_F(NativeWidgetMacTest, CloseWithWindowModalSheet) {
NSWindow* native_parent =
MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask);
+
{
Widget* sheet_widget = ShowWindowModalWidget(native_parent);
EXPECT_TRUE([sheet_widget->GetNativeWindow() isVisible]);
@@ -1383,17 +1438,112 @@ TEST_F(NativeWidgetMacTest, CloseWithWindowModalSheet) {
base::RunLoop().RunUntilIdle();
}
+ // Similar, but invoke -[NSWindow close] immediately after an asynchronous
+ // Close(). This exercises a scenario where two tasks to end the sheet may be
+ // posted. Experimentally (on 10.13) both tasks run, but the second will never
+ // attempt to invoke -didEndSheet: on the |modalDelegate| arg of -beginSheet:.
+ // (If it did, it would be fine.)
+ {
+ Widget* sheet_widget = ShowWindowModalWidget(native_parent);
+ base::scoped_nsobject<NSWindow> sheet_window(
+ sheet_widget->GetNativeWindow(), base::scoped_policy::RETAIN);
+ EXPECT_TRUE([sheet_window isVisible]);
+
+ WidgetChangeObserver widget_observer(sheet_widget);
+ sheet_widget->Close(); // Asynchronous. Can't be called after -close.
+ EXPECT_FALSE(widget_observer.widget_closed());
+ [sheet_window close];
+ EXPECT_TRUE(widget_observer.widget_closed());
+ base::RunLoop().RunUntilIdle();
+
+ // Pretend both tasks ran fully. Note that |sheet_window| serves as its own
+ // |modalDelegate|.
+ [base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>(sheet_window)
+ sheetDidEnd:sheet_window
+ returnCode:NSModalResponseStop
+ contextInfo:nullptr];
+ }
+
+ // Test another hypothetical: What if -sheetDidEnd: was invoked somehow
+ // without going through [NSApp endSheet:] or -[NSWindow endSheet:].
+ {
+ base::mac::ScopedNSAutoreleasePool pool;
+ Widget* sheet_widget = ShowWindowModalWidget(native_parent);
+ NSWindow* sheet_window = sheet_widget->GetNativeWindow();
+ EXPECT_TRUE([sheet_window isVisible]);
+
+ WidgetChangeObserver widget_observer(sheet_widget);
+ sheet_widget->Close();
+
+ [base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>(sheet_window)
+ sheetDidEnd:sheet_window
+ returnCode:NSModalResponseStop
+ contextInfo:nullptr];
+
+ EXPECT_TRUE(widget_observer.widget_closed());
+ // Here, the ViewsNSWindowDelegate should be dealloc'd.
+ }
+ base::RunLoop().RunUntilIdle(); // Run the task posted in Close().
+
+ // Test -[NSWindow close] on the parent window.
{
Widget* sheet_widget = ShowWindowModalWidget(native_parent);
EXPECT_TRUE([sheet_widget->GetNativeWindow() isVisible]);
WidgetChangeObserver widget_observer(sheet_widget);
- // Test -[NSWindow close] on the parent window.
[native_parent close];
EXPECT_TRUE(widget_observer.widget_closed());
}
}
+// Exercise a scenario where the task posted in the asynchronous Close() could
+// eventually complete on a destroyed NSWindowDelegate. Regression test for
+// https://crbug.com/851376.
+TEST_F(NativeWidgetMacTest, CloseWindowModalSheetWithoutSheetParent) {
+ NSWindow* native_parent =
+ MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask);
+ {
+ base::mac::ScopedNSAutoreleasePool pool;
+ Widget* sheet_widget = ShowWindowModalWidget(native_parent);
+ NSWindow* sheet_window = sheet_widget->GetNativeWindow();
+ EXPECT_TRUE([sheet_window isVisible]);
+
+ sheet_widget->Close(); // Asynchronous. Can't be called after -close.
+
+ // Now there's a task to end the sheet in the message queue. But destroying
+ // the NSWindowDelegate without _also_ posting a task that will _retain_ it
+ // is hard. It _is_ possible for a -performSelector:afterDelay: already in
+ // the queue to happen _after_ a PostTask posted now, but it's a very rare
+ // occurrence. So to simulate it, we pretend the sheet isn't actually a
+ // sheet by hiding its sheetParent. This avoids a task being posted that
+ // would retain the delegate, but also puts |native_parent| into a weird
+ // state.
+ //
+ // In fact, the "real" suspected trigger for this bug requires the PostTask
+ // to still be posted, then run to completion, and to dealloc the delegate
+ // it retains all before the -performSelector:afterDelay runs. That's the
+ // theory anyway.
+ //
+ // In reality, it didn't seem possible for -sheetDidEnd: to be invoked twice
+ // (AppKit would suppress it on subsequent calls to -[NSApp endSheet:] or
+ // -[NSWindow endSheet:]), so if the PostTask were to run to completion, the
+ // waiting -performSelector would always no- op. So this is actually testing
+ // a hypothetical where the sheetParent may be somehow nil during teardown
+ // (perhaps due to the parent window being further along in its teardown).
+ EXPECT_TRUE([sheet_window sheetParent]);
+ [sheet_window setValue:nil forKey:@"sheetParent"];
+ EXPECT_FALSE([sheet_window sheetParent]);
+ [sheet_window close];
+
+ // To repro the crash, we need a dealloc to occur here on |sheet_widget|'s
+ // NSWindowDelegate.
+ }
+ // Now there is still a task to end the sheet in the message queue, which
+ // should not crash.
+ base::RunLoop().RunUntilIdle();
+ [native_parent close];
+}
+
// Test calls to Widget::ReparentNativeView() that result in a no-op on Mac.
// Tests with both native and non-native parents.
TEST_F(NativeWidgetMacTest, NoopReparentNativeView) {
@@ -2225,6 +2375,7 @@ TEST_F(NativeWidgetMacTest, TouchBar) {
@implementation NativeWidgetMacTestWindow
@synthesize invalidateShadowCount = invalidateShadowCount_;
+@synthesize orderWindowCount = orderWindowCount_;
@synthesize deallocFlag = deallocFlag_;
- (void)dealloc {
@@ -2240,6 +2391,12 @@ TEST_F(NativeWidgetMacTest, TouchBar) {
[super invalidateShadow];
}
+- (void)orderWindow:(NSWindowOrderingMode)orderingMode
+ relativeTo:(NSInteger)otherWindowNumber {
+ ++orderWindowCount_;
+ [super orderWindow:orderingMode relativeTo:otherWindowNumber];
+}
+
@end
@implementation MockBridgedView
diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h
index 226dcba1511..53c60d77ace 100644
--- a/chromium/ui/views/widget/native_widget_private.h
+++ b/chromium/ui/views/widget/native_widget_private.h
@@ -206,6 +206,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget {
virtual void SetFullscreen(bool fullscreen) = 0;
virtual bool IsFullscreen() const = 0;
virtual void SetOpacity(float opacity) = 0;
+ virtual void SetAspectRatio(const gfx::SizeF& aspect_ratio) = 0;
virtual void FlashFrame(bool flash) = 0;
virtual void RunShellDrag(View* view,
const ui::OSExchangeData& data,
diff --git a/chromium/ui/views/widget/util_mac.h b/chromium/ui/views/widget/util_mac.h
new file mode 100644
index 00000000000..604c693214b
--- /dev/null
+++ b/chromium/ui/views/widget/util_mac.h
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_WIDGET_UTIL_MAC_H_
+#define UI_VIEWS_WIDGET_UTIL_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/foundation_util.h"
+
+// Weak lets Chrome launch even if a future macOS doesn't have NSThemeFrame.
+WEAK_IMPORT_ATTRIBUTE
+@interface NSThemeFrame : NSView
+@end
+
+#endif // UI_VIEWS_WIDGET_UTIL_MAC_H_
diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc
index bf4b1d07f7f..e4e7fc6ce96 100644
--- a/chromium/ui/views/widget/widget.cc
+++ b/chromium/ui/views/widget/widget.cc
@@ -718,6 +718,10 @@ void Widget::SetOpacity(float opacity) {
native_widget_->SetOpacity(opacity);
}
+void Widget::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
+ native_widget_->SetAspectRatio(aspect_ratio);
+}
+
void Widget::FlashFrame(bool flash) {
native_widget_->FlashFrame(flash);
}
@@ -830,6 +834,7 @@ void Widget::UpdateWindowTitle() {
base::i18n::AdjustStringForLocaleDirection(&window_title);
if (!native_widget_->SetWindowTitle(window_title))
return;
+
non_client_view_->UpdateWindowTitle();
// If the non-client view is rendering its own title, it'll need to relayout
@@ -995,7 +1000,8 @@ bool Widget::IsTranslucentWindowOpacitySupported() const {
void Widget::OnSizeConstraintsChanged() {
native_widget_->OnSizeConstraintsChanged();
- non_client_view_->SizeConstraintsChanged();
+ if (non_client_view_)
+ non_client_view_->SizeConstraintsChanged();
}
void Widget::OnOwnerClosing() {}
diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h
index bc3b94fa4f3..6862cfb45d0 100644
--- a/chromium/ui/views/widget/widget.h
+++ b/chromium/ui/views/widget/widget.h
@@ -327,14 +327,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// during application shutdown when the last non-secondary widget is closed.
static void CloseAllSecondaryWidgets();
- // Converts a rectangle from one Widget's coordinate system to another's.
- // Returns false if the conversion couldn't be made, because either these two
- // Widgets do not have a common ancestor or they are not on the screen yet.
- // The value of |*rect| won't be changed when false is returned.
- static bool ConvertRect(const Widget* source,
- const Widget* target,
- gfx::Rect* rect);
-
// Retrieves the Widget implementation associated with the given
// NativeView or Window, or NULL if the supplied handle has no associated
// Widget.
@@ -428,6 +420,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// fit the entire size of the RootView. The RootView takes ownership of this
// View, unless it is set as not being parent-owned.
void SetContentsView(View* view);
+
+ // NOTE: This may not be the same view as WidgetDelegate::GetContentsView().
+ // See RootView::GetContentsView().
View* GetContentsView();
// Returns the bounds of the Widget in screen coordinates.
@@ -552,6 +547,12 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
// underlying windowing system.
void SetOpacity(float opacity);
+ // Sets the aspect ratio of the widget's content, which will be maintained
+ // during interactive resizing. This size disregards title bar and borders.
+ // Once set, some platforms ensure the content will only size to integer
+ // multiples of |aspect_ratio|.
+ void SetAspectRatio(const gfx::SizeF& aspect_ratio);
+
// Flashes the frame of the window to draw attention to it. Currently only
// implemented on Windows for non-Aura.
void FlashFrame(bool flash);
diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc
index ea1c78ff1d6..f1a5004ec93 100644
--- a/chromium/ui/views/widget/widget_delegate.cc
+++ b/chromium/ui/views/widget/widget_delegate.cc
@@ -6,7 +6,7 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/view.h"
#include "ui/views/views_delegate.h"
diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc
index 15a9ad10398..54744d6d8ed 100644
--- a/chromium/ui/views/widget/widget_interactive_uitest.cc
+++ b/chromium/ui/views/widget/widget_interactive_uitest.cc
@@ -16,7 +16,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/core/embedder/embedder.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/resource/resource_bundle.h"
@@ -281,7 +281,7 @@ class WidgetTestInteractive : public WidgetTest {
// Mojo is initialized here similar to how each browser test case
// initializes Mojo when starting. This only works because each
// interactive_ui_test runs in a new process.
- mojo::edk::Init();
+ mojo::core::Init();
gl::GLSurfaceTestSupport::InitializeOneOff();
ui::RegisterPathProvider();
@@ -621,6 +621,10 @@ TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
// Tests mouse move outside of the window into the "resize controller" and back
// will still generate an OnMouseEntered and OnMouseExited event..
TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
+ // TODO(http://crbug.com/864787): Crashes flakily in mus with ws2.
+ if (IsMus())
+ return;
+
Widget* toplevel = CreateTopLevelPlatformWidget();
toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
@@ -1337,6 +1341,10 @@ TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
// Test that window state is not changed after getting out of full screen.
TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
+ // TODO(http://crbug.com/864618): Fails flakily in mus with ws2.
+ if (IsMus())
+ return;
+
Widget* toplevel = CreateTopLevelPlatformWidget();
toplevel->Show();
@@ -1898,7 +1906,13 @@ class WidgetInputMethodInteractiveTest : public WidgetTestInteractive {
};
// Test input method focus changes affected by top window activaction.
-TEST_F(WidgetInputMethodInteractiveTest, Activation) {
+TEST_F(WidgetInputMethodInteractiveTest,
+#if defined(OS_MACOSX)
+ DISABLED_Activation
+#else
+ Activation
+#endif
+ ) {
if (IsMus())
return;
diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc
index 8e298d2f647..3c77de47e4e 100644
--- a/chromium/ui/views/widget/widget_unittest.cc
+++ b/chromium/ui/views/widget/widget_unittest.cc
@@ -741,6 +741,10 @@ class WidgetObserverTest : public WidgetTest, public WidgetObserver {
#endif
TEST_F(WidgetObserverTest, MAYBE_ActivationChange) {
+ // TODO(http://crbug.com/864800): Fails flakily in mus with ws2.
+ if (IsMus())
+ return;
+
WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
WidgetAutoclosePtr toplevel1(NewWidget());
WidgetAutoclosePtr toplevel2(NewWidget());
@@ -949,6 +953,65 @@ TEST_F(WidgetObserverTest, WidgetBoundsChangedNative) {
EXPECT_FALSE(widget_bounds_changed());
}
+namespace {
+
+class MoveTrackingTestDesktopWidgetDelegate : public TestDesktopWidgetDelegate {
+ public:
+ int move_count() const { return move_count_; }
+
+ // WidgetDelegate:
+ void OnWidgetMove() override { ++move_count_; }
+
+ private:
+ int move_count_ = 0;
+};
+
+} // namespace
+
+// An extension to the WidgetBoundsChangedNative test above to ensure move
+// notifications propagate to the WidgetDelegate.
+TEST_F(WidgetObserverTest, OnWidgetMovedWhenOriginChangesNative) {
+ MoveTrackingTestDesktopWidgetDelegate delegate;
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ delegate.InitWidget(params);
+ Widget* widget = delegate.GetWidget();
+ widget->Show();
+ widget->SetBounds(gfx::Rect(100, 100, 300, 200));
+
+ const int moves_during_init = delegate.move_count();
+
+#if defined(OS_WIN)
+ // Windows reliably notifies twice per origin change. https://crbug.com/864938
+ constexpr int kDeltaPerMove = 2;
+#else
+ constexpr int kDeltaPerMove = 1;
+#endif
+
+ // Resize without changing origin. No move.
+ widget->SetBounds(gfx::Rect(100, 100, 310, 210));
+ EXPECT_EQ(moves_during_init, delegate.move_count());
+
+ // Move without changing size. Moves.
+ widget->SetBounds(gfx::Rect(110, 110, 310, 210));
+ EXPECT_EQ(moves_during_init + kDeltaPerMove, delegate.move_count());
+
+ // Changing both moves.
+ widget->SetBounds(gfx::Rect(90, 90, 330, 230));
+ EXPECT_EQ(moves_during_init + 2 * kDeltaPerMove, delegate.move_count());
+
+ // Just grow vertically. On Mac, this changes the AppKit origin since it is
+ // from the bottom left of the screen, but there is no move as far as views is
+ // concerned.
+ widget->SetBounds(gfx::Rect(90, 90, 330, 240));
+ // No change.
+ EXPECT_EQ(moves_during_init + 2 * kDeltaPerMove, delegate.move_count());
+
+ // For a similar reason, move the widget down by the same amount that it grows
+ // vertically. The AppKit origin does not change, but it is a move.
+ widget->SetBounds(gfx::Rect(90, 100, 330, 250));
+ EXPECT_EQ(moves_during_init + 3 * kDeltaPerMove, delegate.move_count());
+}
+
// Test correct behavior when widgets close themselves in response to visibility
// changes.
TEST_F(WidgetObserverTest, ClosingOnHiddenParent) {
@@ -2235,6 +2298,14 @@ TEST_F(WidgetTest, NoCrashOnWidgetDelete) {
widget->Init(params);
}
+TEST_F(WidgetTest, NoCrashOnResizeConstraintsWindowTitleOnPopup) {
+ std::unique_ptr<Widget> widget(new Widget);
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+ widget->OnSizeConstraintsChanged();
+}
+
// Tests that we do not crash when a Widget is destroyed before it finishes
// processing of pending input events in the message loop.
TEST_F(WidgetTest, NoCrashOnWidgetDeleteWithPendingEvents) {
diff --git a/chromium/ui/views/win/DEPS b/chromium/ui/views/win/DEPS
index 5386933cab8..572e8b045ab 100644
--- a/chromium/ui/views/win/DEPS
+++ b/chromium/ui/views/win/DEPS
@@ -21,4 +21,5 @@ include_rules = [
"+ui/views/views_export.h",
"+ui/views/widget/widget_hwnd_utils.h",
"+ui/views/win",
+ "+ui/views/window/window_resize_utils.h",
]
diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc
index cadeb232262..d5f442f71da 100644
--- a/chromium/ui/views/win/hwnd_message_handler.cc
+++ b/chromium/ui/views/win/hwnd_message_handler.cc
@@ -245,6 +245,31 @@ bool IsHitTestOnResizeHandle(LRESULT hittest) {
hittest == HTBOTTOMLEFT || hittest == HTBOTTOMRIGHT;
}
+// Convert |param| to the HitTest used in WindowResizeUtils.
+HitTest GetWindowResizeHitTest(UINT param) {
+ switch (param) {
+ case WMSZ_BOTTOM:
+ return HitTest::kBottom;
+ case WMSZ_TOP:
+ return HitTest::kTop;
+ case WMSZ_LEFT:
+ return HitTest::kLeft;
+ case WMSZ_RIGHT:
+ return HitTest::kRight;
+ case WMSZ_TOPLEFT:
+ return HitTest::kTopLeft;
+ case WMSZ_TOPRIGHT:
+ return HitTest::kTopRight;
+ case WMSZ_BOTTOMLEFT:
+ return HitTest::kBottomLeft;
+ case WMSZ_BOTTOMRIGHT:
+ return HitTest::kBottomRight;
+ default:
+ NOTREACHED();
+ return HitTest::kBottomRight;
+ }
+}
+
const int kTouchDownContextResetTimeout = 500;
// Windows does not flag synthesized mouse messages from touch or pen in all
@@ -868,6 +893,23 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen) {
PerformDwmTransition();
}
+void HWNDMessageHandler::SetAspectRatio(float aspect_ratio) {
+ // If the aspect ratio is not in the valid range, do nothing.
+ DCHECK_GT(aspect_ratio, 0.0f);
+
+ aspect_ratio_ = aspect_ratio;
+
+ // When the aspect ratio is set, size the window to adhere to it. This keeps
+ // the same origin point as the original window.
+ RECT window_rect;
+ if (GetWindowRect(hwnd(), &window_rect)) {
+ gfx::Rect rect(window_rect);
+
+ SizeRectToAspectRatio(WMSZ_BOTTOMRIGHT, &rect);
+ SetBoundsInternal(rect, false);
+ }
+}
+
void HWNDMessageHandler::SizeConstraintsChanged() {
LONG style = GetWindowLong(hwnd(), GWL_STYLE);
// Ignore if this is not a standard window.
@@ -987,7 +1029,7 @@ void HWNDMessageHandler::OnInputMethodDestroyed(
DestroyAXSystemCaret();
}
-void HWNDMessageHandler::OnShowImeIfNeeded() {}
+void HWNDMessageHandler::OnShowVirtualKeyboardIfEnabled() {}
LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message,
WPARAM w_param,
@@ -1599,18 +1641,26 @@ LRESULT HWNDMessageHandler::OnDpiChanged(UINT msg,
if (LOWORD(w_param) != HIWORD(w_param))
NOTIMPLEMENTED() << "Received non-square scaling factors";
+ int dpi;
+ float scaling_factor;
+ if (display::Display::HasForceDeviceScaleFactor()) {
+ scaling_factor = display::Display::GetForcedDeviceScaleFactor();
+ dpi = display::win::GetDPIFromScalingFactor(scaling_factor);
+ } else {
+ dpi = LOWORD(w_param);
+ scaling_factor = display::win::GetScalingFactorFromDPI(dpi_);
+ }
+
// The first WM_DPICHANGED originates from EnableChildWindowDpiMessage during
// initialization. We don't want to propagate this as the client is already
// set at the current scale factor and may cause the window to display too
// soon. See http://crbug.com/625076.
- int dpi = LOWORD(w_param);
if (dpi_ == dpi)
return 0;
dpi_ = dpi;
SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false);
- delegate_->HandleWindowScaleFactorChanged(
- display::win::GetScalingFactorFromDPI(dpi_));
+ delegate_->HandleWindowScaleFactorChanged(scaling_factor);
return 0;
}
@@ -2329,6 +2379,19 @@ void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) {
ResetWindowRegion(false, true);
}
+void HWNDMessageHandler::OnSizing(UINT param, RECT* rect) {
+ // If the aspect ratio was not specified for the window, do nothing.
+ if (!aspect_ratio_.has_value())
+ return;
+
+ gfx::Rect window_rect(*rect);
+ SizeRectToAspectRatio(param, &window_rect);
+
+ // TODO(apacible): Account for window borders as part of the aspect ratio.
+ // https://crbug/869487.
+ *rect = window_rect.ToRECT();
+}
+
void HWNDMessageHandler::OnSysCommand(UINT notification_code,
const gfx::Point& point) {
// Windows uses the 4 lower order bits of |notification_code| for type-
@@ -2912,17 +2975,21 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message,
ui::GetModifiersFromKeyState(), rotation_angle);
event.latency()->AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, event_time, 1);
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1);
// There are cases where the code handling the message destroys the
// window, so use the weak ptr to check if destruction occurred or not.
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleTouchEvent(&event);
- if (event_type == ui::ET_TOUCH_RELEASED)
- id_generator_.ReleaseNumber(pointer_id);
- if (ref)
+ if (ref) {
+ // Release the pointer id only when |HWNDMessageHandler| and |id_generator_|
+ // are not destroyed.
+ if (event_type == ui::ET_TOUCH_RELEASED)
+ id_generator_.ReleaseNumber(pointer_id);
+
SetMsgHandled(event.handled());
+ }
return 0;
}
@@ -3031,10 +3098,7 @@ void HWNDMessageHandler::GenerateTouchEvent(ui::EventType event_type,
event.set_flags(ui::GetModifiersFromKeyState());
event.latency()->AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
- 0,
- time_stamp,
- 1);
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp, 1);
touch_events->push_back(event);
}
@@ -3184,4 +3248,18 @@ void HWNDMessageHandler::DestroyAXSystemCaret() {
ax_system_caret_ = nullptr;
}
+void HWNDMessageHandler::SizeRectToAspectRatio(UINT param,
+ gfx::Rect* window_rect) {
+ gfx::Size min_window_size;
+ gfx::Size max_window_size;
+ delegate_->GetMinMaxSize(&min_window_size, &max_window_size);
+ WindowResizeUtils::SizeMinMaxToAspectRatio(
+ aspect_ratio_.value(), &min_window_size, &max_window_size);
+ min_window_size = delegate_->DIPToScreenSize(min_window_size);
+ max_window_size = delegate_->DIPToScreenSize(max_window_size);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ GetWindowResizeHitTest(param), aspect_ratio_.value(), min_window_size,
+ max_window_size, window_rect);
+}
+
} // namespace views
diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h
index 5afb32aa043..91afedec8e2 100644
--- a/chromium/ui/views/win/hwnd_message_handler.h
+++ b/chromium/ui/views/win/hwnd_message_handler.h
@@ -31,6 +31,7 @@
#include "ui/gfx/win/window_impl.h"
#include "ui/views/views_export.h"
#include "ui/views/win/pen_event_processor.h"
+#include "ui/views/window/window_resize_utils.h"
namespace gfx {
class ImageSkia;
@@ -161,6 +162,9 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
void SetFullscreen(bool fullscreen);
+ // Updates the aspect ratio of the window.
+ void SetAspectRatio(float aspect_ratio);
+
// Updates the window style to reflect whether it can be resized or maximized.
void SizeConstraintsChanged();
@@ -188,7 +192,7 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
void OnTextInputStateChanged(const ui::TextInputClient* client) override;
void OnInputMethodDestroyed(const ui::InputMethod* input_method) override;
- void OnShowImeIfNeeded() override;
+ void OnShowVirtualKeyboardIfEnabled() override;
// Overridden from WindowEventTarget
LRESULT HandleMouseMessage(unsigned int message,
@@ -408,6 +412,7 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
CR_MSG_WM_SETTEXT(OnSetText)
CR_MSG_WM_SETTINGCHANGE(OnSettingChange)
CR_MSG_WM_SIZE(OnSize)
+ CR_MSG_WM_SIZING(OnSizing)
CR_MSG_WM_SYSCOMMAND(OnSysCommand)
CR_MSG_WM_THEMECHANGED(OnThemeChanged)
CR_MSG_WM_TIMECHANGE(OnTimeChange)
@@ -467,6 +472,7 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
LRESULT OnSetText(const wchar_t* text);
void OnSettingChange(UINT flags, const wchar_t* section);
void OnSize(UINT param, const gfx::Size& size);
+ void OnSizing(UINT param, RECT* rect);
void OnSysCommand(UINT notification_code, const gfx::Point& point);
void OnThemeChanged();
void OnTimeChange();
@@ -560,6 +566,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
// if they request its location.
void DestroyAXSystemCaret();
+ // Updates |rect| to adhere to the |aspect_ratio| of the window. |param|
+ // refers to the edge of the window being sized.
+ void SizeRectToAspectRatio(UINT param, gfx::Rect* rect);
+
HWNDMessageHandlerDelegate* delegate_;
std::unique_ptr<FullscreenHandler> fullscreen_handler_;
@@ -586,6 +596,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
// The icon created from the bitmap image of the app icon.
base::win::ScopedHICON app_icon_;
+ // The aspect ratio for the window. This is only used for sizing operations
+ // for the non-client area.
+ base::Optional<float> aspect_ratio_;
+
// The current DPI.
int dpi_;
diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc
index 95089ddb7fa..b5e70938413 100644
--- a/chromium/ui/views/win/pen_event_processor.cc
+++ b/chromium/ui/views/win/pen_event_processor.cc
@@ -41,11 +41,17 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateEvent(
// the WM_POINTER message and then setting up an associated pointer
// details in the MouseEvent which contains the pen's information.
ui::EventPointerType input_type = ui::EventPointerType::POINTER_TYPE_PEN;
- // TODO(lanwei): penFlags of PEN_FLAG_INVERTED may also indicate we are using
- // an eraser, but it is under debate. Please see
- // https://github.com/w3c/pointerevents/issues/134/.
- if (pointer_pen_info.penFlags & PEN_FLAG_ERASER)
+ // For the pointerup event, the penFlags is not set to PEN_FLAG_ERASER, so we
+ // have to check if previously the pointer type is an eraser.
+ if (pointer_pen_info.penFlags & PEN_FLAG_ERASER) {
input_type = ui::EventPointerType::POINTER_TYPE_ERASER;
+ DCHECK(eraser_pointer_id_ == -1 || eraser_pointer_id_ == mapped_pointer_id);
+ eraser_pointer_id_ = mapped_pointer_id;
+ } else if (eraser_pointer_id_ == mapped_pointer_id &&
+ message == WM_POINTERUP) {
+ input_type = ui::EventPointerType::POINTER_TYPE_ERASER;
+ eraser_pointer_id_ = -1;
+ }
// convert pressure into a float [0, 1]. The range of the pressure is
// [0, 1024] as specified on MSDN.
@@ -188,7 +194,7 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateTouchEvent(
flags | ui::GetModifiersFromKeyState(), rotation_angle);
event->set_hovering(event_type == ui::ET_TOUCH_RELEASED);
event->latency()->AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, event_time, 1);
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1);
return event;
}
diff --git a/chromium/ui/views/win/pen_event_processor.h b/chromium/ui/views/win/pen_event_processor.h
index 60d55489d5e..87d949c01bf 100644
--- a/chromium/ui/views/win/pen_event_processor.h
+++ b/chromium/ui/views/win/pen_event_processor.h
@@ -54,6 +54,7 @@ class VIEWS_EXPORT PenEventProcessor {
bool send_touch_for_pen_ = false;
bool sent_mouse_down_ = false;
bool sent_touch_start_ = false;
+ int eraser_pointer_id_ = -1;
DISALLOW_COPY_AND_ASSIGN(PenEventProcessor);
};
diff --git a/chromium/ui/views/win/pen_event_processor_unittest.cc b/chromium/ui/views/win/pen_event_processor_unittest.cc
index 1fa9319f1c9..d7fbaf2c7fa 100644
--- a/chromium/ui/views/win/pen_event_processor_unittest.cc
+++ b/chromium/ui/views/win/pen_event_processor_unittest.cc
@@ -227,4 +227,37 @@ TEST(PenProcessorTest, MouseFlagDMEnabled) {
event->AsMouseEvent()->changed_button_flags());
}
+TEST(PenProcessorTest, PenEraserFlagDMEnabled) {
+ ui::SequentialIDGenerator id_generator(0);
+ PenEventProcessor processor(&id_generator,
+ /*direct_manipulation_enabled*/ true);
+
+ POINTER_PEN_INFO pen_info;
+ memset(&pen_info, 0, sizeof(POINTER_PEN_INFO));
+ gfx::Point point(100, 100);
+
+ pen_info.pointerInfo.pointerFlags =
+ POINTER_FLAG_INCONTACT | POINTER_FLAG_FIRSTBUTTON;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN;
+ pen_info.penFlags = PEN_FLAG_ERASER;
+
+ std::unique_ptr<ui::Event> event =
+ processor.GenerateEvent(WM_POINTERDOWN, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsTouchEvent());
+ EXPECT_EQ(ui::ET_TOUCH_PRESSED, event->AsTouchEvent()->type());
+ EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_ERASER,
+ event->AsTouchEvent()->pointer_details().pointer_type);
+
+ pen_info.pointerInfo.pointerFlags = POINTER_FLAG_UP;
+ pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP;
+
+ event = processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->IsTouchEvent());
+ EXPECT_EQ(ui::ET_TOUCH_RELEASED, event->AsTouchEvent()->type());
+ EXPECT_EQ(ui::EventPointerType::POINTER_TYPE_ERASER,
+ event->AsTouchEvent()->pointer_details().pointer_type);
+}
+
} // namespace views
diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc
index 739e5e548b2..806e809b824 100644
--- a/chromium/ui/views/window/dialog_delegate.cc
+++ b/chromium/ui/views/window/dialog_delegate.cc
@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
#include "ui/gfx/color_palette.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/bubble/bubble_border.h"
@@ -205,12 +206,12 @@ NonClientFrameView* DialogDelegate::CreateNonClientFrameView(Widget* widget) {
// static
NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
+ LayoutProvider* provider = LayoutProvider::Get();
BubbleFrameView* frame = new BubbleFrameView(
- LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_TITLE),
- gfx::Insets());
+ provider->GetInsetsMetric(INSETS_DIALOG_TITLE), gfx::Insets());
const BubbleBorder::Shadow kShadow = BubbleBorder::DIALOG_SHADOW;
- std::unique_ptr<BubbleBorder> border(
- new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor));
+ std::unique_ptr<BubbleBorder> border = std::make_unique<BubbleBorder>(
+ BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor);
border->set_use_theme_background_color(true);
frame->SetBubbleBorder(std::move(border));
DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate();
diff --git a/chromium/ui/views/window/dialog_delegate_unittest.cc b/chromium/ui/views/window/dialog_delegate_unittest.cc
index e3eefefbc1b..bb2b8d54a35 100644
--- a/chromium/ui/views/window/dialog_delegate_unittest.cc
+++ b/chromium/ui/views/window/dialog_delegate_unittest.cc
@@ -40,9 +40,7 @@ class TestDialog : public DialogDelegateView {
}
// WidgetDelegate overrides:
- bool ShouldShowWindowTitle() const override {
- return !title_.empty();
- }
+ bool ShouldShowWindowTitle() const override { return !title_.empty(); }
bool ShouldShowCloseButton() const override { return show_close_button_; }
// DialogDelegateView overrides:
diff --git a/chromium/ui/views/window/frame_background.cc b/chromium/ui/views/window/frame_background.cc
index 7bbbb8db902..a84ce63cc04 100644
--- a/chromium/ui/views/window/frame_background.cc
+++ b/chromium/ui/views/window/frame_background.cc
@@ -4,6 +4,7 @@
#include "ui/views/window/frame_background.h"
+#include "build/build_config.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/theme_provider.h"
@@ -18,6 +19,7 @@ FrameBackground::FrameBackground()
use_custom_frame_(true),
is_active_(true),
incognito_(false),
+ theme_image_y_inset_(0),
top_area_height_(0),
left_edge_(nullptr),
top_edge_(nullptr),
@@ -54,17 +56,12 @@ void FrameBackground::SetCornerImages(const gfx::ImageSkia* top_left,
void FrameBackground::PaintRestored(gfx::Canvas* canvas,
const View* view) const {
- // Fill with the frame color first so we have a constant background for
- // areas not covered by the theme image.
- PaintFrameColor(canvas, view);
+ // Restored window painting is a superset of maximized window painting; let
+ // the maximized code paint the frame color and images.
+ PaintMaximized(canvas, view);
- // Draw the theme frame and overlay, if available.
- if (!theme_image_.isNull()) {
- canvas->TileImageInt(theme_image_, 0, 0, view->width(),
- theme_image_.height());
- }
- if (!theme_overlay_image_.isNull())
- canvas->DrawImageInt(theme_overlay_image_, 0, 0);
+ // Fill the frame borders with the frame color before drawing the edge images.
+ FillFrameBorders(canvas, view);
// Draw the top corners and edge, scaling the corner images down if they
// are too big and relative to the vertical space available.
@@ -123,30 +120,37 @@ void FrameBackground::PaintRestored(gfx::Canvas* canvas,
void FrameBackground::PaintMaximized(gfx::Canvas* canvas,
const View* view) const {
- // We will be painting from -|maximized_top_inset_| to
- // -|maximized_top_inset_| + |theme_image_|.height(). If this is less than
- // |top_area_height_|, we need to paint the frame color to fill in the area
- // beneath the image.
- int theme_frame_bottom = -maximized_top_inset_ +
- (theme_image_.isNull() ? 0 : theme_image_.height());
- if (top_area_height_ > theme_frame_bottom)
- PaintFrameTopArea(canvas, view);
-
- // Draw the theme frame.
+// 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) && !defined(OS_CHROMEOS)
+ auto* native_theme = view->GetNativeTheme();
+ ui::NativeTheme::ExtraParams params;
+ params.frame_top_area.use_custom_frame = use_custom_frame_;
+ params.frame_top_area.is_active = is_active_;
+ params.frame_top_area.incognito = incognito_;
+ params.frame_top_area.default_background_color = frame_color_;
+ native_theme->Paint(canvas->sk_canvas(), ui::NativeTheme::kFrameTopArea,
+ ui::NativeTheme::kNormal,
+ gfx::Rect(0, 0, view->width(), top_area_height_), params);
+#else
+ canvas->FillRect(gfx::Rect(0, 0, view->width(), top_area_height_),
+ frame_color_);
+#endif
+
+ // Draw the theme frame and overlay, if available.
if (!theme_image_.isNull()) {
- canvas->TileImageInt(theme_image_, 0, -maximized_top_inset_, view->width(),
- theme_image_.height());
+ canvas->TileImageInt(theme_image_, 0, theme_image_y_inset_, 0, 0,
+ view->width(), top_area_height_, 1.0f,
+ SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode);
}
- // Draw the theme frame overlay, if available.
if (!theme_overlay_image_.isNull())
canvas->DrawImageInt(theme_overlay_image_, 0, -maximized_top_inset_);
}
-void FrameBackground::PaintFrameColor(gfx::Canvas* canvas,
- const View* view) const {
- PaintFrameTopArea(canvas, view);
-
- // If the window is very short, we're done.
+void FrameBackground::FillFrameBorders(gfx::Canvas* canvas,
+ const View* view) const {
+ // If the window is very short, we don't need to fill any borders.
int remaining_height = view->height() - top_area_height_;
if (remaining_height <= 0)
return;
@@ -171,22 +175,4 @@ void FrameBackground::PaintFrameColor(gfx::Canvas* canvas,
frame_color_);
}
-void FrameBackground::PaintFrameTopArea(gfx::Canvas* canvas,
- const View* view) const {
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
- auto* native_theme = view->GetNativeTheme();
- ui::NativeTheme::ExtraParams params;
- params.frame_top_area.use_custom_frame = use_custom_frame_;
- params.frame_top_area.is_active = is_active_;
- params.frame_top_area.incognito = incognito_;
- params.frame_top_area.default_background_color = frame_color_;
- native_theme->Paint(canvas->sk_canvas(), ui::NativeTheme::kFrameTopArea,
- ui::NativeTheme::kNormal,
- gfx::Rect(0, 0, view->width(), top_area_height_), params);
-#else
- canvas->FillRect(gfx::Rect(0, 0, view->width(), top_area_height_),
- frame_color_);
-#endif
-}
-
} // namespace views
diff --git a/chromium/ui/views/window/frame_background.h b/chromium/ui/views/window/frame_background.h
index 191e5209d94..c8c28703aae 100644
--- a/chromium/ui/views/window/frame_background.h
+++ b/chromium/ui/views/window/frame_background.h
@@ -43,6 +43,9 @@ class VIEWS_EXPORT FrameBackground {
// Memory is owned by the caller.
void set_theme_image(const gfx::ImageSkia& image) { theme_image_ = image; }
+ // Sets an inset into the theme image to begin painting at.
+ void set_theme_image_y_inset(int y_inset) { theme_image_y_inset_ = y_inset; }
+
// Sets an image that overlays the top window image. Usually used to add
// edge highlighting to provide the illusion of depth. May be null (empty).
// Memory is owned by the caller.
@@ -81,17 +84,15 @@ class VIEWS_EXPORT FrameBackground {
void PaintMaximized(gfx::Canvas* canvas, const View* view) const;
private:
- // Fills the frame area with the frame color.
- void PaintFrameColor(gfx::Canvas* canvas, const View* view) const;
-
- // Paints the background of the tab strip.
- void PaintFrameTopArea(gfx::Canvas* canvas, const View* view) const;
+ // Fills the frame side and bottom borders with the frame color.
+ void FillFrameBorders(gfx::Canvas* canvas, const View* view) const;
SkColor frame_color_;
bool use_custom_frame_;
bool is_active_;
bool incognito_;
gfx::ImageSkia theme_image_;
+ int theme_image_y_inset_;
gfx::ImageSkia theme_overlay_image_;
int top_area_height_;
diff --git a/chromium/ui/views/window/non_client_view.cc b/chromium/ui/views/window/non_client_view.cc
index 1dbbeb4f839..cad54b2424a 100644
--- a/chromium/ui/views/window/non_client_view.cc
+++ b/chromium/ui/views/window/non_client_view.cc
@@ -127,16 +127,14 @@ void NonClientView::SizeConstraintsChanged() {
void NonClientView::LayoutFrameView() {
// First layout the NonClientFrameView, which determines the size of the
// ClientView...
- frame_view_->SetBounds(0, 0, width(), height());
-
- // We need to manually call Layout here because layout for the frame view can
- // change independently of the bounds changing - e.g. after the initial
- // display of the window the metrics of the native window controls can change,
- // which does not change the bounds of the window but requires a re-layout to
- // trigger a repaint. We override OnBoundsChanged() for the NonClientFrameView
- // to do nothing so that SetBounds above doesn't cause Layout to be called
- // twice.
- frame_view_->Layout();
+ gfx::Rect new_frame_bounds = GetLocalBounds();
+ if (frame_view_->bounds() == new_frame_bounds) {
+ // SetBoundsRect does a |needs_layout_| check if the bounds aren't actually
+ // changing before triggering a layout. Ensure we do a layout either way.
+ frame_view_->Layout();
+ } else {
+ frame_view_->SetBoundsRect(new_frame_bounds);
+ }
}
void NonClientView::SetAccessibleName(const base::string16& name) {
@@ -173,16 +171,16 @@ void NonClientView::Layout() {
if (base::i18n::IsRTL() && !mirror_client_in_rtl_)
client_bounds.set_x(GetMirroredXForRect(client_bounds));
- client_view_->SetBoundsRect(client_bounds);
+ if (client_bounds != client_view_->bounds()) {
+ client_view_->SetBoundsRect(client_bounds);
+ } else {
+ client_view_->Layout();
+ }
gfx::Path client_clip;
if (frame_view_->GetClientMask(client_view_->size(), &client_clip))
client_view_->set_clip_path(client_clip);
- // We need to manually call Layout on the ClientView as well for the same
- // reason as above.
- client_view_->Layout();
-
if (overlay_view_ && overlay_view_->visible())
overlay_view_->SetBoundsRect(GetLocalBounds());
}
@@ -345,9 +343,4 @@ bool NonClientFrameView::DoesIntersectRect(const View* target,
return !GetWidget()->client_view()->bounds().Intersects(rect);
}
-void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- // Overridden to do nothing. The NonClientView manually calls Layout on the
- // FrameView when it is itself laid out, see comment in NonClientView::Layout.
-}
-
} // namespace views
diff --git a/chromium/ui/views/window/non_client_view.h b/chromium/ui/views/window/non_client_view.h
index e2970c464b0..27cc19d8bad 100644
--- a/chromium/ui/views/window/non_client_view.h
+++ b/chromium/ui/views/window/non_client_view.h
@@ -104,9 +104,6 @@ class VIEWS_EXPORT NonClientFrameView : public View,
bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const override;
- // View:
- void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
-
void set_active_state_override(bool* active_state_override) {
active_state_override_ = active_state_override;
}
diff --git a/chromium/ui/views/window/non_client_view_unittest.cc b/chromium/ui/views/window/non_client_view_unittest.cc
new file mode 100644
index 00000000000..7f2d5c1d25f
--- /dev/null
+++ b/chromium/ui/views/window/non_client_view_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/window/non_client_view.h"
+
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/client_view.h"
+#include "ui/views/window/native_frame_view.h"
+
+namespace views {
+namespace test {
+
+using NonClientViewTest = ViewsTestBase;
+
+namespace {
+
+class NonClientFrameTestView : public NativeFrameView {
+ public:
+ using NativeFrameView::NativeFrameView;
+ int layout_count() const { return layout_count_; }
+
+ // NativeFrameView:
+ void Layout() override {
+ NativeFrameView::Layout();
+ ++layout_count_;
+ }
+
+ private:
+ int layout_count_ = 0;
+};
+
+class ClientTestView : public ClientView {
+ public:
+ using ClientView::ClientView;
+ int layout_count() const { return layout_count_; }
+
+ // ClientView:
+ void Layout() override {
+ ClientView::Layout();
+ ++layout_count_;
+ }
+
+ private:
+ int layout_count_ = 0;
+};
+
+class TestWidgetDelegate : public WidgetDelegateView {
+ public:
+ // WidgetDelegateView:
+ NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
+ return new NonClientFrameTestView(widget);
+ }
+
+ views::ClientView* CreateClientView(Widget* widget) override {
+ return new ClientTestView(widget, this);
+ }
+};
+
+} // namespace
+
+// Ensure Layout() is not called excessively on a ClientView when Widget bounds
+// are changing.
+TEST_F(NonClientViewTest, OnlyLayoutChildViewsOnce) {
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+ params.delegate = new TestWidgetDelegate;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ views::Widget widget;
+ widget.Init(params);
+
+ NonClientView* non_client_view = widget.non_client_view();
+
+ auto* frame_view =
+ static_cast<NonClientFrameTestView*>(non_client_view->frame_view());
+ auto* client_view =
+ static_cast<ClientTestView*>(non_client_view->client_view());
+
+ int initial_frame_view_layouts = frame_view->layout_count();
+ int initial_client_view_layouts = client_view->layout_count();
+
+ non_client_view->Layout();
+ EXPECT_EQ(frame_view->layout_count(), initial_frame_view_layouts + 1);
+ EXPECT_EQ(client_view->layout_count(), initial_client_view_layouts + 1);
+
+ // One more time to make sure it does the layout
+ // even though nothing has changed.
+ non_client_view->Layout();
+ EXPECT_EQ(frame_view->layout_count(), initial_frame_view_layouts + 2);
+ EXPECT_EQ(client_view->layout_count(), initial_client_view_layouts + 2);
+
+ // Ensure changing bounds triggers a (single) layout.
+ widget.SetBounds(gfx::Rect(0, 0, 161, 100));
+ EXPECT_EQ(frame_view->layout_count(), initial_frame_view_layouts + 3);
+ EXPECT_EQ(client_view->layout_count(), initial_client_view_layouts + 3);
+}
+
+} // namespace test
+} // namespace views
diff --git a/chromium/ui/views/window/window_resize_utils.cc b/chromium/ui/views/window/window_resize_utils.cc
new file mode 100644
index 00000000000..61ddee448d7
--- /dev/null
+++ b/chromium/ui/views/window/window_resize_utils.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/views/window/window_resize_utils.h"
+
+#include <algorithm>
+
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace views {
+
+// static
+void WindowResizeUtils::SizeMinMaxToAspectRatio(float aspect_ratio,
+ gfx::Size* min_window_size,
+ gfx::Size* max_window_size) {
+ DCHECK_GT(aspect_ratio, 0.0f);
+
+ // Calculate the height using the min-width and aspect ratio.
+ int min_height = min_window_size->width() / aspect_ratio;
+ if (min_height < min_window_size->height()) {
+ // The supplied width is too small to honor the min size, so use the height
+ // to determine the minimum width.
+ min_window_size->set_width(min_window_size->height() * aspect_ratio);
+ } else {
+ min_window_size->set_height(min_height);
+ }
+
+ // Calculate the height using the max-width and aspect ratio.
+ int max_height = max_window_size->width() / aspect_ratio;
+ if (max_height > max_window_size->height()) {
+ // The supplied width is too large to honor the max size, so use the height
+ // to determine the maximum width.
+ max_window_size->set_width(max_window_size->height() * aspect_ratio);
+ } else {
+ max_window_size->set_height(max_height);
+ }
+
+ DCHECK_GE(max_window_size->width(), min_window_size->width());
+ DCHECK_GE(max_window_size->height(), min_window_size->height());
+}
+
+// static
+void WindowResizeUtils::SizeRectToAspectRatio(HitTest param,
+ float aspect_ratio,
+ const gfx::Size& min_window_size,
+ const gfx::Size& max_window_size,
+ gfx::Rect* rect) {
+ DCHECK_GT(aspect_ratio, 0.0f);
+ DCHECK_GE(max_window_size.width(), min_window_size.width());
+ DCHECK_GE(max_window_size.height(), min_window_size.height());
+
+ float rect_width = 0.0;
+ float rect_height = 0.0;
+ if (param == HitTest::kLeft || param == HitTest::kRight ||
+ param == HitTest::kTopLeft ||
+ param == HitTest::kBottomLeft) { /* horizontal axis to pivot */
+ rect_width = std::min(max_window_size.width(),
+ std::max(rect->width(), min_window_size.width()));
+ rect_height = rect_width / aspect_ratio;
+ } else { /* vertical axis to pivot */
+ rect_height = std::min(max_window_size.height(),
+ std::max(rect->height(), min_window_size.height()));
+ rect_width = rect_height * aspect_ratio;
+ }
+
+ // |rect| bounds before sizing to aspect ratio.
+ int left = rect->x();
+ int top = rect->y();
+ int right = rect->right();
+ int bottom = rect->bottom();
+
+ switch (param) {
+ case HitTest::kRight:
+ case HitTest::kBottom:
+ right = rect_width + left;
+ bottom = top + rect_height;
+ break;
+ case HitTest::kTop:
+ right = rect_width + left;
+ top = bottom - rect_height;
+ break;
+ case HitTest::kLeft:
+ case HitTest::kTopLeft:
+ left = right - rect_width;
+ top = bottom - rect_height;
+ break;
+ case HitTest::kTopRight:
+ right = left + rect_width;
+ top = bottom - rect_height;
+ break;
+ case HitTest::kBottomLeft:
+ left = right - rect_width;
+ bottom = top + rect_height;
+ break;
+ case HitTest::kBottomRight:
+ right = left + rect_width;
+ bottom = top + rect_height;
+ break;
+ }
+
+ rect->SetByBounds(left, top, right, bottom);
+}
+
+} // namespace views \ No newline at end of file
diff --git a/chromium/ui/views/window/window_resize_utils.h b/chromium/ui/views/window/window_resize_utils.h
new file mode 100644
index 00000000000..f833e82967c
--- /dev/null
+++ b/chromium/ui/views/window/window_resize_utils.h
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. 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_WINDOW_WINDOW_RESIZE_UTILS_H_
+#define UI_VIEWS_WINDOW_WINDOW_RESIZE_UTILS_H_
+
+#include "base/macros.h"
+#include "ui/views/views_export.h"
+
+namespace gfx {
+class Size;
+class Rect;
+} // namespace gfx
+
+namespace views {
+
+enum class HitTest {
+ kBottom,
+ kBottomLeft,
+ kBottomRight,
+ kLeft,
+ kRight,
+ kTop,
+ kTopLeft,
+ kTopRight
+};
+
+class VIEWS_EXPORT WindowResizeUtils {
+ public:
+ // Force the min and max window sizes to adhere to the aspect ratio.
+ // |aspect_ratio| must be valid and is found using width / height.
+ static void SizeMinMaxToAspectRatio(float aspect_ratio,
+ gfx::Size* min_window_size,
+ gfx::Size* max_window_size);
+
+ // Updates |rect| to adhere to the |aspect_ratio| of the window, if it has
+ // been set. |param| refers to the edge of the window being sized.
+ // |min_window_size| and |max_window_size| are expected to adhere to the
+ // given aspect ratio.
+ // |aspect_ratio| must be valid and is found using width / height.
+ // TODO(apacible): |max_window_size| is expected to be non-empty. Handle
+ // unconstrained max sizes and sizing when windows are maximized.
+ static void SizeRectToAspectRatio(HitTest param,
+ float aspect_ratio,
+ const gfx::Size& min_window_size,
+ const gfx::Size& max_window_size,
+ gfx::Rect* rect);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WindowResizeUtils);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_WINDOW_WINDOW_RESIZE_UTILS_H_ \ No newline at end of file
diff --git a/chromium/ui/views/window/window_resize_utils_unittest.cc b/chromium/ui/views/window/window_resize_utils_unittest.cc
new file mode 100644
index 00000000000..9510ac8784a
--- /dev/null
+++ b/chromium/ui/views/window/window_resize_utils_unittest.cc
@@ -0,0 +1,161 @@
+// CopykRight 2018 The Chromium Authors. All kRights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/window/window_resize_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace views {
+
+namespace {
+// Aspect ratio is defined by width / height.
+constexpr float kAspectRatioSquare = 1.0f;
+constexpr float kAspectRatioHorizontal = 2.0f;
+constexpr float kAspectRatioVertical = 0.5f;
+
+const gfx::Size kMinSizeSquare = gfx::Size(10, 10);
+const gfx::Size kMaxSizeSquare = gfx::Size(50, 50);
+
+const gfx::Size kMinSizeHorizontal = gfx::Size(20, 10);
+const gfx::Size kMaxSizeHorizontal = gfx::Size(50, 25);
+
+const gfx::Size kMinSizeVertical = gfx::Size(10, 20);
+const gfx::Size kMaxSizeVertical = gfx::Size(25, 50);
+} // namespace
+
+// Tests resizing of window with a 1:1 aspect ratio. This test also tests the
+// 'pivot points' when resizing, i.e. the opposite side or corner of the
+// window.
+TEST(WindowResizeUtilsTest, SizeToSquareAspectRatio) {
+ // Size from the top of the window.
+ // |window_rect| within the bounds of kMinSizeSquare and kMaxSizeSquare.
+ gfx::Rect window_rect(100, 100, 15, 15);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTop, kAspectRatioSquare,
+ kMinSizeSquare, kMaxSizeSquare,
+ &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, 15, 15));
+
+ // Size from the bottom right corner of the window.
+ // |window_rect| smaller than kMinSizeSquare.
+ window_rect.SetRect(100, 100, 5, 5);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottomRight,
+ kAspectRatioSquare, kMinSizeSquare,
+ kMaxSizeSquare, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeSquare.width(),
+ kMinSizeSquare.height()));
+
+ // Size from the top of the window.
+ // |window_rect| larger than kMaxSizeSquare.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTop, kAspectRatioSquare,
+ kMinSizeSquare, kMaxSizeSquare,
+ &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 150, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the bottom of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottom, kAspectRatioSquare,
+ kMinSizeSquare, kMaxSizeSquare,
+ &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the left of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kLeft, kAspectRatioSquare,
+ kMinSizeSquare, kMaxSizeSquare,
+ &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(150, 150, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the right of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kRight, kAspectRatioSquare,
+ kMinSizeSquare, kMaxSizeSquare,
+ &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the top left corner of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTopLeft,
+ kAspectRatioSquare, kMinSizeSquare,
+ kMaxSizeSquare, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(150, 150, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the top right corner of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTopRight,
+ kAspectRatioSquare, kMinSizeSquare,
+ kMaxSizeSquare, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 150, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+
+ // Size from the bottom left corner of the window.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottomLeft,
+ kAspectRatioSquare, kMinSizeSquare,
+ kMaxSizeSquare, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(150, 100, kMaxSizeSquare.width(),
+ kMaxSizeSquare.height()));
+}
+
+// Tests the aspect ratio of the gfx::Rect adheres to the horizontal aspect
+// ratio.
+TEST(WindowResizeUtilsTest, SizeToHorizontalAspectRatio) {
+ // |window_rect| within bounds of kMinSizeHorizontal and kMaxSizeHorizontal.
+ gfx::Rect window_rect(100, 100, 20, 10);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kTop, kAspectRatioHorizontal, kMinSizeHorizontal,
+ kMaxSizeHorizontal, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, 20, 10));
+
+ // |window_rect| smaller than kMinSizeHorizontal.
+ window_rect.SetRect(100, 100, 5, 5);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kBottomRight, kAspectRatioHorizontal, kMinSizeHorizontal,
+ kMaxSizeHorizontal, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeHorizontal.width(),
+ kMinSizeHorizontal.height()));
+
+ // |window_rect| greater than kMaxSizeHorizontal.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kTop, kAspectRatioHorizontal, kMinSizeHorizontal,
+ kMaxSizeHorizontal, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 175, kMaxSizeHorizontal.width(),
+ kMaxSizeHorizontal.height()));
+}
+
+// Tests the aspect ratio of the gfx::Rect adheres to the vertical aspect ratio.
+TEST(WindowResizeUtilsTest, SizeToVerticalAspectRatio) {
+ // |window_rect| within bounds of kMinSizeVertical and kMaxSizeVertical.
+ gfx::Rect window_rect(100, 100, 10, 20);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
+ kMaxSizeVertical, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, 10, 20));
+
+ // |window_rect| smaller than kMinSizeVertical.
+ window_rect.SetRect(100, 100, 5, 5);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
+ kMaxSizeVertical, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeVertical.width(),
+ kMinSizeVertical.height()));
+
+ // |window_rect| greater than kMaxSizeVertical.
+ window_rect.SetRect(100, 100, 100, 100);
+ WindowResizeUtils::SizeRectToAspectRatio(
+ HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
+ kMaxSizeVertical, &window_rect);
+ EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeVertical.width(),
+ kMaxSizeVertical.height()));
+}
+
+} // namespace views \ No newline at end of file